file_entity.module 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <?php
  2. /**
  3. * @file
  4. * Extends Drupal file entities to be fieldable and viewable.
  5. */
  6. /**
  7. * As part of extending Drupal core's file entity API, this module adds some
  8. * functions to the 'file' namespace. For organization, those are kept in the
  9. * 'file_entity.file_api.inc' file.
  10. */
  11. require_once dirname(__FILE__) . '/file_entity.file_api.inc';
  12. // @todo Remove when http://drupal.org/node/977052 is fixed.
  13. require_once dirname(__FILE__) . '/file_entity.field.inc';
  14. /**
  15. * Implements hook_help().
  16. */
  17. function file_entity_help($path, $arg) {
  18. switch ($path) {
  19. case 'admin/config/media/file-types':
  20. $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>';
  21. return $output;
  22. }
  23. }
  24. /**
  25. * Access callback for files.
  26. */
  27. function file_entity_access($op) {
  28. return (user_access('administer files') || user_access($op . ' file'));
  29. }
  30. /**
  31. * Implements hook_menu().
  32. */
  33. function file_entity_menu() {
  34. // File Configuration
  35. $items['admin/config/media/file-types'] = array(
  36. 'title' => 'File types',
  37. 'description' => 'Manage settings for the type of files used on your site.',
  38. 'page callback' => 'file_entity_list_types_page',
  39. 'access arguments' => array('administer site configuration'),
  40. 'file' => 'file_entity.admin.inc',
  41. );
  42. $items['admin/config/media/file-types/manage/%'] = array(
  43. 'title' => 'Manage file types',
  44. 'description' => 'Manage settings for the type of files used on your site.',
  45. );
  46. $items['admin/content/file'] = array(
  47. 'title' => 'Files',
  48. 'description' => 'Manage files used on your site.',
  49. 'page callback' => 'drupal_get_form',
  50. 'page arguments' => array('file_entity_admin_files'),
  51. 'access arguments' => array('administer files'),
  52. 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM,
  53. 'file' => 'file_entity.admin.inc',
  54. );
  55. $items['admin/content/file/list'] = array(
  56. 'title' => 'List',
  57. 'type' => MENU_DEFAULT_LOCAL_TASK,
  58. );
  59. // general view, edit, delete for files
  60. $items['file/%file'] = array(
  61. 'page callback' => 'file_entity_view_page',
  62. 'page arguments' => array(1),
  63. 'access callback' => 'file_entity_access',
  64. 'access arguments' => array('view'),
  65. 'file' => 'file_entity.pages.inc',
  66. );
  67. $items['file/%file/view'] = array(
  68. 'title' => 'View',
  69. 'type' => MENU_DEFAULT_LOCAL_TASK,
  70. 'weight' => -10,
  71. );
  72. $items['file/%file/edit'] = array(
  73. 'title' => 'Edit',
  74. 'page callback' => 'file_entity_page_edit',
  75. 'page arguments' => array(1),
  76. 'access callback' => 'file_entity_access',
  77. 'access arguments' => array('edit'),
  78. 'weight' => 0,
  79. 'type' => MENU_LOCAL_TASK,
  80. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  81. 'file' => 'file_entity.pages.inc',
  82. );
  83. $items['file/%file/delete'] = array(
  84. 'title' => 'Delete',
  85. 'page callback' => 'file_entity_page_delete',
  86. 'page arguments' => array(1),
  87. 'access callback' => 'file_entity_access',
  88. 'access arguments' => array('edit'),
  89. 'weight' => 1,
  90. 'type' => MENU_LOCAL_TASK,
  91. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  92. 'file' => 'file_entity.pages.inc',
  93. );
  94. // Attach a "Manage file display" tab to each file type in the same way that
  95. // Field UI attaches "Manage fields" and "Manage display" tabs. Note that
  96. // Field UI does not have to be enabled; we're just using the same IA pattern
  97. // here for attaching the "Manage file display" page.
  98. $entity_info = entity_get_info('file');
  99. foreach ($entity_info['bundles'] as $file_type => $bundle_info) {
  100. if (isset($bundle_info['admin'])) {
  101. // Get the base path and access.
  102. $path = $bundle_info['admin']['path'];
  103. $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
  104. $access += array(
  105. 'access callback' => 'user_access',
  106. 'access arguments' => array('administer site configuration'),
  107. );
  108. // The file type must be passed to the page callbacks. It might be
  109. // configured as a wildcard (multiple file types sharing the same menu
  110. // router path).
  111. $file_type_argument = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $file_type;
  112. // Add the 'Manage file display' tab.
  113. $items["$path/file-display"] = array(
  114. 'title' => 'Manage file display',
  115. 'page callback' => 'drupal_get_form',
  116. 'page arguments' => array('file_entity_file_display_form', $file_type_argument, 'default'),
  117. 'type' => MENU_LOCAL_TASK,
  118. 'weight' => 3,
  119. 'file' => 'file_entity.admin.inc',
  120. ) + $access;
  121. // Add a secondary tab for each view mode.
  122. $weight = 0;
  123. $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view modes'];
  124. foreach ($view_modes as $view_mode => $view_mode_info) {
  125. $items["$path/file-display/$view_mode"] = array(
  126. 'title' => $view_mode_info['label'],
  127. 'page arguments' => array('file_entity_file_display_form', $file_type_argument, $view_mode),
  128. 'type' => ($view_mode == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK),
  129. 'weight' => ($view_mode == 'default' ? -10 : $weight++),
  130. 'file' => 'file_entity.admin.inc',
  131. // View modes for which the 'custom settings' flag isn't TRUE are
  132. // disabled via this access callback. This needs to extend, rather
  133. // than override normal $access rules.
  134. 'access callback' => '_file_entity_view_mode_menu_access',
  135. 'access arguments' => array_merge(array($file_type_argument, $view_mode, $access['access callback']), $access['access arguments']),
  136. );
  137. }
  138. }
  139. }
  140. return $items;
  141. }
  142. /**
  143. * Implement hook_permission().
  144. */
  145. function file_entity_permission() {
  146. return array(
  147. 'administer files' => array(
  148. 'title' => t('Administer files'),
  149. 'description' => t('Add, edit or delete files and administer settings.'),
  150. ),
  151. 'view file' => array(
  152. 'title' => t('View file'),
  153. 'description' => t('View all files.'),
  154. ),
  155. 'edit file' => array(
  156. 'title' => t('Edit file'),
  157. 'description' => t('Edit all files.'),
  158. ),
  159. );
  160. }
  161. /**
  162. * Implements hook_admin_paths().
  163. */
  164. function file_entity_admin_paths() {
  165. $paths = array(
  166. 'file/*/edit' => TRUE,
  167. 'file/*/delete' => TRUE,
  168. );
  169. return $paths;
  170. }
  171. /**
  172. * Implements hook_theme().
  173. */
  174. function file_entity_theme() {
  175. return array(
  176. 'file_entity' => array(
  177. 'render element' => 'elements',
  178. 'template' => 'file_entity',
  179. ),
  180. 'file_entity_file_type_overview' => array(
  181. 'variables' => array('label' => NULL, 'description' => NULL),
  182. 'file' => 'file_entity.admin.inc',
  183. ),
  184. 'file_entity_file_display_order' => array(
  185. 'render element' => 'element',
  186. 'file' => 'file_entity.admin.inc',
  187. ),
  188. 'file_entity_file_link' => array(
  189. 'variables' => array('file' => NULL, 'icon_directory' => NULL),
  190. )
  191. );
  192. }
  193. /**
  194. * Implements hook_entity_info_alter().
  195. *
  196. * Extends the core file entity to be fieldable. Modules can define file types
  197. * via hook_file_type_info(). For each defined type, create a bundle, so that
  198. * fields can be configured per file type.
  199. */
  200. function file_entity_entity_info_alter(&$entity_info) {
  201. $entity_info['file']['fieldable'] = TRUE;
  202. $entity_info['file']['entity keys']['bundle'] = 'type';
  203. $entity_info['file']['bundles'] = array();
  204. $entity_info['file']['uri callback'] = 'file_entity_uri';
  205. $entity_info['file']['view modes']['full'] = array(
  206. 'label' => t('Full'),
  207. 'custom settings' => FALSE,
  208. );
  209. foreach (file_info_file_types() as $type => $info) {
  210. $info += array(
  211. // Provide a default administration path for Field UI, but not if 'admin'
  212. // has been explicitly set to NULL.
  213. 'admin' => array(
  214. 'path' => 'admin/config/media/file-types/manage/%',
  215. 'real path' => 'admin/config/media/file-types/manage/' . $type,
  216. 'bundle argument' => 5,
  217. ),
  218. );
  219. $entity_info['file']['bundles'][$type] = array_intersect_key($info, drupal_map_assoc(array('label', 'admin')));
  220. $entity_info['file']['view callback'] = 'file_view_multiple';
  221. }
  222. }
  223. /**
  224. * URI callback for file entities.
  225. */
  226. function file_entity_uri($file) {
  227. $uri['path'] = 'file/' . $file->fid;
  228. return $uri;
  229. }
  230. /**
  231. * Implements hook_field_extra_fields().
  232. *
  233. * Adds 'file' as an extra field, so that its display and form component can be
  234. * weighted relative to the fields that are added to file entity bundles.
  235. */
  236. function file_entity_field_extra_fields() {
  237. $info = array();
  238. foreach (file_type_get_names() as $type => $name) {
  239. $info['file'][$type]['form']['filename'] = array(
  240. 'label' => t('File name'),
  241. 'description' => t('File name'),
  242. 'weight' => -10,
  243. );
  244. $info['file'][$type]['form']['preview'] = array(
  245. 'label' => t('File'),
  246. 'description' => t('File preview'),
  247. 'weight' => -5,
  248. );
  249. $info['file'][$type]['display']['file'] = array(
  250. 'label' => t('File'),
  251. 'description' => t('File display'),
  252. 'weight' => 0,
  253. );
  254. }
  255. return $info;
  256. }
  257. /**
  258. * Implements hook_file_presave().
  259. */
  260. function file_entity_file_presave($file) {
  261. // Always ensure the filemime property is current.
  262. if (!empty($file->original) || empty($file->filemime)) {
  263. $file->filemime = file_get_mimetype($file->uri);
  264. }
  265. // Always update file type based on filemime.
  266. $file->type = file_get_type($file);
  267. field_attach_presave('file', $file);
  268. }
  269. /**
  270. * Implements hook_file_insert().
  271. */
  272. function file_entity_file_insert($file) {
  273. field_attach_insert('file', $file);
  274. }
  275. /**
  276. * Implement hook_file_update().
  277. */
  278. function file_entity_file_update($file) {
  279. field_attach_update('file', $file);
  280. }
  281. /**
  282. * Implements hook_file_delete().
  283. */
  284. function file_entity_file_delete($file) {
  285. field_attach_delete('file', $file);
  286. }
  287. /**
  288. * Implements hook_file_formatter_info().
  289. */
  290. function file_entity_file_formatter_info() {
  291. $formatters = array();
  292. // Allow file field formatters to be reused for displaying the file entity's
  293. // file pseudo-field.
  294. if (module_exists('file')) {
  295. foreach (field_info_formatter_types() as $field_formatter_type => $field_formatter_info) {
  296. if (in_array('file', $field_formatter_info['field types'])) {
  297. $formatters['file_field_' . $field_formatter_type] = array(
  298. 'label' => $field_formatter_info['label'],
  299. 'view callback' => 'file_entity_file_formatter_file_field_view',
  300. );
  301. if (isset($field_formatter_info['settings'])) {
  302. $formatters['file_field_' . $field_formatter_type] += array(
  303. 'default settings' => $field_formatter_info['settings'],
  304. 'settings callback' => 'file_entity_file_formatter_file_field_settings',
  305. );
  306. }
  307. }
  308. }
  309. }
  310. // Add a simple file formatter for displaying an image in a chosen style.
  311. if (module_exists('image')) {
  312. $formatters['file_image'] = array(
  313. 'label' => t('Image'),
  314. 'default settings' => array('image_style' => ''),
  315. 'view callback' => 'file_entity_file_formatter_file_image_view',
  316. 'settings callback' => 'file_entity_file_formatter_file_image_settings',
  317. );
  318. }
  319. return $formatters;
  320. }
  321. /**
  322. * Implements hook_file_formatter_FORMATTER_view().
  323. *
  324. * This function provides a bridge to the field formatter API, so that file
  325. * field formatters can be reused for displaying the file entity's file
  326. * pseudo-field.
  327. */
  328. function file_entity_file_formatter_file_field_view($file, $display, $langcode) {
  329. if (strpos($display['type'], 'file_field_') === 0) {
  330. $field_formatter_type = substr($display['type'], strlen('file_field_'));
  331. $field_formatter_info = field_info_formatter_types($field_formatter_type);
  332. if (isset($field_formatter_info['module'])) {
  333. // Set $display['type'] to what hook_field_formatter_*() expects.
  334. $display['type'] = $field_formatter_type;
  335. // Set $items to what file field formatters expect. See file_field_load(),
  336. // and note that, here, $file is already a fully loaded entity.
  337. $items = array((array) $file);
  338. // Invoke hook_field_formatter_prepare_view() and
  339. // hook_field_formatter_view(). Note that we are reusing field formatter
  340. // functions, but we are not displaying a Field API field, so we set
  341. // $field and $instance accordingly, and do not invoke
  342. // hook_field_prepare_view(). This assumes that the formatter functions do
  343. // not rely on $field or $instance. A module that implements formatter
  344. // functions that rely on $field or $instance (and therefore, can only be
  345. // used for real fields) can prevent this formatter from being used on the
  346. // pseudo-field by removing it within hook_file_formatter_info_alter().
  347. $field = $instance = NULL;
  348. if (($function = ($field_formatter_info['module'] . '_field_formatter_prepare_view')) && function_exists($function)) {
  349. $fid = $file->fid;
  350. // hook_field_formatter_prepare_view() alters $items by reference.
  351. $grouped_items = array($fid => &$items);
  352. $function('file', array($fid => $file), $field, array($fid => $instance), $langcode, $grouped_items, array($fid => $display));
  353. }
  354. if (($function = ($field_formatter_info['module'] . '_field_formatter_view')) && function_exists($function)) {
  355. $element = $function('file', $file, $field, $instance, $langcode, $items, $display);
  356. // We passed the file as $items[0], so return the corresponding element.
  357. if (isset($element[0])) {
  358. return $element[0];
  359. }
  360. }
  361. }
  362. }
  363. }
  364. /**
  365. * Implements hook_file_formatter_FORMATTER_settings().
  366. *
  367. * This function provides a bridge to the field formatter API, so that file
  368. * field formatters can be reused for displaying the file entity's file
  369. * pseudo-field.
  370. */
  371. function file_entity_file_formatter_file_field_settings($form, &$form_state, $settings, $formatter_type, $file_type, $view_mode) {
  372. if (strpos($formatter_type, 'file_field_') === 0) {
  373. $field_formatter_type = substr($formatter_type, strlen('file_field_'));
  374. $field_formatter_info = field_info_formatter_types($field_formatter_type);
  375. // Invoke hook_field_formatter_settings_form(). We are reusing field
  376. // formatter functions, but we are not working with a Field API field, so
  377. // set $field accordingly. Unfortunately, the API is for $settings to be
  378. // transfered via the $instance parameter, so we must mock it.
  379. if (isset($field_formatter_info['module']) && ($function = ($field_formatter_info['module'] . '_field_formatter_settings_form')) && function_exists($function)) {
  380. $field = NULL;
  381. $mock_instance['display'][$view_mode] = array(
  382. 'type' => $field_formatter_type,
  383. 'settings' => $settings,
  384. );
  385. return $function($field, $mock_instance, $view_mode, $form, $form_state);
  386. }
  387. }
  388. }
  389. /**
  390. * Implements hook_file_formatter_FORMATTER_view().
  391. *
  392. * Returns a drupal_render() array to display an image of the chosen style.
  393. *
  394. * This formatter is only capable of displaying local images. If the passed in
  395. * file is either not local or not an image, nothing is returned, so that
  396. * file_view_file() can try another formatter.
  397. */
  398. function file_entity_file_formatter_file_image_view($file, $display, $langcode) {
  399. $scheme = file_uri_scheme($file->uri);
  400. $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
  401. if (isset($local_wrappers[$scheme]) && strpos($file->filemime, 'image/') === 0 && $image = image_load($file->uri)) {
  402. if (!empty($display['settings']['image_style'])) {
  403. $element = array(
  404. '#theme' => 'image_style',
  405. '#style_name' => $display['settings']['image_style'],
  406. '#path' => $file->uri,
  407. '#width' => $image->info['width'],
  408. '#height' => $image->info['height'],
  409. );
  410. }
  411. else {
  412. $element = array(
  413. '#theme' => 'image',
  414. '#path' => $file->uri,
  415. '#width' => $image->info['width'],
  416. '#height' => $image->info['height'],
  417. );
  418. }
  419. return $element;
  420. }
  421. }
  422. /**
  423. * Implements hook_file_formatter_FORMATTER_settings().
  424. *
  425. * Returns form elements for configuring the 'file_image' formatter.
  426. */
  427. function file_entity_file_formatter_file_image_settings($form, &$form_state, $settings) {
  428. $element = array();
  429. $element['image_style'] = array(
  430. '#title' => t('Image style'),
  431. '#type' => 'select',
  432. '#options' => image_style_options(FALSE),
  433. '#default_value' => $settings['image_style'],
  434. '#empty_option' => t('None (original image)'),
  435. );
  436. return $element;
  437. }
  438. /**
  439. * Menu access callback for the 'view mode file display settings' pages.
  440. *
  441. * Based on _field_ui_view_mode_menu_access(), but the Field UI module might not
  442. * be enabled.
  443. */
  444. function _file_entity_view_mode_menu_access($file_type, $view_mode, $access_callback) {
  445. // Deny access if the view mode isn't configured to use custom display
  446. // settings.
  447. $view_mode_settings = field_view_mode_settings('file', $file_type);
  448. $visibility = ($view_mode == 'default') || !empty($view_mode_settings[$view_mode]['custom_settings']);
  449. if (!$visibility) {
  450. return FALSE;
  451. }
  452. // Otherwise, continue to an $access_callback check.
  453. $args = array_slice(func_get_args(), 3);
  454. $callback = empty($access_callback) ? 0 : trim($access_callback);
  455. if (is_numeric($callback)) {
  456. return (bool) $callback;
  457. }
  458. elseif (function_exists($access_callback)) {
  459. return call_user_func_array($access_callback, $args);
  460. }
  461. }
  462. /**
  463. * Implements hook_modules_enabled().
  464. */
  465. function file_entity_modules_enabled($modules) {
  466. file_info_cache_clear();
  467. }
  468. /**
  469. * Implements hook_modules_disabled().
  470. */
  471. function file_entity_modules_disabled($modules) {
  472. file_info_cache_clear();
  473. }
  474. /**
  475. * Implements hook_views_api().
  476. */
  477. function file_entity_views_api() {
  478. return array(
  479. 'api' => 3,
  480. );
  481. }
  482. /**
  483. * Returns whether the current page is the full page view of the passed-in file.
  484. *
  485. * @param $file
  486. * A file object.
  487. */
  488. function file_is_page($file) {
  489. $page_file = menu_get_object('file', 1);
  490. return (!empty($page_file) ? $page_file->fid == $file->fid : FALSE);
  491. }
  492. /**
  493. * Process variables for file_entity.tpl.php
  494. *
  495. * The $variables array contains the following arguments:
  496. * - $file
  497. * - $view_mode
  498. *
  499. * @see file_entity.tpl.php
  500. */
  501. function template_preprocess_file_entity(&$variables) {
  502. $view_mode = $variables['view_mode'] = $variables['elements']['#view_mode'];
  503. $variables['file'] = $variables['elements']['#file'];
  504. $file = $variables['file'];
  505. $variables['date'] = format_date($file->timestamp);
  506. $account = user_load($file->uid);
  507. $variables['name'] = theme('username', array('account' => $account));
  508. // @todo Use entity_uri once http://drupal.org/node/1057242 is fixed.
  509. //$uri = entity_uri('file', $file);
  510. //$variables['file_url'] = url($uri['path'], $uri['options']);
  511. $variables['file_url'] = file_create_url($file->uri);
  512. $label = entity_label('file', $file);
  513. $variables['label'] = check_plain($label);
  514. $variables['page'] = $view_mode == 'full' && file_is_page($file);
  515. // Disable the file name from being displayed as the title until we can
  516. // figure out a better way to control this.
  517. // @see http://drupal.org/node/1245266
  518. $variables['page'] = TRUE;
  519. // Flatten the file object's member fields.
  520. $variables = array_merge((array) $file, $variables);
  521. // Helpful $content variable for templates.
  522. $variables += array('content' => array());
  523. foreach (element_children($variables['elements']) as $key) {
  524. $variables['content'][$key] = $variables['elements'][$key];
  525. }
  526. // Make the field variables available with the appropriate language.
  527. field_attach_preprocess('file', $file, $variables['content'], $variables);
  528. // Display post information only on certain file types.
  529. if (variable_get('file_submitted_' . $file->type, FALSE)) {
  530. $variables['display_submitted'] = TRUE;
  531. $variables['submitted'] = t('Uploaded by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
  532. $variables['user_picture'] = theme_get_setting('toggle_file_user_picture') ? theme('user_picture', array('account' => $account)) : '';
  533. }
  534. else {
  535. $variables['display_submitted'] = FALSE;
  536. $variables['submitted'] = '';
  537. $variables['user_picture'] = '';
  538. }
  539. // Gather file classes.
  540. $variables['classes_array'][] = drupal_html_class('file-' . $file->type);
  541. $variables['classes_array'][] = drupal_html_class('file-' . $file->filemime);
  542. if ($file->status != FILE_STATUS_PERMANENT) {
  543. $variables['classes_array'][] = 'file-temporary';
  544. }
  545. // Change the 'file-entity' class into 'file'
  546. if ($variables['classes_array'][0] == 'file-entity') {
  547. $variables['classes_array'][0] = 'file';
  548. }
  549. // Clean up name so there are no underscores.
  550. $variables['theme_hook_suggestions'][] = 'file__' . $file->type;
  551. $variables['theme_hook_suggestions'][] = 'file__' . $file->type . '__' . $view_mode;
  552. $variables['theme_hook_suggestions'][] = 'file__' . str_replace(array('/', '-'), array('__', '_'), $file->filemime);
  553. $variables['theme_hook_suggestions'][] = 'file__' . str_replace(array('/', '-'), array('__', '_'), $file->filemime) . '__' . $view_mode;
  554. $variables['theme_hook_suggestions'][] = 'file__' . $file->fid;
  555. $variables['theme_hook_suggestions'][] = 'file__' . $file->fid . '__' . $view_mode;
  556. }
  557. /**
  558. * Returns the file type name of the passed file or file type string.
  559. *
  560. * @param $file
  561. * A file object or string that indicates the file type to return.
  562. *
  563. * @return
  564. * The file type name or FALSE if the file type is not found.
  565. */
  566. function file_type_get_name($file) {
  567. $type = is_object($file) ? $file->type : $file;
  568. $info = entity_get_info('file');
  569. return isset($info['bundles'][$type]['label']) ? $info['bundles'][$type]['label'] : FALSE;
  570. }
  571. /**
  572. * Returns a list of available file type names.
  573. *
  574. * @return
  575. * An array of file type names, keyed by the type.
  576. */
  577. function file_type_get_names() {
  578. $names = &drupal_static(__FUNCTION__);
  579. if (!isset($names)) {
  580. $info = entity_get_info('file');
  581. foreach ($info['bundles'] as $bundle => $bundle_info) {
  582. $names[$bundle] = $bundle_info['label'];
  583. }
  584. }
  585. return $names;
  586. }
  587. /**
  588. * Implements hook_file_mimetype_mapping_alter().
  589. */
  590. function file_entity_file_mimetype_mapping_alter(&$mapping) {
  591. // Fix the mime type mapping for ogg.
  592. // @todo Remove when http://drupal.org/node/1239376 is fixed in core (7.8).
  593. $new_mappings['ogg'] = 'audio/ogg';
  594. // Add support for m4v.
  595. // @todo Remove when http://drupal.org/node/1290486 is fixed in core (7.9).
  596. $new_mappings['m4v'] = 'video/x-m4v';
  597. // Add support for mka and mkv.
  598. // @todo Remove when http://drupal.org/node/1293140 is fixed in core.
  599. $new_mappings['mka'] = 'audio/x-matroska';
  600. $new_mappings['mkv'] = 'video/x-matroska';
  601. // Add support for webp.
  602. // @todo Remove when http://drupal.org/node/1347624 is fixed in core.
  603. $new_mappings['webp'] = 'image/webp';
  604. foreach ($new_mappings as $extension => $mime_type) {
  605. if (!in_array($mime_type, $mapping['mimetypes'])) {
  606. // If the mime type does not already exist, add it.
  607. $mapping['mimetypes'][] = $mime_type;
  608. }
  609. // Get the index of the mime type and assign the extension to that key.
  610. $index = array_search($mime_type, $mapping['mimetypes']);
  611. $mapping['extensions'][$extension] = $index;
  612. }
  613. }
  614. /**
  615. * Return an array of available view modes for file entities.
  616. */
  617. function file_entity_view_mode_labels() {
  618. $labels = &drupal_static(__FUNCTION__);
  619. if (!isset($options)) {
  620. $entity_info = entity_get_info('file');
  621. $labels = array('default' => t('Default'));
  622. foreach ($entity_info['view modes'] as $machine_name => $mode) {
  623. $labels[$machine_name] = $mode['label'];
  624. }
  625. }
  626. return $labels;
  627. }
  628. /**
  629. * Return the label for a specific file entity view mode.
  630. */
  631. function file_entity_view_mode_label($view_mode, $default = FALSE) {
  632. $labels = file_entity_view_mode_labels();
  633. return isset($labels[$view_mode]) ? $labels[$view_mode] : $default;
  634. }
  635. /**
  636. * Implements hook_file_operations_info().
  637. */
  638. function file_entity_file_operations_info() {
  639. $operations = array(
  640. 'delete' => array(
  641. 'label' => t('Delete selected files'),
  642. 'callback' => 'file_entity_multiple_delete_confirm_operation',
  643. 'confirm' => TRUE,
  644. ),
  645. );
  646. return $operations;
  647. }
  648. /**
  649. * File operation to show a confirm form for file deletion.
  650. *
  651. * @param array $files
  652. * An array of file_ids to delete.
  653. * @return type
  654. */
  655. function file_entity_multiple_delete_confirm_operation($files) {
  656. // This function is a form generation function.
  657. $form = array();
  658. $form_state = array();
  659. // Set the submit handler explicitly because this form is being built
  660. // under a different form_id.
  661. $form['#submit'] = array();
  662. $form['#submit'][] = 'file_entity_multiple_delete_confirm_submit';
  663. return file_entity_multiple_delete_confirm($form, $form_state, $files);
  664. }
  665. /**
  666. * Helper function to get a list of hidden stream wrappers.
  667. *
  668. * This is used in several places to filter queries for media so that files in
  669. * temporary:// don't show up.
  670. */
  671. function file_entity_get_hidden_stream_wrappers() {
  672. return array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STREAM_WRAPPERS_VISIBLE));
  673. }
  674. /**
  675. * A copy of theme_file_file_link() that makes the link point to file/[fid].
  676. *
  677. * @see theme_file_file_link()
  678. * @return type
  679. */
  680. function theme_file_entity_file_link($variables) {
  681. $file = $variables['file'];
  682. $icon_directory = $variables['icon_directory'];
  683. $url = 'file/' . $file->fid;
  684. $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory));
  685. // Set options as per anchor format described at
  686. // http://microformats.org/wiki/file-format-examples
  687. $options = array(
  688. 'attributes' => array(
  689. 'type' => $file->filemime . '; length=' . $file->filesize,
  690. ),
  691. );
  692. // Use the description as the link text if available.
  693. if (empty($file->description)) {
  694. $link_text = $file->filename;
  695. }
  696. else {
  697. $link_text = $file->description;
  698. $options['attributes']['title'] = check_plain($file->filename);
  699. }
  700. return '<span class="file">' . $icon . ' ' . l($link_text, $url, $options) . '</span>';
  701. }