file_entity.module

  1. cis7 sites/all/modules/ulmus/file_entity/file_entity.module
  2. cle7 sites/all/modules/ulmus/file_entity/file_entity.module
  3. ecd7 sites/all/modules/ulmus/file_entity/file_entity.module
  4. elmsmedia7 sites/all/modules/ulmus/file_entity/file_entity.module
  5. harmony7 sites/all/modules/ulmus/file_entity/file_entity.module
  6. icor7 sites/all/modules/ulmus/file_entity/file_entity.module
  7. meedjum_blog7 sites/all/modules/ulmus/file_entity/file_entity.module
  8. mooc7 sites/all/modules/ulmus/file_entity/file_entity.module

Extends Drupal file entities to be fieldable and viewable.

Functions

Namesort descending Description
file_entity_access Determine if a user may perform the given operation on the specified file.
file_entity_action_info_alter Implements hook_action_info_alter().
file_entity_admin_menu_map Implements hook_admin_menu_map().
file_entity_admin_paths Implements hook_admin_paths().
file_entity_cron_queue_info
file_entity_ctools_plugin_api Implements hook_ctools_plugin_api().
file_entity_ctools_plugin_directory
file_entity_download_uri Return an URI for a file download.
file_entity_entity_info_alter Implements hook_entity_info_alter().
file_entity_entity_property_info Implements hook_entity_property_info().
file_entity_field_attach_load Implements hook_field_attach_load().
file_entity_field_display_file_alter Implements hook_field_display_ENTITY_TYPE_alter().
file_entity_field_extra_fields Implements hook_field_extra_fields().
file_entity_file_default_types End of "defgroup file_entity_access".
file_entity_file_download Implements hook_file_download().
file_entity_file_entity_access Implements hook_file_entity_access().
file_entity_file_formatter_file_field_settings Implements hook_file_formatter_FORMATTER_settings().
file_entity_file_formatter_file_field_view Implements hook_file_formatter_FORMATTER_view().
file_entity_file_formatter_file_image_settings Implements hook_file_formatter_FORMATTER_settings().
file_entity_file_formatter_file_image_view Implements hook_file_formatter_FORMATTER_view().
file_entity_file_formatter_info Implements hook_file_formatter_info().
file_entity_file_get_mimetype_type
file_entity_file_is_local Check if a file entity is considered local or not.
file_entity_file_is_readable Check if a file entity is readable or not.
file_entity_file_is_writeable Check if a file entity is considered writeable or not.
file_entity_file_operations Implements hook_file_operations().
file_entity_file_ranking Implements hook_file_ranking().
file_entity_fnmatch A wrapper function for the PHP function fnmatch().
file_entity_form_search_form_alter Implements hook_form_FORM_ID_alter().
file_entity_get_download_token
file_entity_get_hidden_stream_wrappers Helper function to get a list of hidden stream wrappers.
file_entity_get_public_and_private_stream_wrapper_names
file_entity_get_stream_wrapper Return a specific stream wrapper's registry information.
file_entity_help Implements hook_help().
file_entity_hook_info Implements hook_hook_info().
file_entity_hook_info_alter Implements hook_hook_info_alter().
file_entity_invalidate_field_caches Clear the field cache for any entities referencing a specific file.
file_entity_is_page Returns whether the current page is the full page view of the passed-in file.
file_entity_list_permissions Helper function to generate standard file permission list for a given type.
file_entity_match_mimetypes Checks if pattern(s) match mimetype(s).
file_entity_menu Implements hook_menu().
file_entity_menu_local_tasks_alter Implements hook_menu_local_tasks_alter().
file_entity_metadata_form_file Entity API callback to get the form of a file entity.
file_entity_modules_disabled Implements hook_modules_disabled().
file_entity_modules_enabled Implements hook_modules_enabled().
file_entity_permission Implement hook_permission().
file_entity_permissions_get_configured_types Returns an array of file types that should be managed by permissions.
file_entity_query_entity_field_access_alter Implements hook_query_TAG_alter().
file_entity_query_file_access_alter Implements hook_query_TAG_alter().
file_entity_search_access Implements hook_search_access().
file_entity_search_admin Implements hook_search_admin().
file_entity_search_execute Implements hook_search_execute().
file_entity_search_info Implements hook_search_info().
file_entity_search_reset Implements hook_search_reset().
file_entity_search_status Implements hook_search_status().
file_entity_search_validate Form API callback for the search form. Registered in file_entity_form_alter().
file_entity_stream_wrappers_alter Implements hook_stream_wrappers_alter().
file_entity_theme Implements hook_theme().
file_entity_type_determine
file_entity_type_get_name Returns the file type name of the passed file or file type string.
file_entity_type_get_names Returns a list of available file type names.
file_entity_update_index Implements hook_update_index().
file_entity_upload_validators_pre_render Pre-render callback for adding validation descriptions to file upload fields.
file_entity_uri URI callback for file entities.
file_entity_views_api Implements hook_views_api().
file_entity_view_mode_label Return the label for a specific file entity view mode.
file_entity_view_mode_labels Return an array of available view modes for file entities.
image_file_default_displays_alter Implements hook_file_default_displays_alter() on behalf of image.module.
pathauto_file_delete Implements hook_file_delete() on behalf of pathauto.module.
pathauto_file_insert Implements hook_file_insert() on behalf of pathauto.module.
pathauto_file_operations Implements hook_file_operations() on behalf of pathauto.module.
pathauto_file_update Implements hook_file_update() on behalf of pathauto.module.
pathauto_file_update_action Update action wrapper for pathauto_file_update_alias().
pathauto_file_update_alias Update the URL aliases for an individual file.
pathauto_file_update_alias_multiple Update the URL aliases for multiple files.
pathauto_form_file_entity_edit_alter Implements hook_form_FORM_ID_alter() on behalf of pathauto.module.
path_file_delete Implements hook_file_delete() on behalf of path.module.
path_file_insert Implements hook_file_insert() on behalf of path.module.
path_file_update Implements hook_file_update() on behalf of path.module.
path_form_file_entity_edit_alter Implements hook_form_FORM_ID_alter() for file_entity_edit() on behalf of path.module.
template_preprocess_file_entity Process variables for file_entity.tpl.php
theme_file_entity_search_admin Returns HTML for the file ranking part of the search settings admin page.
_file_entity_get_fields_by_type Find all fields that are of a certain field type.
_file_entity_index_file Index a single file.
_file_entity_query_file_entity_access_alter Helper for file entity access functions.
_file_entity_rankings Gather the rankings from the the hook_ranking implementations.
_file_entity_view_mode_menu_access Menu access callback for the 'view mode file display settings' pages.

Constants

Namesort descending Description
FILE_ENTITY_ACCESS_ALLOW Modules should return this value from hook_file_entity_access() to allow access to a file.
FILE_ENTITY_ACCESS_DENY Modules should return this value from hook_file_entity_access() to deny access to a file.
FILE_ENTITY_ACCESS_IGNORE Modules should return this value from hook_file_entity_access() to not affect file access.

File

sites/all/modules/ulmus/file_entity/file_entity.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * Extends Drupal file entities to be fieldable and viewable.
  5. */
  6. /**
  7. * Modules should return this value from hook_file_entity_access() to allow
  8. * access to a file.
  9. */
  10. define('FILE_ENTITY_ACCESS_ALLOW', 'allow');
  11. /**
  12. * Modules should return this value from hook_file_entity_access() to deny
  13. * access to a file.
  14. */
  15. define('FILE_ENTITY_ACCESS_DENY', 'deny');
  16. /**
  17. * Modules should return this value from hook_file_entity_access() to not affect
  18. * file access.
  19. */
  20. define('FILE_ENTITY_ACCESS_IGNORE', NULL);
  21. /**
  22. * As part of extending Drupal core's file entity API, this module adds some
  23. * functions to the 'file' namespace. For organization, those are kept in the
  24. * 'file_entity.file_api.inc' file.
  25. */
  26. require_once dirname(__FILE__) . '/file_entity.file_api.inc';
  27. // @todo Remove when http://drupal.org/node/977052 is fixed.
  28. require_once dirname(__FILE__) . '/file_entity.field.inc';
  29. /**
  30. * Implements hook_hook_info().
  31. */
  32. function file_entity_hook_info() {
  33. $hooks = array(
  34. 'file_operation_info',
  35. 'file_operation_info_alter',
  36. 'file_type_info',
  37. 'file_type_info_alter',
  38. 'file_formatter_info',
  39. 'file_formatter_info_alter',
  40. 'file_view',
  41. 'file_view_alter',
  42. 'file_displays_alter',
  43. 'file_type',
  44. 'file_type_alter',
  45. 'file_download_headers_alter',
  46. 'file_entity_access',
  47. );
  48. return array_fill_keys($hooks, array('group' => 'file'));
  49. }
  50. /**
  51. * Implements hook_hook_info_alter().
  52. *
  53. * Add support for existing core hooks to be located in modulename.file.inc.
  54. */
  55. function file_entity_hook_info_alter(&$info) {
  56. $hooks = array(
  57. // File API hooks
  58. 'file_copy',
  59. 'file_move',
  60. 'file_validate',
  61. // File access
  62. 'file_download',
  63. 'file_download_access',
  64. 'file_download_access_alter',
  65. // File entity hooks
  66. 'file_load',
  67. 'file_presave',
  68. 'file_insert',
  69. 'file_update',
  70. 'file_delete',
  71. // Miscellaneous hooks
  72. 'file_mimetype_mapping_alter',
  73. 'file_url_alter',
  74. );
  75. $info += array_fill_keys($hooks, array('group' => 'file'));
  76. }
  77. /**
  78. * Implements hook_help().
  79. */
  80. function file_entity_help($path, $arg) {
  81. switch ($path) {
  82. case 'admin/structure/file-types':
  83. $output = '<p>' . t('When a file is uploaded to this website, it is assigned one of the following types, based on what kind of file it is.') . '</p>';
  84. return $output;
  85. case 'admin/structure/file-types/manage/%/display/preview':
  86. case 'admin/structure/file-types/manage/%/file-display/preview':
  87. drupal_set_message(t('Some modules rely on the Preview view mode to function correctly. Changing these settings may break parts of your site.'), 'warning');
  88. break;
  89. }
  90. }
  91. /**
  92. * Implements hook_menu().
  93. */
  94. function file_entity_menu() {
  95. // File Configuration
  96. // @todo Move this back to admin/config/media/file-types in Drupal 8 if
  97. // MENU_MAX_DEPTH is increased to a value higher than 9.
  98. $items['admin/structure/file-types'] = array(
  99. 'title' => 'File types',
  100. 'description' => 'Manage settings for the type of files used on your site.',
  101. 'page callback' => 'file_entity_list_types_page',
  102. 'access arguments' => array('administer file types'),
  103. 'file' => 'file_entity.admin.inc',
  104. );
  105. $items['admin/structure/file-types/add'] = array(
  106. 'title' => 'Add file type',
  107. 'page callback' => 'drupal_get_form',
  108. 'page arguments' => array('file_entity_file_type_form'),
  109. 'access arguments' => array('administer file types'),
  110. 'type' => MENU_LOCAL_ACTION,
  111. 'file' => 'file_entity.admin.inc',
  112. );
  113. $items['admin/structure/file-types/manage/%file_type'] = array(
  114. 'title' => 'Manage file types',
  115. 'description' => 'Manage settings for the type of files used on your site.',
  116. );
  117. $items['admin/structure/file-types/manage/%file_type/enable'] = array(
  118. 'title' => 'Enable',
  119. 'page callback' => 'drupal_get_form',
  120. 'page arguments' => array('file_entity_type_enable_confirm', 4),
  121. 'access arguments' => array('administer file types'),
  122. 'file' => 'file_entity.admin.inc',
  123. 'type' => MENU_CALLBACK,
  124. );
  125. $items['admin/structure/file-types/manage/%file_type/disable'] = array(
  126. 'title' => 'Disable',
  127. 'page callback' => 'drupal_get_form',
  128. 'page arguments' => array('file_entity_type_disable_confirm', 4),
  129. 'access arguments' => array('administer file types'),
  130. 'file' => 'file_entity.admin.inc',
  131. 'type' => MENU_CALLBACK,
  132. );
  133. $items['admin/structure/file-types/manage/%file_type/revert'] = array(
  134. 'title' => 'Revert',
  135. 'page callback' => 'drupal_get_form',
  136. 'page arguments' => array('file_entity_type_revert_confirm', 4),
  137. 'access arguments' => array('administer file types'),
  138. 'file' => 'file_entity.admin.inc',
  139. 'type' => MENU_CALLBACK,
  140. );
  141. $items['admin/structure/file-types/manage/%file_type/delete'] = array(
  142. 'title' => 'Delete',
  143. 'page callback' => 'drupal_get_form',
  144. 'page arguments' => array('file_entity_type_delete_confirm', 4),
  145. 'access arguments' => array('administer file types'),
  146. 'file' => 'file_entity.admin.inc',
  147. 'type' => MENU_CALLBACK,
  148. );
  149. $items['admin/content/file'] = array(
  150. 'title' => 'Files',
  151. 'description' => 'Manage files used on your site.',
  152. 'page callback' => 'drupal_get_form',
  153. 'page arguments' => array('file_entity_admin_file'),
  154. 'access arguments' => array('administer files'),
  155. 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM,
  156. 'file' => 'file_entity.admin.inc',
  157. );
  158. $items['admin/content/file/list'] = array(
  159. 'title' => 'List',
  160. 'type' => MENU_DEFAULT_LOCAL_TASK,
  161. );
  162. // general view, edit, delete for files
  163. $items['file/add'] = array(
  164. 'title' => 'Add file',
  165. 'page callback' => 'drupal_get_form',
  166. 'page arguments' => array('file_entity_add_upload', array()),
  167. 'access callback' => 'file_entity_access',
  168. 'access arguments' => array('create'),
  169. 'file' => 'file_entity.pages.inc',
  170. );
  171. if (module_exists('plupload') && module_exists('multiform')) {
  172. $items['file/add']['page arguments'] = array('file_entity_add_upload_multiple');
  173. }
  174. $items['file/add/upload'] = array(
  175. 'title' => 'Upload',
  176. 'type' => MENU_DEFAULT_LOCAL_TASK,
  177. 'weight' => -10,
  178. );
  179. $items['file/add/upload/file'] = array(
  180. 'title' => 'File',
  181. 'type' => MENU_DEFAULT_LOCAL_TASK,
  182. 'weight' => -10,
  183. );
  184. $items['file/add/upload/archive'] = array(
  185. 'title' => 'Archive',
  186. 'page callback' => 'drupal_get_form',
  187. 'page arguments' => array('file_entity_upload_archive_form'),
  188. 'access arguments' => array('administer files'),
  189. 'file' => 'file_entity.pages.inc',
  190. 'type' => MENU_LOCAL_TASK,
  191. 'weight' => -5,
  192. );
  193. $items['file/%file'] = array(
  194. 'title callback' => 'entity_label',
  195. 'title arguments' => array('file', 1),
  196. // The page callback also invokes drupal_set_title() in case
  197. // the menu router's title is overridden by a menu link.
  198. 'page callback' => 'file_entity_view_page',
  199. 'page arguments' => array(1),
  200. 'access callback' => 'file_entity_access',
  201. 'access arguments' => array('view', 1),
  202. 'file' => 'file_entity.pages.inc',
  203. );
  204. $items['file/%file/view'] = array(
  205. 'title' => 'View',
  206. 'type' => MENU_DEFAULT_LOCAL_TASK,
  207. 'weight' => -10,
  208. );
  209. $items['file/%file/usage'] = array(
  210. 'title' => 'Usage',
  211. 'page callback' => 'file_entity_usage_page',
  212. 'page arguments' => array(1),
  213. 'access callback' => 'file_entity_access',
  214. 'access arguments' => array('update', 1),
  215. 'type' => MENU_LOCAL_TASK,
  216. 'context' => MENU_CONTEXT_PAGE,
  217. 'file' => 'file_entity.pages.inc',
  218. );
  219. $items['file/%file/download'] = array(
  220. 'title' => 'Download',
  221. 'page callback' => 'file_entity_download_page',
  222. 'page arguments' => array(1),
  223. 'access callback' => 'file_entity_access',
  224. 'access arguments' => array('download', 1),
  225. 'file' => 'file_entity.pages.inc',
  226. 'type' => MENU_CALLBACK,
  227. );
  228. $items['file/%file/edit'] = array(
  229. 'title' => 'Edit',
  230. 'page callback' => 'drupal_get_form',
  231. 'page arguments' => array('file_entity_edit', 1),
  232. 'access callback' => 'file_entity_access',
  233. 'access arguments' => array('update', 1),
  234. 'weight' => 0,
  235. 'type' => MENU_LOCAL_TASK,
  236. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  237. 'file' => 'file_entity.pages.inc',
  238. );
  239. $items['file/%file/delete'] = array(
  240. 'title' => 'Delete',
  241. 'page callback' => 'drupal_get_form',
  242. 'page arguments' => array('file_entity_delete_form', 1),
  243. 'access callback' => 'file_entity_access',
  244. 'access arguments' => array('delete', 1),
  245. 'weight' => 1,
  246. 'type' => MENU_LOCAL_TASK,
  247. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  248. 'file' => 'file_entity.pages.inc',
  249. );
  250. // Attach a "Manage file display" tab to each file type in the same way that
  251. // Field UI attaches "Manage fields" and "Manage display" tabs. Note that
  252. // Field UI does not have to be enabled; we're just using the same IA pattern
  253. // here for attaching the "Manage file display" page.
  254. $entity_info = entity_get_info('file');
  255. foreach ($entity_info['bundles'] as $file_type => $bundle_info) {
  256. if (isset($bundle_info['admin'])) {
  257. // Get the base path and access.
  258. $path = $bundle_info['admin']['path'];
  259. $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
  260. $access += array(
  261. 'access callback' => 'user_access',
  262. 'access arguments' => array('administer file types'),
  263. );
  264. // The file type must be passed to the page callbacks. It might be
  265. // configured as a wildcard (multiple file types sharing the same menu
  266. // router path).
  267. $file_type_argument = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $file_type;
  268. $items[$path] = array(
  269. 'title' => 'Edit file type',
  270. 'title callback' => 'file_entity_type_get_name',
  271. 'title arguments' => array(4),
  272. 'page callback' => 'drupal_get_form',
  273. 'page arguments' => array('file_entity_file_type_form', $file_type_argument),
  274. 'file' => 'file_entity.admin.inc',
  275. ) + $access;
  276. // Add the 'File type settings' tab.
  277. $items["$path/edit"] = array(
  278. 'title' => 'Edit',
  279. 'type' => MENU_DEFAULT_LOCAL_TASK,
  280. );
  281. // Add the 'Manage file display' tab.
  282. $items["$path/file-display"] = array(
  283. 'title' => 'Manage file display',
  284. 'page callback' => 'drupal_get_form',
  285. 'page arguments' => array('file_entity_file_display_form', $file_type_argument, 'default'),
  286. 'type' => MENU_LOCAL_TASK,
  287. 'weight' => 3,
  288. 'file' => 'file_entity.admin.inc',
  289. ) + $access;
  290. // Add a secondary tab for each view mode.
  291. $weight = 0;
  292. $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view modes'];
  293. foreach ($view_modes as $view_mode => $view_mode_info) {
  294. $items["$path/file-display/$view_mode"] = array(
  295. 'title' => $view_mode_info['label'],
  296. 'page arguments' => array('file_entity_file_display_form', $file_type_argument, $view_mode),
  297. 'type' => ($view_mode == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK),
  298. 'weight' => ($view_mode == 'default' ? -10 : $weight++),
  299. 'file' => 'file_entity.admin.inc',
  300. // View modes for which the 'custom settings' flag isn't TRUE are
  301. // disabled via this access callback. This needs to extend, rather
  302. // than override normal $access rules.
  303. 'access callback' => '_file_entity_view_mode_menu_access',
  304. 'access arguments' => array_merge(array($file_type_argument, $view_mode, $access['access callback']), $access['access arguments']),
  305. );
  306. }
  307. }
  308. }
  309. $items['admin/config/media/file-settings'] = array(
  310. 'title' => 'File settings',
  311. 'description' => 'Configure allowed file extensions, default alt and title sources, and the file upload wizard.',
  312. 'page callback' => 'drupal_get_form',
  313. 'page arguments' => array('file_entity_settings_form'),
  314. 'access arguments' => array('administer site configuration'),
  315. 'file' => 'file_entity.admin.inc',
  316. );
  317. // Optional devel module integration
  318. if (module_exists('devel')) {
  319. $items['file/%file/devel'] = array(
  320. 'title' => 'Devel',
  321. 'page callback' => 'devel_load_object',
  322. 'page arguments' => array('file', 1),
  323. 'access arguments' => array('access devel information'),
  324. 'type' => MENU_LOCAL_TASK,
  325. 'file' => 'devel.pages.inc',
  326. 'file path' => drupal_get_path('module', 'devel'),
  327. 'weight' => 100,
  328. );
  329. $items['file/%file/devel/load'] = array(
  330. 'title' => 'Load',
  331. 'type' => MENU_DEFAULT_LOCAL_TASK,
  332. );
  333. $items['file/%file/devel/render'] = array(
  334. 'title' => 'Render',
  335. 'page callback' => 'devel_render_object',
  336. 'page arguments' => array('file', 1),
  337. 'access arguments' => array('access devel information'),
  338. 'file' => 'devel.pages.inc',
  339. 'file path' => drupal_get_path('module', 'devel'),
  340. 'type' => MENU_LOCAL_TASK,
  341. 'weight' => 100,
  342. );
  343. if (module_exists('token')) {
  344. $items['file/%file/devel/token'] = array(
  345. 'title' => 'Tokens',
  346. 'page callback' => 'token_devel_token_object',
  347. 'page arguments' => array('file', 1),
  348. 'access arguments' => array('access devel information'),
  349. 'type' => MENU_LOCAL_TASK,
  350. 'file' => 'token.pages.inc',
  351. 'file path' => drupal_get_path('module', 'token'),
  352. 'weight' => 5,
  353. );
  354. }
  355. }
  356. return $items;
  357. }
  358. /**
  359. * Implements hook_menu_local_tasks_alter().
  360. */
  361. function file_entity_menu_local_tasks_alter(&$data, $router_item, $root_path) {
  362. // Add action link to 'file/add' on 'admin/content/file' page.
  363. if ($root_path == 'admin/content/file') {
  364. $item = menu_get_item('file/add');
  365. if (!empty($item['access'])) {
  366. $data['actions']['output'][] = array(
  367. '#theme' => 'menu_local_action',
  368. '#link' => $item,
  369. '#weight' => $item['weight'],
  370. );
  371. }
  372. }
  373. }
  374. /**
  375. * Implement hook_permission().
  376. */
  377. function file_entity_permission() {
  378. $permissions = array(
  379. 'bypass file access' => array(
  380. 'title' => t('Bypass file access control'),
  381. 'description' => t('View, edit and delete all files regardless of permission restrictions.'),
  382. 'restrict access' => TRUE,
  383. ),
  384. 'administer file types' => array(
  385. 'title' => t('Administer file types'),
  386. 'restrict access' => TRUE,
  387. ),
  388. 'administer files' => array(
  389. 'title' => t('Administer files'),
  390. 'restrict access' => TRUE,
  391. ),
  392. 'create files' => array(
  393. 'title' => t('Add and upload new files'),
  394. ),
  395. 'view own private files' => array(
  396. 'title' => t('View own private files'),
  397. ),
  398. 'view own files' => array(
  399. 'title' => t('View own files'),
  400. ),
  401. 'view private files' => array(
  402. 'title' => t('View private files'),
  403. 'restrict access' => TRUE,
  404. ),
  405. 'view files' => array(
  406. 'title' => t('View files'),
  407. ),
  408. );
  409. // Add description for the 'View file details' and 'View own private file
  410. // details' permissions to show which stream wrappers they apply to.
  411. $wrappers = file_entity_get_public_and_private_stream_wrapper_names();
  412. $wrappers += array('public' => array(t('None')), 'private' => array(t('None')));
  413. $permissions['view files']['description'] = t('Includes the following stream wrappers: %wrappers.', array('%wrappers' => implode(', ', $wrappers['public'])));
  414. $permissions['view own private files']['description'] = t('Includes the following stream wrappers: %wrappers.', array('%wrappers' => implode(', ', $wrappers['private'])));
  415. // Generate standard file permissions for all applicable file types.
  416. foreach (file_entity_permissions_get_configured_types() as $type) {
  417. $permissions += file_entity_list_permissions($type);
  418. }
  419. return $permissions;
  420. }
  421. /*
  422. * Implements hook_cron_queue_info().
  423. */
  424. function file_entity_cron_queue_info() {
  425. $queues['file_entity_type_determine'] = array(
  426. 'worker callback' => 'file_entity_type_determine',
  427. );
  428. return $queues;
  429. }
  430. /*
  431. * Determines file type for a given file ID and saves the file.
  432. *
  433. * @param $fid
  434. * A file ID.
  435. */
  436. function file_entity_type_determine($fid) {
  437. if ($file = file_load($fid)) {
  438. // The file type will be automatically determined when saving the file.
  439. file_save($file);
  440. }
  441. }
  442. /**
  443. * Gather the rankings from the the hook_ranking implementations.
  444. *
  445. * @param $query
  446. * A query object that has been extended with the Search DB Extender.
  447. */
  448. function _file_entity_rankings(SelectQueryExtender $query) {
  449. if ($ranking = module_invoke_all('file_ranking')) {
  450. $tables = &$query->getTables();
  451. foreach ($ranking as $rank => $values) {
  452. if ($file_rank = variable_get('file_entity_rank_' . $rank, 0)) {
  453. // If the table defined in the ranking isn't already joined, then add it.
  454. if (isset($values['join']) && !isset($tables[$values['join']['alias']])) {
  455. $query->addJoin($values['join']['type'], $values['join']['table'], $values['join']['alias'], $values['join']['on']);
  456. }
  457. $arguments = isset($values['arguments']) ? $values['arguments'] : array();
  458. $query->addScore($values['score'], $arguments, $file_rank);
  459. }
  460. }
  461. }
  462. }
  463. /**
  464. * Implements hook_search_info().
  465. */
  466. function file_entity_search_info() {
  467. return array(
  468. 'title' => 'Files',
  469. 'path' => 'file',
  470. );
  471. }
  472. /**
  473. * Implements hook_search_access().
  474. */
  475. function file_entity_search_access() {
  476. return user_access('view own private files') || user_access('view own files') || user_access('view private files') || user_access('view files');
  477. }
  478. /**
  479. * Implements hook_search_reset().
  480. */
  481. function file_entity_search_reset() {
  482. db_update('search_dataset')
  483. ->fields(array('reindex' => REQUEST_TIME))
  484. ->condition('type', 'file')
  485. ->execute();
  486. }
  487. /**
  488. * Implements hook_search_status().
  489. */
  490. function file_entity_search_status() {
  491. $total = db_query('SELECT COUNT(*) FROM {file_managed}')->fetchField();
  492. $remaining = db_query("SELECT COUNT(*) FROM {file_managed} fm LEFT JOIN {search_dataset} d ON d.type = 'file' AND d.sid = fm.fid WHERE d.sid IS NULL OR d.reindex <> 0")->fetchField();
  493. return array('remaining' => $remaining, 'total' => $total);
  494. }
  495. /**
  496. * Implements hook_search_admin().
  497. */
  498. function file_entity_search_admin() {
  499. // Output form for defining rank factor weights.
  500. $form['file_ranking'] = array(
  501. '#type' => 'fieldset',
  502. '#title' => t('File ranking'),
  503. );
  504. $form['file_ranking']['#theme'] = 'file_entity_search_admin';
  505. $form['file_ranking']['info'] = array(
  506. '#value' => '<em>' . t('The following numbers control which properties the file search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em>'
  507. );
  508. // Note: reversed to reflect that higher number = higher ranking.
  509. $options = drupal_map_assoc(range(0, 10));
  510. foreach (module_invoke_all('file_ranking') as $var => $values) {
  511. $form['file_ranking']['factors']['file_entity_rank_' . $var] = array(
  512. '#title' => $values['title'],
  513. '#type' => 'select',
  514. '#options' => $options,
  515. '#default_value' => variable_get('file_entity_rank_' . $var, 0),
  516. );
  517. }
  518. return $form;
  519. }
  520. /**
  521. * Implements hook_search_execute().
  522. */
  523. function file_entity_search_execute($keys = NULL, $conditions = NULL) {
  524. global $user;
  525. // Build matching conditions
  526. $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault');
  527. $query->join('file_managed', 'fm', 'fm.fid = i.sid');
  528. $query->searchExpression($keys, 'file');
  529. // Insert special keywords.
  530. $query->setOption('type', 'fm.type');
  531. if ($query->setOption('term', 'ti.tid')) {
  532. $query->join('taxonomy_index', 'ti', 'fm.fid = ti.fid');
  533. }
  534. // Only continue if the first pass query matches.
  535. if (!$query->executeFirstPass()) {
  536. return array();
  537. }
  538. // Add the ranking expressions.
  539. _file_entity_rankings($query);
  540. // Load results.
  541. $find = $query
  542. ->limit(10)
  543. ->addTag('file_access')
  544. ->execute();
  545. $results = array();
  546. foreach ($find as $item) {
  547. // Render the file.
  548. $file = file_load($item->sid);
  549. $build = file_view($file, 'search_result');
  550. unset($build['#theme']);
  551. $file->rendered = drupal_render($build);
  552. $extra = module_invoke_all('file_entity_search_result', $file);
  553. $types = file_entity_type_get_names();
  554. $uri = entity_uri('file', $file);
  555. $results[] = array(
  556. 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))),
  557. 'type' => check_plain($types[$file->type]),
  558. 'title' => $file->filename,
  559. 'user' => theme('username', array('account' => user_load($file->uid))),
  560. 'date' => $file->timestamp,
  561. 'file' => $file,
  562. 'extra' => $extra,
  563. 'score' => $item->calculated_score,
  564. 'snippet' => search_excerpt($keys, $file->rendered),
  565. 'language' => function_exists('entity_language') ? entity_language('file', $file) : NULL,
  566. );
  567. }
  568. return $results;
  569. }
  570. /**
  571. * Implements hook_file_ranking().
  572. */
  573. function file_entity_file_ranking() {
  574. // Create the ranking array and add the basic ranking options.
  575. $ranking = array(
  576. 'relevance' => array(
  577. 'title' => t('Keyword relevance'),
  578. // Average relevance values hover around 0.15
  579. 'score' => 'i.relevance',
  580. ),
  581. );
  582. // Add relevance based on creation date.
  583. if ($file_cron_last = variable_get('file_entity_cron_last', 0)) {
  584. $ranking['timestamp'] = array(
  585. 'title' => t('Recently posted'),
  586. // Exponential decay with half-life of 6 months, starting at last indexed file
  587. 'score' => 'POW(2.0, (fm.timestamp - :file_cron_last) * 6.43e-8)',
  588. 'arguments' => array(':file_cron_last' => $file_cron_last),
  589. );
  590. }
  591. return $ranking;
  592. }
  593. /**
  594. * Returns HTML for the file ranking part of the search settings admin page.
  595. *
  596. * @param $variables
  597. * An associative array containing:
  598. * - form: A render element representing the form.
  599. *
  600. * @ingroup themeable
  601. */
  602. function theme_file_entity_search_admin($variables) {
  603. $form = $variables['form'];
  604. $output = drupal_render($form['info']);
  605. $header = array(t('Factor'), t('Weight'));
  606. foreach (element_children($form['factors']) as $key) {
  607. $row = array();
  608. $row[] = $form['factors'][$key]['#title'];
  609. $form['factors'][$key]['#title_display'] = 'invisible';
  610. $row[] = drupal_render($form['factors'][$key]);
  611. $rows[] = $row;
  612. }
  613. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  614. $output .= drupal_render_children($form);
  615. return $output;
  616. }
  617. /**
  618. * Implements hook_update_index().
  619. */
  620. function file_entity_update_index() {
  621. $limit = (int)variable_get('search_cron_limit', 100);
  622. $result = db_query_range("SELECT fm.fid FROM {file_managed} fm LEFT JOIN {search_dataset} d ON d.type = 'file' AND d.sid = fm.fid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, fm.fid ASC", 0, $limit, array(), array('target' => 'slave'));
  623. foreach ($result as $file) {
  624. _file_entity_index_file($file);
  625. }
  626. }
  627. /**
  628. * Index a single file.
  629. *
  630. * @param $file
  631. * The file to index.
  632. */
  633. function _file_entity_index_file($file) {
  634. $file = file_load($file->fid);
  635. // Save the creation time of the most recent indexed file, for the search
  636. // results half-life calculation.
  637. variable_set('file_entity_cron_last', $file->timestamp);
  638. // Render the file.
  639. $build = file_view($file, 'search_index');
  640. unset($build['#theme']);
  641. $file->rendered = drupal_render($build);
  642. $text = '<h1>' . check_plain($file->filename) . '</h1>' . $file->rendered;
  643. // Fetch extra data normally not visible
  644. $extra = module_invoke_all('file_entity_update_index', $file);
  645. foreach ($extra as $t) {
  646. $text .= $t;
  647. }
  648. // Update index
  649. search_index($file->fid, 'file', $text);
  650. }
  651. /**
  652. * Implements hook_form_FORM_ID_alter().
  653. */
  654. function file_entity_form_search_form_alter(&$form, $form_state) {
  655. if (isset($form['module']) && $form['module']['#value'] == 'file_entity' && user_access('use advanced search')) {
  656. // Keyword boxes:
  657. $form['advanced'] = array(
  658. '#type' => 'fieldset',
  659. '#title' => t('Advanced search'),
  660. '#collapsible' => TRUE,
  661. '#collapsed' => TRUE,
  662. '#attributes' => array('class' => array('search-advanced')),
  663. );
  664. $form['advanced']['keywords'] = array(
  665. '#prefix' => '<div class="criterion">',
  666. '#suffix' => '</div>',
  667. );
  668. $form['advanced']['keywords']['or'] = array(
  669. '#type' => 'textfield',
  670. '#title' => t('Containing any of the words'),
  671. '#size' => 30,
  672. '#maxlength' => 255,
  673. );
  674. $form['advanced']['keywords']['phrase'] = array(
  675. '#type' => 'textfield',
  676. '#title' => t('Containing the phrase'),
  677. '#size' => 30,
  678. '#maxlength' => 255,
  679. );
  680. $form['advanced']['keywords']['negative'] = array(
  681. '#type' => 'textfield',
  682. '#title' => t('Containing none of the words'),
  683. '#size' => 30,
  684. '#maxlength' => 255,
  685. );
  686. // File types:
  687. $types = array_map('check_plain', file_entity_type_get_names());
  688. $form['advanced']['type'] = array(
  689. '#type' => 'checkboxes',
  690. '#title' => t('Only of the type(s)'),
  691. '#prefix' => '<div class="criterion">',
  692. '#suffix' => '</div>',
  693. '#options' => $types,
  694. );
  695. $form['advanced']['submit'] = array(
  696. '#type' => 'submit',
  697. '#value' => t('Advanced search'),
  698. '#prefix' => '<div class="action">',
  699. '#suffix' => '</div>',
  700. '#weight' => 100,
  701. );
  702. $form['#validate'][] = 'file_entity_search_validate';
  703. }
  704. }
  705. /**
  706. * Form API callback for the search form. Registered in file_entity_form_alter().
  707. */
  708. function file_entity_search_validate($form, &$form_state) {
  709. // Initialize using any existing basic search keywords.
  710. $keys = $form_state['values']['processed_keys'];
  711. // Insert extra restrictions into the search keywords string.
  712. if (isset($form_state['values']['type']) && is_array($form_state['values']['type'])) {
  713. // Retrieve selected types - Form API sets the value of unselected
  714. // checkboxes to 0.
  715. $form_state['values']['type'] = array_filter($form_state['values']['type']);
  716. if (count($form_state['values']['type'])) {
  717. $keys = search_expression_insert($keys, 'type', implode(',', array_keys($form_state['values']['type'])));
  718. }
  719. }
  720. if (isset($form_state['values']['term']) && is_array($form_state['values']['term']) && count($form_state['values']['term'])) {
  721. $keys = search_expression_insert($keys, 'term', implode(',', $form_state['values']['term']));
  722. }
  723. if ($form_state['values']['or'] != '') {
  724. if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['or'], $matches)) {
  725. $keys .= ' ' . implode(' OR ', $matches[1]);
  726. }
  727. }
  728. if ($form_state['values']['negative'] != '') {
  729. if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state['values']['negative'], $matches)) {
  730. $keys .= ' -' . implode(' -', $matches[1]);
  731. }
  732. }
  733. if ($form_state['values']['phrase'] != '') {
  734. $keys .= ' "' . str_replace('"', ' ', $form_state['values']['phrase']) . '"';
  735. }
  736. if (!empty($keys)) {
  737. form_set_value($form['basic']['processed_keys'], trim($keys), $form_state);
  738. }
  739. }
  740. /**
  741. * Implements hook_admin_paths().
  742. */
  743. function file_entity_admin_paths() {
  744. $paths = array(
  745. 'file/add' => TRUE,
  746. 'file/add/*' => TRUE,
  747. 'file/*/edit' => TRUE,
  748. 'file/*/usage' => TRUE,
  749. 'file/*/delete' => TRUE,
  750. );
  751. return $paths;
  752. }
  753. /**
  754. * Implements hook_action_info_alter().
  755. */
  756. function file_entity_action_info_alter(&$actions) {
  757. if (module_exists('pathauto')) {
  758. $actions['pathauto_file_update_action'] = array(
  759. 'type' => 'file',
  760. 'label' => t('Update file alias'),
  761. 'configurable' => FALSE,
  762. );
  763. }
  764. }
  765. /**
  766. * Implements hook_theme().
  767. */
  768. function file_entity_theme() {
  769. return array(
  770. 'file_entity' => array(
  771. 'render element' => 'elements',
  772. 'template' => 'file_entity',
  773. ),
  774. 'file_entity_search_admin' => array(
  775. 'render element' => 'form',
  776. ),
  777. 'file_entity_file_type_overview' => array(
  778. 'variables' => array('label' => NULL, 'description' => NULL),
  779. 'file' => 'file_entity.admin.inc',
  780. ),
  781. 'file_entity_file_display_order' => array(
  782. 'render element' => 'element',
  783. 'file' => 'file_entity.admin.inc',
  784. ),
  785. 'file_entity_file_link' => array(
  786. 'variables' => array('file' => NULL, 'icon_directory' => NULL),
  787. 'file' => 'file_entity.theme.inc',
  788. ),
  789. 'file_entity_download_link' => array(
  790. 'variables' => array('file' => NULL, 'icon_directory' => NULL, 'text' => NULL),
  791. 'file' => 'file_entity.theme.inc',
  792. ),
  793. 'file_entity_file_audio' => array(
  794. 'variables' => array(
  795. 'files' => array(),
  796. 'controls' => TRUE,
  797. 'autoplay' => FALSE,
  798. 'loop' => FALSE,
  799. ),
  800. 'file' => 'file_entity.theme.inc',
  801. ),
  802. 'file_entity_file_video' => array(
  803. 'variables' => array(
  804. 'files' => array(),
  805. 'controls' => TRUE,
  806. 'autoplay' => FALSE,
  807. 'loop' => FALSE,
  808. 'muted' => FALSE,
  809. 'width' => NULL,
  810. 'height' => NULL,
  811. ),
  812. 'file' => 'file_entity.theme.inc',
  813. ),
  814. );
  815. }
  816. /**
  817. * Implements hook_entity_info_alter().
  818. *
  819. * Extends the core file entity to be fieldable. The file type is used as the
  820. * bundle key. File types are implemented as CTools exportables, so modules can
  821. * define default file types via hook_file_default_types(), and the
  822. * administrator can override the default types or add custom ones via
  823. * admin/structure/file-types.
  824. */
  825. function file_entity_entity_info_alter(&$entity_info) {
  826. $entity_info['file']['fieldable'] = TRUE;
  827. $entity_info['file']['entity keys']['bundle'] = 'type';
  828. $entity_info['file']['bundle keys']['bundle'] = 'type';
  829. $entity_info['file']['bundles'] = array();
  830. $entity_info['file']['uri callback'] = 'file_entity_uri';
  831. $entity_info['file']['view modes']['teaser'] = array(
  832. 'label' => t('Teaser'),
  833. 'custom settings' => TRUE,
  834. );
  835. $entity_info['file']['view modes']['full'] = array(
  836. 'label' => t('Full content'),
  837. 'custom settings' => FALSE,
  838. );
  839. $entity_info['file']['view modes']['preview'] = array(
  840. 'label' => t('Preview'),
  841. 'custom settings' => TRUE,
  842. );
  843. $entity_info['file']['view modes']['rss'] = array(
  844. 'label' => t('RSS'),
  845. 'custom settings' => FALSE,
  846. );
  847. // Search integration is provided by file_entity.module, so search-related
  848. // view modes for files are defined here and not in search.module.
  849. if (module_exists('search')) {
  850. $entity_info['file']['view modes']['search_index'] = array(
  851. 'label' => t('Search index'),
  852. 'custom settings' => FALSE,
  853. );
  854. $entity_info['file']['view modes']['search_result'] = array(
  855. 'label' => t('Search result'),
  856. 'custom settings' => FALSE,
  857. );
  858. }
  859. foreach (file_type_get_enabled_types() as $type) {
  860. $entity_info['file']['bundles'][$type->type] = array(
  861. 'label' => $type->label,
  862. 'admin' => array(
  863. 'path' => 'admin/structure/file-types/manage/%file_type',
  864. 'real path' => 'admin/structure/file-types/manage/' . $type->type,
  865. 'bundle argument' => 4,
  866. ),
  867. );
  868. }
  869. // Enable Metatag support.
  870. $entity_info['file']['metatags'] = TRUE;
  871. // Ensure some of the Entity API callbacks are supported.
  872. $entity_info['file']['creation callback'] = 'entity_metadata_create_object';
  873. $entity_info['file']['view callback'] = 'file_view_multiple';
  874. $entity_info['file']['edit callback'] = 'file_entity_metadata_form_file';
  875. $entity_info['file']['access callback'] = 'file_entity_access';
  876. // Add integration with the Title module for file name replacement support.
  877. $entity_info['file']['field replacement'] = array(
  878. 'filename' => array(
  879. 'field' => array(
  880. 'type' => 'text',
  881. 'cardinality' => 1,
  882. 'translatable' => TRUE,
  883. ),
  884. 'instance' => array(
  885. 'label' => t('File name'),
  886. 'description' => t('A field replacing file name.'),
  887. 'required' => TRUE,
  888. 'settings' => array(
  889. 'text_processing' => 0,
  890. ),
  891. 'widget' => array(
  892. 'weight' => -5,
  893. ),
  894. 'display' => array(
  895. 'default' => array(
  896. 'type' => 'hidden',
  897. ),
  898. ),
  899. ),
  900. 'preprocess_key' => 'filename',
  901. ),
  902. );
  903. }
  904. /**
  905. * Implements hook_entity_property_info().
  906. */
  907. function file_entity_entity_property_info() {
  908. $info['file']['properties']['type'] = array(
  909. 'label' => t('File type'),
  910. 'type' => 'token',
  911. 'description' => t('The type of the file.'),
  912. 'setter callback' => 'entity_property_verbatim_set',
  913. 'setter permission' => 'administer files',
  914. 'options list' => 'file_entity_type_get_names',
  915. 'required' => TRUE,
  916. 'schema field' => 'type',
  917. );
  918. return $info;
  919. }
  920. /**
  921. * Implements hook_field_display_ENTITY_TYPE_alter().
  922. */
  923. function file_entity_field_display_file_alter(&$display, $context) {
  924. // Hide field labels in search index.
  925. if ($context['view_mode'] == 'search_index') {
  926. $display['label'] = 'hidden';
  927. }
  928. }
  929. /**
  930. * URI callback for file entities.
  931. */
  932. function file_entity_uri($file) {
  933. $uri['path'] = 'file/' . $file->fid;
  934. return $uri;
  935. }
  936. /**
  937. * Entity API callback to get the form of a file entity.
  938. */
  939. function file_entity_metadata_form_file($file) {
  940. // Pre-populate the form-state with the right form include.
  941. $form_state['build_info']['args'] = array($file);
  942. form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages');
  943. return drupal_build_form('file_entity_edit', $form_state);
  944. }
  945. /**
  946. * Implements hook_ctools_plugin_directory().
  947. */
  948. function file_entity_ctools_plugin_directory($module, $type) {
  949. if ($module == 'ctools' && $type == 'content_types') {
  950. return 'plugins/' . $type;
  951. }
  952. }
  953. /**
  954. * Implements hook_field_extra_fields().
  955. *
  956. * Adds 'file' as an extra field, so that its display and form component can be
  957. * weighted relative to the fields that are added to file entity bundles.
  958. */
  959. function file_entity_field_extra_fields() {
  960. $info = array();
  961. if ($file_type_names = file_entity_type_get_names()) {
  962. foreach ($file_type_names as $type => $name) {
  963. $info['file'][$type]['form']['filename'] = array(
  964. 'label' => t('File name'),
  965. 'description' => t('File name'),
  966. 'weight' => -10,
  967. );
  968. $info['file'][$type]['form']['preview'] = array(
  969. 'label' => t('File'),
  970. 'description' => t('File preview'),
  971. 'weight' => -5,
  972. );
  973. $info['file'][$type]['display']['file'] = array(
  974. 'label' => t('File'),
  975. 'description' => t('File display'),
  976. 'weight' => 0,
  977. );
  978. }
  979. }
  980. return $info;
  981. }
  982. /**
  983. * Implements hook_file_formatter_info().
  984. */
  985. function file_entity_file_formatter_info() {
  986. $formatters = array();
  987. // Allow file field formatters to be reused for displaying the file entity's
  988. // file pseudo-field.
  989. foreach (field_info_formatter_types() as $key => $formatter) {
  990. if (array_intersect($formatter['field types'], array('file', 'image'))) {
  991. $key = 'file_field_' . $key;
  992. $formatters[$key] = array(
  993. 'label' => $formatter['label'],
  994. 'description' => !empty($formatter['description']) ? $formatter['description'] : '',
  995. 'view callback' => 'file_entity_file_formatter_file_field_view',
  996. );
  997. if (!empty($formatter['settings'])) {
  998. $formatters[$key] += array(
  999. 'default settings' => $formatter['settings'],
  1000. 'settings callback' => 'file_entity_file_formatter_file_field_settings',
  1001. );
  1002. }
  1003. if (!empty($formatter['file formatter'])) {
  1004. $formatters[$key] += $formatter['file formatter'];
  1005. }
  1006. }
  1007. }
  1008. // Add a simple file formatter for displaying an image in a chosen style.
  1009. if (module_exists('image')) {
  1010. $formatters['file_image'] = array(
  1011. 'label' => t('Image'),
  1012. 'default settings' => array(
  1013. 'image_style' => '',
  1014. 'alt' => '[file:field_file_image_alt_text]',
  1015. 'title' => '[file:field_file_image_title_text]'
  1016. ),
  1017. 'view callback' => 'file_entity_file_formatter_file_image_view',
  1018. 'settings callback' => 'file_entity_file_formatter_file_image_settings',
  1019. 'hidden' => TRUE,
  1020. 'mime types' => array('image/*'),
  1021. );
  1022. }
  1023. return $formatters;
  1024. }
  1025. /**
  1026. * Implements hook_file_formatter_FORMATTER_view().
  1027. *
  1028. * This function provides a bridge to the field formatter API, so that file
  1029. * field formatters can be reused for displaying the file entity's file
  1030. * pseudo-field.
  1031. */
  1032. function file_entity_file_formatter_file_field_view($file, $display, $langcode) {
  1033. if (strpos($display['type'], 'file_field_') === 0) {
  1034. $field_formatter_type = substr($display['type'], strlen('file_field_'));
  1035. $field_formatter_info = field_info_formatter_types($field_formatter_type);
  1036. if (isset($field_formatter_info['module'])) {
  1037. // Set $display['type'] to what hook_field_formatter_*() expects.
  1038. $display['type'] = $field_formatter_type;
  1039. // Set $items to what file field formatters expect. See file_field_load(),
  1040. // and note that, here, $file is already a fully loaded entity.
  1041. $items = array((array) $file);
  1042. // Invoke hook_field_formatter_prepare_view() and
  1043. // hook_field_formatter_view(). Note that we are reusing field formatter
  1044. // functions, but we are not displaying a Field API field, so we set
  1045. // $field and $instance accordingly, and do not invoke
  1046. // hook_field_prepare_view(). This assumes that the formatter functions do
  1047. // not rely on $field or $instance. A module that implements formatter
  1048. // functions that rely on $field or $instance (and therefore, can only be
  1049. // used for real fields) can prevent this formatter from being used on the
  1050. // pseudo-field by removing it within hook_file_formatter_info_alter().
  1051. $field = $instance = NULL;
  1052. if (($function = ($field_formatter_info['module'] . '_field_formatter_prepare_view')) && function_exists($function)) {
  1053. $fid = $file->fid;
  1054. // hook_field_formatter_prepare_view() alters $items by reference.
  1055. $grouped_items = array($fid => &$items);
  1056. $function('file', array($fid => $file), $field, array($fid => $instance), $langcode, $grouped_items, array($fid => $display));
  1057. }
  1058. if (($function = ($field_formatter_info['module'] . '_field_formatter_view')) && function_exists($function)) {
  1059. $element = $function('file', $file, $field, $instance, $langcode, $items, $display);
  1060. // We passed the file as $items[0], so return the corresponding element.
  1061. if (isset($element[0])) {
  1062. return $element[0];
  1063. }
  1064. }
  1065. }
  1066. }
  1067. }
  1068. /**
  1069. * Implements hook_file_formatter_FORMATTER_settings().
  1070. *
  1071. * This function provides a bridge to the field formatter API, so that file
  1072. * field formatters can be reused for displaying the file entity's file
  1073. * pseudo-field.
  1074. */
  1075. function file_entity_file_formatter_file_field_settings($form, &$form_state, $settings, $formatter_type, $file_type, $view_mode) {
  1076. if (strpos($formatter_type, 'file_field_') === 0) {
  1077. $field_formatter_type = substr($formatter_type, strlen('file_field_'));
  1078. $field_formatter_info = field_info_formatter_types($field_formatter_type);
  1079. // Invoke hook_field_formatter_settings_form(). We are reusing field
  1080. // formatter functions, but we are not working with a Field API field, so
  1081. // set $field accordingly. Unfortunately, the API is for $settings to be
  1082. // transfered via the $instance parameter, so we must mock it.
  1083. if (isset($field_formatter_info['module']) && ($function = ($field_formatter_info['module'] . '_field_formatter_settings_form')) && function_exists($function)) {
  1084. $field = NULL;
  1085. $mock_instance = array(
  1086. 'display' => array(
  1087. $view_mode => array(
  1088. 'type' => $field_formatter_type,
  1089. 'settings' => $settings,
  1090. ),
  1091. ),
  1092. 'entity_type' => 'file',
  1093. 'bundle' => $file_type,
  1094. );
  1095. return $function($field, $mock_instance, $view_mode, $form, $form_state);
  1096. }
  1097. }
  1098. }
  1099. /**
  1100. * Implements hook_file_formatter_FORMATTER_view().
  1101. *
  1102. * Returns a drupal_render() array to display an image of the chosen style.
  1103. *
  1104. * This formatter is only capable of displaying local images. If the passed in
  1105. * file is either not local or not an image, nothing is returned, so that
  1106. * file_view_file() can try another formatter.
  1107. */
  1108. function file_entity_file_formatter_file_image_view($file, $display, $langcode) {
  1109. // Prevent PHP notices when trying to read empty files.
  1110. // @see http://drupal.org/node/681042
  1111. if (!$file->filesize) {
  1112. return;
  1113. }
  1114. // Do not bother proceeding if this file does not have an image mime type.
  1115. if (file_entity_file_get_mimetype_type($file) != 'image') {
  1116. return;
  1117. }
  1118. if (file_entity_file_is_readable($file)) {
  1119. // We don't sanitize here.
  1120. // @see http://drupal.org/node/1553094#comment-6257382
  1121. // Theme function will take care of escaping.
  1122. if (!isset($file->metadata)) {
  1123. $file->metadata = array();
  1124. }
  1125. $file->metadata += array('width' => NULL, 'height' => NULL);
  1126. $replace_options = array(
  1127. 'clear' => TRUE,
  1128. 'sanitize' => FALSE,
  1129. );
  1130. if (!empty($display['settings']['image_style'])) {
  1131. $element = array(
  1132. '#theme' => 'image_style',
  1133. '#style_name' => $display['settings']['image_style'],
  1134. '#path' => $file->uri,
  1135. '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->metadata['width'],
  1136. '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->metadata['height'],
  1137. '#alt' => token_replace($display['settings']['alt'], array('file' => $file), $replace_options),
  1138. '#title' => token_replace($display['settings']['title'], array('file' => $file), $replace_options),
  1139. );
  1140. }
  1141. else {
  1142. $element = array(
  1143. '#theme' => 'image',
  1144. '#path' => $file->uri,
  1145. '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->metadata['width'],
  1146. '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->metadata['height'],
  1147. '#alt' => token_replace($display['settings']['alt'], array('file' => $file), $replace_options),
  1148. '#title' => token_replace($display['settings']['title'], array('file' => $file), $replace_options),
  1149. );
  1150. }
  1151. return $element;
  1152. }
  1153. }
  1154. /**
  1155. * Check if a file entity is readable or not.
  1156. *
  1157. * @param object $file
  1158. * A file entity object from file_load().
  1159. *
  1160. * @return boolean
  1161. * TRUE if the file is using a readable stream wrapper, or FALSE otherwise.
  1162. */
  1163. function file_entity_file_is_readable($file) {
  1164. $scheme = file_uri_scheme($file->uri);
  1165. $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_READ);
  1166. return !empty($wrappers[$scheme]);
  1167. }
  1168. /**
  1169. * Implements hook_file_formatter_FORMATTER_settings().
  1170. *
  1171. * Returns form elements for configuring the 'file_image' formatter.
  1172. */
  1173. function file_entity_file_formatter_file_image_settings($form, &$form_state, $settings) {
  1174. $element = array();
  1175. $element['image_style'] = array(
  1176. '#title' => t('Image style'),
  1177. '#type' => 'select',
  1178. '#options' => image_style_options(FALSE),
  1179. '#default_value' => $settings['image_style'],
  1180. '#empty_option' => t('None (original image)'),
  1181. );
  1182. // For image files we allow the alt attribute (required in HTML).
  1183. $element['alt'] = array(
  1184. '#title' => t('Alt attribute'),
  1185. '#description' => t('The text to use as value for the <em>img</em> tag <em>alt</em> attribute.'),
  1186. '#type' => 'textfield',
  1187. '#default_value' => $settings['alt'],
  1188. );
  1189. // Allow the setting of the title attribute.
  1190. $element['title'] = array(
  1191. '#title' => t('Title attribute'),
  1192. '#description' => t('The text to use as value for the <em>img</em> tag <em>title</em> attribute.'),
  1193. '#type' => 'textfield',
  1194. '#default_value' => $settings['title'],
  1195. );
  1196. if (module_exists('token')) {
  1197. $element['alt']['#description'] .= t('This field supports tokens.');
  1198. $element['title']['#description'] .= t('This field supports tokens.');
  1199. $element['tokens'] = array(
  1200. '#theme' => 'token_tree',
  1201. '#token_types' => array('file'),
  1202. '#dialog' => TRUE,
  1203. );
  1204. }
  1205. return $element;
  1206. }
  1207. /**
  1208. * Menu access callback for the 'view mode file display settings' pages.
  1209. *
  1210. * Based on _field_ui_view_mode_menu_access(), but the Field UI module might not
  1211. * be enabled.
  1212. */
  1213. function _file_entity_view_mode_menu_access($file_type, $view_mode, $access_callback) {
  1214. // Deny access if the view mode isn't configured to use custom display
  1215. // settings.
  1216. $view_mode_settings = field_view_mode_settings('file', $file_type->type);
  1217. $visibility = ($view_mode == 'default') || !empty($view_mode_settings[$view_mode]['custom_settings']);
  1218. if (!$visibility) {
  1219. return FALSE;
  1220. }
  1221. // Otherwise, continue to an $access_callback check.
  1222. $args = array_slice(func_get_args(), 3);
  1223. $callback = empty($access_callback) ? 0 : trim($access_callback);
  1224. if (is_numeric($callback)) {
  1225. return (bool) $callback;
  1226. }
  1227. elseif (function_exists($access_callback)) {
  1228. return call_user_func_array($access_callback, $args);
  1229. }
  1230. }
  1231. /**
  1232. * Implements hook_modules_enabled().
  1233. */
  1234. function file_entity_modules_enabled($modules) {
  1235. file_info_cache_clear();
  1236. }
  1237. /**
  1238. * Implements hook_modules_disabled().
  1239. */
  1240. function file_entity_modules_disabled($modules) {
  1241. file_info_cache_clear();
  1242. }
  1243. /**
  1244. * Implements hook_views_api().
  1245. */
  1246. function file_entity_views_api() {
  1247. return array(
  1248. 'api' => 3,
  1249. );
  1250. }
  1251. /**
  1252. * Returns whether the current page is the full page view of the passed-in file.
  1253. *
  1254. * @param $file
  1255. * A file object.
  1256. */
  1257. function file_entity_is_page($file) {
  1258. $page_file = menu_get_object('file', 1);
  1259. return (!empty($page_file) ? $page_file->fid == $file->fid : FALSE);
  1260. }
  1261. /**
  1262. * Process variables for file_entity.tpl.php
  1263. *
  1264. * The $variables array contains the following arguments:
  1265. * - $file
  1266. * - $view_mode
  1267. *
  1268. * @see file_entity.tpl.php
  1269. */
  1270. function template_preprocess_file_entity(&$variables) {
  1271. $view_mode = $variables['view_mode'] = $variables['elements']['#view_mode'];
  1272. $variables['file'] = $variables['elements']['#file'];
  1273. $file = $variables['file'];
  1274. $variables['id'] = drupal_html_id('file-'. $file->fid);
  1275. $variables['date'] = format_date($file->timestamp);
  1276. $account = user_load($file->uid);
  1277. $variables['name'] = theme('username', array('account' => $account));
  1278. $uri = entity_uri('file', $file);
  1279. $variables['file_url'] = url($uri['path'], $uri['options']);
  1280. $label = entity_label('file', $file);
  1281. $variables['label'] = check_plain($label);
  1282. $variables['page'] = $view_mode == 'full' && file_entity_is_page($file);
  1283. // Hide the file name from being displayed until we can figure out a better
  1284. // way to control this. We cannot simply not output the title since
  1285. // contextual links require $title_suffix to be output in the template.
  1286. // @see http://drupal.org/node/1245266
  1287. if (!$variables['page']) {
  1288. $variables['title_attributes_array']['class'][] = 'element-invisible';
  1289. }
  1290. // Flatten the file object's member fields.
  1291. $variables = array_merge((array) $file, $variables);
  1292. // Helpful $content variable for templates.
  1293. $variables += array('content' => array());
  1294. foreach (element_children($variables['elements']) as $key) {
  1295. $variables['content'][$key] = $variables['elements'][$key];
  1296. }
  1297. // Make the field variables available with the appropriate language.
  1298. field_attach_preprocess('file', $file, $variables['content'], $variables);
  1299. // Attach the file object to the content element.
  1300. $variables['content']['file']['#file'] = $file;
  1301. // Display post information only on certain file types.
  1302. if (variable_get('file_submitted_' . $file->type, FALSE)) {
  1303. $variables['display_submitted'] = TRUE;
  1304. $variables['submitted'] = t('Uploaded by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
  1305. $variables['user_picture'] = theme_get_setting('toggle_file_user_picture') ? theme('user_picture', array('account' => $account)) : '';
  1306. }
  1307. else {
  1308. $variables['display_submitted'] = FALSE;
  1309. $variables['submitted'] = '';
  1310. $variables['user_picture'] = '';
  1311. }
  1312. // Gather file classes.
  1313. $variables['classes_array'][] = drupal_html_class('file-' . $file->type);
  1314. $variables['classes_array'][] = drupal_html_class('file-' . $file->filemime);
  1315. if ($file->status != FILE_STATUS_PERMANENT) {
  1316. $variables['classes_array'][] = 'file-temporary';
  1317. }
  1318. // Change the 'file-entity' class into 'file'
  1319. if ($variables['classes_array'][0] == 'file-entity') {
  1320. $variables['classes_array'][0] = 'file';
  1321. }
  1322. // Clean up name so there are no underscores.
  1323. $variables['theme_hook_suggestions'][] = 'file__' . $file->type;
  1324. $variables['theme_hook_suggestions'][] = 'file__' . $file->type . '__' . $view_mode;
  1325. $variables['theme_hook_suggestions'][] = 'file__' . str_replace(array('/', '-'), array('__', '_'), $file->filemime);
  1326. $variables['theme_hook_suggestions'][] = 'file__' . str_replace(array('/', '-'), array('__', '_'), $file->filemime) . '__' . $view_mode;
  1327. $variables['theme_hook_suggestions'][] = 'file__' . $file->fid;
  1328. $variables['theme_hook_suggestions'][] = 'file__' . $file->fid . '__' . $view_mode;
  1329. }
  1330. /**
  1331. * Returns the file type name of the passed file or file type string.
  1332. *
  1333. * @param $file
  1334. * A file object or string that indicates the file type to return.
  1335. *
  1336. * @return
  1337. * The file type name or FALSE if the file type is not found.
  1338. */
  1339. function file_entity_type_get_name($file) {
  1340. $type = is_object($file) ? $file->type : $file;
  1341. $info = entity_get_info('file');
  1342. return isset($info['bundles'][$type]['label']) ? $info['bundles'][$type]['label'] : FALSE;
  1343. }
  1344. /**
  1345. * Returns a list of available file type names.
  1346. *
  1347. * @return
  1348. * An array of file type names, keyed by the type.
  1349. */
  1350. function file_entity_type_get_names() {
  1351. $names = &drupal_static(__FUNCTION__);
  1352. if (!isset($names)) {
  1353. $info = entity_get_info('file');
  1354. foreach ($info['bundles'] as $bundle => $bundle_info) {
  1355. $names[$bundle] = $bundle_info['label'];
  1356. }
  1357. }
  1358. return $names;
  1359. }
  1360. /**
  1361. * Return an array of available view modes for file entities.
  1362. */
  1363. function file_entity_view_mode_labels() {
  1364. $labels = &drupal_static(__FUNCTION__);
  1365. if (!isset($options)) {
  1366. $entity_info = entity_get_info('file');
  1367. $labels = array('default' => t('Default'));
  1368. foreach ($entity_info['view modes'] as $machine_name => $mode) {
  1369. $labels[$machine_name] = $mode['label'];
  1370. }
  1371. }
  1372. return $labels;
  1373. }
  1374. /**
  1375. * Return the label for a specific file entity view mode.
  1376. */
  1377. function file_entity_view_mode_label($view_mode, $default = FALSE) {
  1378. $labels = file_entity_view_mode_labels();
  1379. return isset($labels[$view_mode]) ? $labels[$view_mode] : $default;
  1380. }
  1381. /**
  1382. * Helper function to get a list of hidden stream wrappers.
  1383. *
  1384. * This is used in several places to filter queries for media so that files in
  1385. * temporary:// don't show up.
  1386. */
  1387. function file_entity_get_hidden_stream_wrappers() {
  1388. return array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STREAM_WRAPPERS_VISIBLE));
  1389. }
  1390. /**
  1391. * Return a specific stream wrapper's registry information.
  1392. *
  1393. * @param $scheme
  1394. * A URI scheme, a stream is referenced as "scheme://target".
  1395. *
  1396. * @see file_get_stream_wrappers()
  1397. */
  1398. function file_entity_get_stream_wrapper($scheme) {
  1399. $wrappers = file_get_stream_wrappers();
  1400. return isset($wrappers[$scheme]) ? $wrappers[$scheme] : FALSE;
  1401. }
  1402. /**
  1403. * Implements hook_stream_wrappers_alter().
  1404. */
  1405. function file_entity_stream_wrappers_alter(&$wrappers) {
  1406. if (isset($wrappers['private'])) {
  1407. $wrappers['private']['private'] = TRUE;
  1408. }
  1409. if (isset($wrappers['temporary'])) {
  1410. $wrappers['temporary']['private'] = TRUE;
  1411. }
  1412. }
  1413. /**
  1414. * Implements hook_ctools_plugin_api().
  1415. */
  1416. function file_entity_ctools_plugin_api($owner, $api) {
  1417. if ($owner == 'file_entity' && $api == 'file_type') {
  1418. return array('version' => 1);
  1419. }
  1420. if ($owner == 'file_entity' && $api == 'file_default_displays') {
  1421. return array('version' => 1);
  1422. }
  1423. }
  1424. /**
  1425. * @defgroup file_entity_access File access rights
  1426. * @{
  1427. * The file access system determines who can do what to which files.
  1428. *
  1429. * In determining access rights for a file, file_entity_access() first checks
  1430. * whether the user has the "bypass file access" permission. Such users have
  1431. * unrestricted access to all files. user 1 will always pass this check.
  1432. *
  1433. * Next, all implementations of hook_file_entity_access() will be called. Each
  1434. * implementation may explicitly allow, explicitly deny, or ignore the access
  1435. * request. If at least one module says to deny the request, it will be rejected.
  1436. * If no modules deny the request and at least one says to allow it, the request
  1437. * will be permitted.
  1438. *
  1439. * There is no access grant system for files.
  1440. *
  1441. * In file listings, the process above is followed except that
  1442. * hook_file_entity_access() is not called on each file for performance reasons
  1443. * and for proper functioning of the pager system. When adding a filelisting to
  1444. * your module, be sure to use a dynamic query created by db_select()
  1445. * and add a tag of "file_entity_access". This will allow modules dealing
  1446. * with file access to ensure only files to which the user has access
  1447. * are retrieved, through the use of hook_query_TAG_alter().
  1448. *
  1449. * Note: Even a single module returning FILE_ENTITY_ACCESS_DENY from
  1450. * hook_file_entity_access() will block access to the file. Therefore,
  1451. * implementers should take care to not deny access unless they really intend to.
  1452. * Unless a module wishes to actively deny access it should return
  1453. * FILE_ENTITY_ACCESS_IGNORE (or simply return nothing)
  1454. * to allow other modules to control access.
  1455. *
  1456. * Stream wrappers that are considered private should implement a 'private'
  1457. * flag equal to TRUE in hook_stream_wrappers().
  1458. */
  1459. /**
  1460. * Determine if a user may perform the given operation on the specified file.
  1461. *
  1462. * @param $op
  1463. * The operation to be performed on the file. Possible values are:
  1464. * - "view"
  1465. * - "download"
  1466. * - "update"
  1467. * - "delete"
  1468. * - "create"
  1469. * @param $file
  1470. * The file object on which the operation is to be performed, or file type
  1471. * (e.g. 'image') for "create" operation.
  1472. * @param $account
  1473. * Optional, a user object representing the user for whom the operation is to
  1474. * be performed. Determines access for a user other than the current user.
  1475. *
  1476. * @return
  1477. * TRUE if the operation may be performed, FALSE otherwise.
  1478. */
  1479. function file_entity_access($op, $file = NULL, $account = NULL) {
  1480. $rights = &drupal_static(__FUNCTION__, array());
  1481. if (!$file && !in_array($op, array('view', 'download', 'update', 'delete', 'create'), TRUE)) {
  1482. // If there was no file to check against, and the $op was not one of the
  1483. // supported ones, we return access denied.
  1484. return FALSE;
  1485. }
  1486. // If no user object is supplied, the access check is for the current user.
  1487. if (empty($account)) {
  1488. $account = $GLOBALS['user'];
  1489. }
  1490. // $file may be either an object or a file type. Since file types cannot be
  1491. // an integer, use either fid or type as the static cache id.
  1492. $cache_id = is_object($file) ? $file->fid : $file;
  1493. // If we've already checked access for this file, user and op, return from
  1494. // cache.
  1495. if (isset($rights[$account->uid][$cache_id][$op])) {
  1496. return $rights[$account->uid][$cache_id][$op];
  1497. }
  1498. if (user_access('bypass file access', $account)) {
  1499. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1500. }
  1501. // We grant access to the file if both of the following conditions are met:
  1502. // - No modules say to deny access.
  1503. // - At least one module says to grant access.
  1504. $access = module_invoke_all('file_entity_access', $op, $file, $account);
  1505. if (in_array(FILE_ENTITY_ACCESS_DENY, $access, TRUE)) {
  1506. return $rights[$account->uid][$cache_id][$op] = FALSE;
  1507. }
  1508. elseif (in_array(FILE_ENTITY_ACCESS_ALLOW, $access, TRUE)) {
  1509. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1510. }
  1511. // Fall back to default behaviors on view.
  1512. if ($op == 'view' && is_object($file)) {
  1513. $scheme = file_uri_scheme($file->uri);
  1514. $wrapper = file_entity_get_stream_wrapper($scheme);
  1515. if (!empty($wrapper['private'])) {
  1516. // For private files, users can view private files if the
  1517. // user has the 'view private files' permission.
  1518. if (user_access('view private files', $account)) {
  1519. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1520. }
  1521. // For private files, users can view their own private files if the
  1522. // user is not anonymous, and has the 'view own private files' permission.
  1523. if (!empty($account->uid) && $file->uid == $account->uid && user_access('view own private files', $account)) {
  1524. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1525. }
  1526. }
  1527. elseif ($file->status == FILE_STATUS_PERMANENT && $file->uid == $account->uid && user_access('view own files', $account)) {
  1528. // For non-private files, allow to see if user owns the file.
  1529. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1530. }
  1531. elseif ($file->status == FILE_STATUS_PERMANENT && user_access('view files', $account)) {
  1532. // For non-private files, users can view if they have the 'view files'
  1533. // permission.
  1534. return $rights[$account->uid][$cache_id][$op] = TRUE;
  1535. }
  1536. }
  1537. return FALSE;
  1538. }
  1539. /**
  1540. * Implements hook_file_entity_access().
  1541. */
  1542. function file_entity_file_entity_access($op, $file, $account) {
  1543. // If the file URI is invalid, deny access.
  1544. if (is_object($file) && !file_valid_uri($file->uri)) {
  1545. return FILE_ENTITY_ACCESS_DENY;
  1546. }
  1547. if ($op == 'create') {
  1548. if (user_access('create files')) {
  1549. return FILE_ENTITY_ACCESS_ALLOW;
  1550. }
  1551. }
  1552. if (!empty($file)) {
  1553. $type = is_string($file) ? $file : $file->type;
  1554. if (in_array($type, file_entity_permissions_get_configured_types())) {
  1555. if ($op == 'download') {
  1556. if (user_access('download any ' . $type . ' files', $account) || is_object($file) && user_access('download own ' . $type . ' files', $account) && ($account->uid == $file->uid)) {
  1557. return FILE_ENTITY_ACCESS_ALLOW;
  1558. }
  1559. }
  1560. if ($op == 'update') {
  1561. if (user_access('edit any ' . $type . ' files', $account) || (is_object($file) && user_access('edit own ' . $type . ' files', $account) && ($account->uid == $file->uid))) {
  1562. return FILE_ENTITY_ACCESS_ALLOW;
  1563. }
  1564. }
  1565. if ($op == 'delete') {
  1566. if (user_access('delete any ' . $type . ' files', $account) || (is_object($file) && user_access('delete own ' . $type . ' files', $account) && ($account->uid == $file->uid))) {
  1567. return FILE_ENTITY_ACCESS_ALLOW;
  1568. }
  1569. }
  1570. }
  1571. }
  1572. return FILE_ENTITY_ACCESS_IGNORE;
  1573. }
  1574. /**
  1575. * Implements hook_query_TAG_alter().
  1576. *
  1577. * This is the hook_query_alter() for queries tagged with 'file_access'. It adds
  1578. * file access checks for the user account given by the 'account' meta-data (or
  1579. * global $user if not provided).
  1580. */
  1581. function file_entity_query_file_access_alter(QueryAlterableInterface $query) {
  1582. _file_entity_query_file_entity_access_alter($query, 'file');
  1583. }
  1584. /**
  1585. * Implements hook_query_TAG_alter().
  1586. *
  1587. * This function implements the same functionality as
  1588. * file_entity_query_file_access_alter() for the SQL field storage engine. File
  1589. * access conditions are added for field values belonging to files only.
  1590. */
  1591. function file_entity_query_entity_field_access_alter(QueryAlterableInterface $query) {
  1592. //_file_entity_query_file_entity_access_alter($query, 'entity');
  1593. }
  1594. /**
  1595. * Helper for file entity access functions.
  1596. *
  1597. * @param $query
  1598. * The query to add conditions to.
  1599. * @param $type
  1600. * Either 'file' or 'entity' depending on what sort of query it is. See
  1601. * file_entity_query_file_entity_access_alter() and
  1602. * file_entity_query_entity_field_access_alter() for more.
  1603. */
  1604. function _file_entity_query_file_entity_access_alter($query, $type) {
  1605. global $user;
  1606. // Read meta-data from query, if provided.
  1607. if (!$account = $query->getMetaData('account')) {
  1608. $account = $user;
  1609. }
  1610. // If $account can bypass file access, we don't need to alter the query.
  1611. if (user_access('bypass file access', $account)) {
  1612. return;
  1613. }
  1614. $tables = $query->getTables();
  1615. $base_table = $query->getMetaData('base_table');
  1616. // If no base table is specified explicitly, search for one.
  1617. if (!$base_table) {
  1618. $fallback = '';
  1619. foreach ($tables as $alias => $table_info) {
  1620. if (!($table_info instanceof SelectQueryInterface)) {
  1621. $table = $table_info['table'];
  1622. // If the file_managed table is in the query, it wins immediately.
  1623. if ($table == 'file_managed') {
  1624. $base_table = $table;
  1625. break;
  1626. }
  1627. // Check whether the table has a foreign key to file_managed.fid. If it
  1628. // does, do not run this check again as we found a base table and only
  1629. // file_managed can triumph that.
  1630. if (!$base_table) {
  1631. // The schema is cached.
  1632. $schema = drupal_get_schema($table);
  1633. if (isset($schema['fields']['fid'])) {
  1634. if (isset($schema['foreign keys'])) {
  1635. foreach ($schema['foreign keys'] as $relation) {
  1636. if ($relation['table'] === 'file_managed' && $relation['columns'] === array('fid' => 'fid')) {
  1637. $base_table = $table;
  1638. }
  1639. }
  1640. }
  1641. else {
  1642. // At least it's a fid. A table with a field called fid is very
  1643. // very likely to be a file_managed.fid in a file access query.
  1644. $fallback = $table;
  1645. }
  1646. }
  1647. }
  1648. }
  1649. }
  1650. // If there is nothing else, use the fallback.
  1651. if (!$base_table) {
  1652. if ($fallback) {
  1653. watchdog('security', 'Your file listing query is using @fallback as a base table in a query tagged for file access. This might not be secure and might not even work. Specify foreign keys in your schema to file_managed.fid ', array('@fallback' => $fallback), WATCHDOG_WARNING);
  1654. $base_table = $fallback;
  1655. }
  1656. else {
  1657. throw new Exception(t('Query tagged for file access but there is no fid. Add foreign keys to file_managed.fid in schema to fix.'));
  1658. }
  1659. }
  1660. }
  1661. if ($type == 'entity') {
  1662. // The original query looked something like:
  1663. // @code
  1664. // SELECT fid FROM sometable s
  1665. // WHERE ($file_access_conditions)
  1666. // @endcode
  1667. //
  1668. // Our query will look like:
  1669. // @code
  1670. // SELECT entity_type, entity_id
  1671. // FROM field_data_something s
  1672. // WHERE (entity_type = 'file' AND $file_access_conditions) OR (entity_type <> 'file')
  1673. // @endcode
  1674. //
  1675. // So instead of directly adding to the query object, we need to collect
  1676. // all of the file access conditions in a separate db_and() object and
  1677. // then add it to the query at the end.
  1678. $file_conditions = db_and();
  1679. }
  1680. foreach ($tables as $falias => $tableinfo) {
  1681. $table = $tableinfo['table'];
  1682. if (!($table instanceof SelectQueryInterface) && $table == $base_table) {
  1683. $subquery = db_select('file_managed', 'fm_access')->fields('fm_access', array('fid'));
  1684. $subquery_conditions = db_or();
  1685. $wrappers = file_entity_get_public_and_private_stream_wrapper_names();
  1686. if (!empty($wrappers['public'])) {
  1687. if (user_access('view files', $account)) {
  1688. foreach (array_keys($wrappers['public']) as $wrapper) {
  1689. $subquery_conditions->condition('fm_access.uri', $wrapper . '%', 'LIKE');
  1690. }
  1691. }
  1692. elseif (user_access('view own files', $account)) {
  1693. foreach (array_keys($wrappers['public']) as $wrapper) {
  1694. $subquery_conditions->condition(db_and()
  1695. ->condition('fm_access.uri', $wrapper . '%', 'LIKE')
  1696. ->condition('fm_access.uid', $account->uid)
  1697. );
  1698. }
  1699. }
  1700. }
  1701. if (!empty($wrappers['private'])) {
  1702. if (user_access('view private files', $account)) {
  1703. foreach (array_keys($wrappers['private']) as $wrapper) {
  1704. $subquery_conditions->condition('fm_access.uri', $wrapper . '%', 'LIKE');
  1705. }
  1706. }
  1707. elseif (user_access('view own private files', $account)) {
  1708. foreach (array_keys($wrappers['private']) as $wrapper) {
  1709. $subquery_conditions->condition(db_and()
  1710. ->condition('fm_access.uri', $wrapper . '%', 'LIKE')
  1711. ->condition('fm_access.uid', $account->uid)
  1712. );
  1713. }
  1714. }
  1715. }
  1716. if ($subquery_conditions->count()) {
  1717. $subquery->condition($subquery_conditions);
  1718. $field = 'fid';
  1719. // Now handle entities.
  1720. if ($type == 'entity') {
  1721. // Set a common alias for entities.
  1722. $base_alias = $falias;
  1723. $field = 'entity_id';
  1724. }
  1725. $subquery->where("$falias.$field = fm_access.fid");
  1726. // For an entity query, attach the subquery to entity conditions.
  1727. if ($type == 'entity') {
  1728. $file_conditions->exists($subquery);
  1729. }
  1730. // Otherwise attach it to the node query itself.
  1731. elseif ($table == 'file_managed') {
  1732. // Fix for https://drupal.org/node/2073085
  1733. $db_or = db_or();
  1734. $db_or->exists($subquery);
  1735. $db_or->isNull($falias . '.' . $field);
  1736. $query->condition($db_or);
  1737. }
  1738. else {
  1739. $query->exists($subquery);
  1740. }
  1741. }
  1742. }
  1743. }
  1744. if ($type == 'entity' && $file_conditions->count()) {
  1745. // All the file access conditions are only for field values belonging to
  1746. // files.
  1747. $file_conditions->condition("$base_alias.entity_type", 'file');
  1748. $or = db_or();
  1749. $or->condition($file_conditions);
  1750. // If the field value belongs to a non-file entity type then this function
  1751. // does not do anything with it.
  1752. $or->condition("$base_alias.entity_type", 'file', '<>');
  1753. // Add the compiled set of rules to the query.
  1754. $query->condition($or);
  1755. }
  1756. }
  1757. /**
  1758. * Implements hook_file_download().
  1759. */
  1760. function file_entity_file_download($uri) {
  1761. // Load the file from the URI.
  1762. $file = file_uri_to_object($uri);
  1763. // An existing file wasn't found, so we don't control access.
  1764. // E.g. image derivatives will fall here.
  1765. if (empty($file->fid)) {
  1766. return NULL;
  1767. }
  1768. // Allow the user to download the file if they have appropriate permissions.
  1769. if (file_entity_access('view', $file)) {
  1770. return file_get_content_headers($file);
  1771. }
  1772. return NULL;
  1773. }
  1774. /**
  1775. * Helper function to generate standard file permission list for a given type.
  1776. *
  1777. * @param $type
  1778. * The machine-readable name of the file type.
  1779. * @return array
  1780. * An array of permission names and descriptions.
  1781. */
  1782. function file_entity_list_permissions($type) {
  1783. $info = file_type_load($type);
  1784. // Build standard list of file permissions for this type.
  1785. $permissions = array(
  1786. "edit own $type files" => array(
  1787. 'title' => t('%type_name: Edit own files', array('%type_name' => $info->label)),
  1788. ),
  1789. "edit any $type files" => array(
  1790. 'title' => t('%type_name: Edit any files', array('%type_name' => $info->label)),
  1791. ),
  1792. "delete own $type files" => array(
  1793. 'title' => t('%type_name: Delete own files', array('%type_name' => $info->label)),
  1794. ),
  1795. "delete any $type files" => array(
  1796. 'title' => t('%type_name: Delete any files', array('%type_name' => $info->label)),
  1797. ),
  1798. "download own $type files" => array(
  1799. 'title' => t('%type_name: Download own files', array('%type_name' => $info->label)),
  1800. ),
  1801. "download any $type files" => array(
  1802. 'title' => t('%type_name: Download any files', array('%type_name' => $info->label)),
  1803. ),
  1804. );
  1805. return $permissions;
  1806. }
  1807. /**
  1808. * Returns an array of file types that should be managed by permissions.
  1809. *
  1810. * By default, this will include all file types in the system. To exclude a
  1811. * specific file from getting permissions defined for it, set the
  1812. * file_entity_permissions_$type variable to 0. File entity does not provide an
  1813. * interface for doing so, however, contrib modules may exclude their own files
  1814. * in hook_install(). Alternatively, contrib modules may configure all file
  1815. * types at once, or decide to apply some other hook_file_entity_access()
  1816. * implementation to some or all file types.
  1817. *
  1818. * @return
  1819. * An array of file types managed by this module.
  1820. */
  1821. function file_entity_permissions_get_configured_types() {
  1822. $configured_types = array();
  1823. foreach (file_type_get_enabled_types() as $type => $info) {
  1824. if (variable_get('file_entity_permissions_' . $type, 1)) {
  1825. $configured_types[] = $type;
  1826. }
  1827. }
  1828. return $configured_types;
  1829. }
  1830. /**
  1831. * @} End of "defgroup file_entity_access".
  1832. *
  1833. * Implements hook_file_default_types().
  1834. */
  1835. function file_entity_file_default_types() {
  1836. $types = array();
  1837. // Image.
  1838. $types['image'] = (object) array(
  1839. 'api_version' => 1,
  1840. 'type' => 'image',
  1841. 'label' => t('Image'),
  1842. 'description' => t('An <em>Image</em> file is a still visual.'),
  1843. 'mimetypes' => array(
  1844. 'image/*',
  1845. ),
  1846. );
  1847. // Video.
  1848. $types['video'] = (object) array(
  1849. 'api_version' => 1,
  1850. 'type' => 'video',
  1851. 'label' => t('Video'),
  1852. 'description' => t('A <em>Video</em> file is a moving visual recording.'),
  1853. 'mimetypes' => array(
  1854. 'video/*',
  1855. ),
  1856. );
  1857. // Audio.
  1858. $types['audio'] = (object) array(
  1859. 'api_version' => 1,
  1860. 'type' => 'audio',
  1861. 'label' => t('Audio'),
  1862. 'description' => t('An <em>Audio</em> file is a sound recording.'),
  1863. 'mimetypes' => array(
  1864. 'audio/*',
  1865. ),
  1866. );
  1867. // Document.
  1868. $types['document'] = (object) array(
  1869. 'api_version' => 1,
  1870. 'type' => 'document',
  1871. 'label' => t('Document'),
  1872. 'description' => t('A <em>Document</em> file is written information.'),
  1873. 'mimetypes' => array(
  1874. 'text/plain',
  1875. 'application/msword',
  1876. 'application/vnd.ms-excel',
  1877. 'application/pdf',
  1878. 'application/vnd.ms-powerpoint',
  1879. 'application/vnd.oasis.opendocument.text',
  1880. 'application/vnd.oasis.opendocument.spreadsheet',
  1881. 'application/vnd.oasis.opendocument.presentation',
  1882. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  1883. 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  1884. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  1885. ),
  1886. );
  1887. return $types;
  1888. }
  1889. /**
  1890. * Implements hook_file_operations().
  1891. */
  1892. function file_entity_file_operations() {
  1893. $operations = array(
  1894. 'permanent' => array(
  1895. 'label' => t('Indicate that the selected files are permanent and should not be deleted'),
  1896. 'callback' => 'file_entity_mass_update',
  1897. 'callback arguments' => array('updates' => array('status' => FILE_STATUS_PERMANENT)),
  1898. ),
  1899. 'temporary' => array(
  1900. 'label' => t('Indicate that the selected files are temporary and should be removed during cron runs'),
  1901. 'callback' => 'file_entity_mass_update',
  1902. 'callback arguments' => array('updates' => array('status' => 0)),
  1903. ),
  1904. 'delete' => array(
  1905. 'label' => t('Delete selected files'),
  1906. 'callback' => NULL,
  1907. ),
  1908. );
  1909. return $operations;
  1910. }
  1911. /**
  1912. * Clear the field cache for any entities referencing a specific file.
  1913. *
  1914. * @param object $file
  1915. * A file object.
  1916. */
  1917. function file_entity_invalidate_field_caches($file) {
  1918. $entity_types = &drupal_static(__FUNCTION__);
  1919. // Gather the list of entity types which support field caching.
  1920. if (!isset($entity_types)) {
  1921. $entity_types = array();
  1922. foreach (entity_get_info() as $entity_type => $entity_info) {
  1923. if (!empty($entity_info['fieldable']) && !empty($entity_info['field cache'])) {
  1924. $entity_types[] = $entity_type;
  1925. }
  1926. }
  1927. }
  1928. // If no entity types support field caching, then there is no work to be done.
  1929. if (empty($entity_types)) {
  1930. return;
  1931. }
  1932. $records = db_query("SELECT DISTINCT type, id FROM {file_usage} WHERE fid = :fid AND type IN (:types) AND id > 0", array(':fid' => $file->fid, ':types' => $entity_types))->fetchAll();
  1933. if (!empty($records)) {
  1934. $cids = array();
  1935. foreach ($records as $record) {
  1936. $cids[] = 'field:' . $record->type . ':' . $record->id;
  1937. }
  1938. cache_clear_all($cids, 'cache_field');
  1939. }
  1940. }
  1941. /**
  1942. * Check if a file entity is considered local or not.
  1943. *
  1944. * @param object $file
  1945. * A file entity object from file_load().
  1946. *
  1947. * @return
  1948. * TRUE if the file is using a local stream wrapper, or FALSE otherwise.
  1949. */
  1950. function file_entity_file_is_local($file) {
  1951. $scheme = file_uri_scheme($file->uri);
  1952. $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
  1953. return !empty($wrappers[$scheme]) && empty($wrappers[$scheme]['remote']);
  1954. }
  1955. /**
  1956. * Check if a file entity is considered writeable or not.
  1957. *
  1958. * @param object $file
  1959. * A file entity object from file_load().
  1960. *
  1961. * @return
  1962. * TRUE if the file is using a visible, readable and writeable stream wrapper,
  1963. * or FALSE otherwise.
  1964. */
  1965. function file_entity_file_is_writeable($file) {
  1966. $scheme = file_uri_scheme($file->uri);
  1967. $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
  1968. return !empty($wrappers[$scheme]);
  1969. }
  1970. /**
  1971. * Pre-render callback for adding validation descriptions to file upload fields.
  1972. */
  1973. function file_entity_upload_validators_pre_render($element) {
  1974. if (!empty($element['#upload_validators'])) {
  1975. if (!isset($element['#description'])) {
  1976. $element['#description'] = '';
  1977. }
  1978. if ($element['#description'] !== FALSE) {
  1979. $element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators']));
  1980. }
  1981. }
  1982. return $element;
  1983. }
  1984. /**
  1985. * Implements hook_file_default_displays_alter() on behalf of image.module.
  1986. */
  1987. function image_file_default_displays_alter(&$file_displays) {
  1988. // Images should be displayed as unstyled images by default.
  1989. if (isset($file_displays['image__default__file_field_file_default'])) {
  1990. $file_displays['image__default__file_field_file_default']->status = FALSE;
  1991. }
  1992. $file_display = new stdClass();
  1993. $file_display->api_version = 1;
  1994. $file_display->name = 'image__default__file_field_image';
  1995. $file_display->weight = 50;
  1996. $file_display->status = TRUE;
  1997. $file_display->settings = array(
  1998. 'image_style' => '',
  1999. 'image_link' => '',
  2000. );
  2001. $file_displays['image__default__file_field_image'] = $file_display;
  2002. // Image previews should be displayed as image thumbnails by default.
  2003. if (isset($file_displays['image__preview__file_field_file_default'])) {
  2004. $file_displays['image__preview__file_field_file_default']->status = FALSE;
  2005. }
  2006. $file_display = new stdClass();
  2007. $file_display->api_version = 1;
  2008. $file_display->name = 'image__preview__file_field_image';
  2009. $file_display->weight = 50;
  2010. $file_display->status = TRUE;
  2011. $file_display->settings = array(
  2012. 'image_style' => 'thumbnail',
  2013. 'image_link' => '',
  2014. );
  2015. $file_displays['image__preview__file_field_image'] = $file_display;
  2016. // Image teasers should be displayed as medium images by default.
  2017. if (isset($file_displays['image__teaser__file_field_file_default'])) {
  2018. $file_displays['image__teaser__file_field_file_default']->status = FALSE;
  2019. }
  2020. $file_display = new stdClass();
  2021. $file_display->api_version = 1;
  2022. $file_display->name = 'image__teaser__file_field_image';
  2023. $file_display->weight = 50;
  2024. $file_display->status = TRUE;
  2025. $file_display->settings = array(
  2026. 'image_style' => 'medium',
  2027. 'image_link' => 'content',
  2028. );
  2029. $file_displays['image__teaser__file_field_image'] = $file_display;
  2030. }
  2031. /**
  2032. * @name pathauto_file Pathauto integration for the core file module.
  2033. * @{
  2034. */
  2035. /**
  2036. * Implements hook_file_insert() on behalf of pathauto.module.
  2037. */
  2038. function pathauto_file_insert($file) {
  2039. pathauto_file_update_alias($file, 'insert');
  2040. }
  2041. /**
  2042. * Implements hook_file_update() on behalf of pathauto.module.
  2043. */
  2044. function pathauto_file_update($file) {
  2045. pathauto_file_update_alias($file, 'update');
  2046. }
  2047. /**
  2048. * Implements hook_file_delete() on behalf of pathauto.module.
  2049. */
  2050. function pathauto_file_delete($file) {
  2051. pathauto_entity_path_delete_all('file', $file, "file/{$file->fid}");
  2052. }
  2053. /**
  2054. * Implements hook_form_FORM_ID_alter() on behalf of pathauto.module.
  2055. *
  2056. * Add the Pathauto settings to the file form.
  2057. */
  2058. function pathauto_form_file_entity_edit_alter(&$form, &$form_state, $form_id) {
  2059. $file = $form_state['file'];
  2060. $langcode = pathauto_entity_language('file', $file);
  2061. pathauto_field_attach_form('file', $file, $form, $form_state, $langcode);
  2062. }
  2063. /**
  2064. * Implements hook_file_operations() on behalf of pathauto.module.
  2065. */
  2066. function pathauto_file_operations() {
  2067. $operations['pathauto_update_alias'] = array(
  2068. 'label' => t('Update URL alias'),
  2069. 'callback' => 'pathauto_file_update_alias_multiple',
  2070. 'callback arguments' => array('bulkupdate', array('message' => TRUE)),
  2071. );
  2072. return $operations;
  2073. }
  2074. /**
  2075. * Update the URL aliases for an individual file.
  2076. *
  2077. * @param $file
  2078. * A file object.
  2079. * @param $op
  2080. * Operation being performed on the file ('insert', 'update' or 'bulkupdate').
  2081. * @param $options
  2082. * An optional array of additional options.
  2083. */
  2084. function pathauto_file_update_alias(stdClass $file, $op, array $options = array()) {
  2085. // Skip processing if the user has disabled pathauto for the file.
  2086. if (isset($file->path['pathauto']) && empty($file->path['pathauto']) && empty($options['force'])) {
  2087. return;
  2088. }
  2089. $options += array('language' => pathauto_entity_language('file', $file));
  2090. // Skip processing if the file has no pattern.
  2091. if (!pathauto_pattern_load_by_entity('file', $file->type, $options['language'])) {
  2092. return;
  2093. }
  2094. module_load_include('inc', 'pathauto');
  2095. $uri = entity_uri('file', $file);
  2096. pathauto_create_alias('file', $op, $uri['path'], array('file' => $file), $file->type, $options['language']);
  2097. }
  2098. /**
  2099. * Update the URL aliases for multiple files.
  2100. *
  2101. * @param $fids
  2102. * An array of file IDs.
  2103. * @param $op
  2104. * Operation being performed on the files ('insert', 'update' or
  2105. * 'bulkupdate').
  2106. * @param $options
  2107. * An optional array of additional options.
  2108. */
  2109. function pathauto_file_update_alias_multiple(array $fids, $op, array $options = array()) {
  2110. $options += array('message' => FALSE);
  2111. $files = file_load_multiple($fids);
  2112. foreach ($files as $file) {
  2113. pathauto_file_update_alias($file, $op, $options);
  2114. }
  2115. if (!empty($options['message'])) {
  2116. drupal_set_message(format_plural(count($fids), 'Updated URL alias for 1 file.', 'Updated URL aliases for @count files.'));
  2117. }
  2118. }
  2119. /**
  2120. * Update action wrapper for pathauto_file_update_alias().
  2121. */
  2122. function pathauto_file_update_action($file, $context = array()) {
  2123. pathauto_file_update_alias($file, 'bulkupdate', array('message' => TRUE));
  2124. }
  2125. /**
  2126. * @} End of "name pathauto_file".
  2127. */
  2128. /**
  2129. * Implements hook_form_FORM_ID_alter() for file_entity_edit() on behalf of path.module.
  2130. */
  2131. function path_form_file_entity_edit_alter(&$form, $form_state) {
  2132. // Make sure this does not show up on the delete confirmation form.
  2133. if (empty($form_state['confirm_delete'])) {
  2134. $file = $form_state['file'];
  2135. $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL;
  2136. $langcode = !empty($langcode) ? $langcode : LANGUAGE_NONE;
  2137. $conditions = array('source' => 'file/' . $file->fid, 'language' => $langcode);
  2138. $path = (isset($file->fid) ? path_load($conditions) : array());
  2139. if ($path === FALSE) {
  2140. $path = array();
  2141. }
  2142. $path += array(
  2143. 'pid' => NULL,
  2144. 'source' => isset($file->fid) ? 'file/' . $file->fid : NULL,
  2145. 'alias' => '',
  2146. 'language' => $langcode,
  2147. );
  2148. $form['path'] = array(
  2149. '#type' => 'fieldset',
  2150. '#title' => t('URL path settings'),
  2151. '#collapsible' => TRUE,
  2152. '#collapsed' => empty($path['alias']),
  2153. '#group' => 'additional_settings',
  2154. '#attributes' => array(
  2155. 'class' => array('path-form'),
  2156. ),
  2157. '#attached' => array(
  2158. 'js' => array(drupal_get_path('module', 'path') . '/path.js'),
  2159. ),
  2160. '#access' => user_access('create url aliases') || user_access('administer url aliases'),
  2161. '#weight' => 30,
  2162. '#tree' => TRUE,
  2163. '#element_validate' => array('path_form_element_validate'),
  2164. );
  2165. $form['path']['alias'] = array(
  2166. '#type' => 'textfield',
  2167. '#title' => t('URL alias'),
  2168. '#default_value' => $path['alias'],
  2169. '#maxlength' => 255,
  2170. '#description' => t('Optionally specify an alternative URL by which this file can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
  2171. );
  2172. $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
  2173. $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
  2174. $form['path']['language'] = array('#type' => 'value', '#value' => $path['language']);
  2175. }
  2176. }
  2177. /**
  2178. * Implements hook_file_insert() on behalf of path.module.
  2179. */
  2180. function path_file_insert($file) {
  2181. if (isset($file->path)) {
  2182. $path = $file->path;
  2183. $path['alias'] = trim($path['alias']);
  2184. // Only save a non-empty alias.
  2185. if (!empty($path['alias'])) {
  2186. // Ensure fields for programmatic executions.
  2187. $path['source'] = 'file/' . $file->fid;
  2188. // Core does not provide a way to store the file language but contrib
  2189. // modules can do it so we need to take this into account.
  2190. $langcode = entity_language('file', $file);
  2191. $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE;
  2192. path_save($path);
  2193. }
  2194. }
  2195. }
  2196. /**
  2197. * Implements hook_file_update() on behalf of path.module.
  2198. */
  2199. function path_file_update($file) {
  2200. if (isset($file->path)) {
  2201. $path = $file->path;
  2202. $path['alias'] = trim($path['alias']);
  2203. // Delete old alias if user erased it.
  2204. if (!empty($path['fid']) && empty($path['alias'])) {
  2205. path_delete($path['fid']);
  2206. }
  2207. // Only save a non-empty alias.
  2208. if (!empty($path['alias'])) {
  2209. // Ensure fields for programmatic executions.
  2210. $path['source'] = 'file/' . $file->fid;
  2211. // Core does not provide a way to store the file language but contrib
  2212. // modules can do it so we need to take this into account.
  2213. $langcode = entity_language('file', $file);
  2214. $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE;
  2215. path_save($path);
  2216. }
  2217. }
  2218. }
  2219. /**
  2220. * Implements hook_file_delete() on behalf of path.module.
  2221. */
  2222. function path_file_delete($file) {
  2223. // Delete all aliases associated with this file.
  2224. path_delete(array('source' => 'file/' . $file->fid));
  2225. }
  2226. /**
  2227. * Checks if pattern(s) match mimetype(s).
  2228. */
  2229. function file_entity_match_mimetypes($needle, $haystack) {
  2230. $needle = is_array($needle) ? $needle : array($needle);
  2231. $haystack = is_array($haystack) ? $haystack : array($haystack);
  2232. foreach ($haystack as $mimetype) {
  2233. foreach ($needle as $search) {
  2234. if (file_entity_fnmatch($search, $mimetype) || file_entity_fnmatch($mimetype, $search)) {
  2235. return TRUE;
  2236. }
  2237. }
  2238. }
  2239. return FALSE;
  2240. }
  2241. /**
  2242. * A wrapper function for the PHP function fnmatch().
  2243. *
  2244. * We include this, because Windows servers do not implement fnmatch() until
  2245. * PHP Version 5.3. See: http://php.net/manual/en/function.fnmatch.php
  2246. */
  2247. function file_entity_fnmatch($pattern, $string) {
  2248. if (!function_exists('fnmatch')) {
  2249. return preg_match("#^" . strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']')) . "$#", $string);
  2250. }
  2251. return fnmatch($pattern, $string);
  2252. }
  2253. /**
  2254. * Return an URI for a file download.
  2255. */
  2256. function file_entity_download_uri($file) {
  2257. $uri = array('path' => "file/{$file->fid}/download", 'options' => array());
  2258. if (!variable_get('file_entity_allow_insecure_download', FALSE)) {
  2259. $uri['options']['query']['token'] = file_entity_get_download_token($file);
  2260. }
  2261. return $uri;
  2262. }
  2263. function file_entity_file_get_mimetype_type($file) {
  2264. list($type, $subtype) = explode('/', $file->filemime, 2);
  2265. return $type;
  2266. }
  2267. /**
  2268. * Implements hook_admin_menu_map().
  2269. */
  2270. function file_entity_admin_menu_map() {
  2271. if (!user_access('administer file types')) {
  2272. return;
  2273. }
  2274. $map['admin/structure/file-types/manage/%file_type'] = array(
  2275. 'parent' => 'admin/structure/file-types',
  2276. 'arguments' => array(
  2277. array('%file_type' => array_keys(file_entity_type_get_names())),
  2278. ),
  2279. );
  2280. return $map;
  2281. }
  2282. /*
  2283. * Generate a file download CSRF token.
  2284. *
  2285. * This is essentially a duplicate of drupal_get_token, that attempts to still
  2286. * work if the user is anonymous, by using ip_address() as the identifier
  2287. * rather than session_id().
  2288. *
  2289. * @param object $file
  2290. * A file entity object.
  2291. *
  2292. * @return string
  2293. * A CSRF token string.
  2294. */
  2295. function file_entity_get_download_token($file) {
  2296. $identifier = !empty($GLOBALS['user']->uid) ? session_id() : '';
  2297. return drupal_hmac_base64("file/$file->fid/download" . ':' . filemtime($file->uri) . ':' . $file->filesize, $identifier . drupal_get_private_key() . drupal_get_hash_salt());
  2298. }
  2299. /**
  2300. * Find all fields that are of a certain field type.
  2301. *
  2302. * @param string $field_type
  2303. * A field type.
  2304. *
  2305. * @return array
  2306. * An array of field names that match the type $field_type.
  2307. */
  2308. function _file_entity_get_fields_by_type($field_type) {
  2309. $return = array();
  2310. if (function_exists('field_info_field_map')) {
  2311. foreach (field_info_field_map() as $field_name => $field) {
  2312. if ($field['type'] == $field_type) {
  2313. $return[$field_name] = $field_name;
  2314. }
  2315. }
  2316. }
  2317. else {
  2318. foreach (field_info_fields() as $field_name => $field) {
  2319. if ($field['type'] == $field_type) {
  2320. $return[$field_name] = $field_name;
  2321. }
  2322. }
  2323. }
  2324. return $return;
  2325. }
  2326. /**
  2327. * Implements hook_field_attach_load().
  2328. */
  2329. function file_entity_field_attach_load($entity_type, $entities, $age, $options) {
  2330. // Loop over all the entities looking for entities with attached images.
  2331. foreach ($entities as $entity) {
  2332. list(, , $bundle) = entity_extract_ids($entity_type, $entity);
  2333. // Examine every image field instance attached to this entity's bundle.
  2334. $instances = array_intersect_key(field_info_instances($entity_type, $bundle), _file_entity_get_fields_by_type('image'));
  2335. foreach ($instances as $field_name => $instance) {
  2336. if (!empty($entity->{$field_name})) {
  2337. foreach ($entity->{$field_name} as $langcode => $items) {
  2338. foreach ($items as $delta => $item) {
  2339. // If alt and title text is not specified, fall back to alt and
  2340. // title text on the file.
  2341. if (!empty($item['fid']) && (empty($item['alt']) || empty($item['title']))) {
  2342. $file = file_load($item['fid']);
  2343. foreach (array('alt', 'title') as $key) {
  2344. if (empty($item[$key]) && !empty($file->{$key})) {
  2345. $entity->{$field_name}[$langcode][$delta][$key] = $file->{$key};
  2346. }
  2347. }
  2348. }
  2349. }
  2350. }
  2351. }
  2352. }
  2353. }
  2354. }
  2355. function file_entity_get_public_and_private_stream_wrapper_names($flag = STREAM_WRAPPERS_VISIBLE) {
  2356. $wrappers = array();
  2357. foreach (file_get_stream_wrappers($flag) as $key => $wrapper) {
  2358. if (empty($wrapper['private'])) {
  2359. $wrappers['public'][$key] = $wrapper['name'];
  2360. }
  2361. else {
  2362. $wrappers['private'][$key] = $wrapper['name'];
  2363. }
  2364. }
  2365. return $wrappers;
  2366. }