array('label' => t('Application (multipurpose)')), 'audio' => array('label' => t('Audio')), 'image' => array('label' => t('Image')), 'text' => array('label' => t('Text')), 'video' => array('label' => t('Video')), ); drupal_alter('file_type_info', $info); uasort($info, '_file_entity_sort_weight_label'); } if ($file_type) { if (isset($info[$file_type])) { return $info[$file_type]; } } else { return $info; } } /** * Determines the file type of a passed in file object. * * The file type is determined by extracting the 'first' part of the file's * MIME type. For example, a PNG image with a MIME type of 'image/png' will * have a file type of 'image'. * * @link http://www.iana.org/assignments/media-types/index.html IANA list of official MIME media types @endlink */ function file_get_type($file) { // Ensure that a MIME type has been determined first. if (empty($file->filemime)) { $file->filemime = file_get_mimetype($file->uri); } return substr($file->filemime, 0, strpos($file->filemime, '/')); } /** * Returns information about file formatters from hook_file_formatter_info(). * * @param $formatter_type * (optional) A file formatter type name. If ommitted, all file formatter * will be returned. * * @return * Either a file formatter description, as provided by * hook_file_formatter_info(), or an array of all existing file formatters, * keyed by formatter type name. */ function file_info_formatter_types($formatter_type = NULL) { $info = &drupal_static(__FUNCTION__); if (!isset($info)) { $info = module_invoke_all('file_formatter_info'); drupal_alter('file_formatter_info', $info); uasort($info, '_file_entity_sort_weight_label'); } if ($formatter_type) { if (isset($info[$formatter_type])) { return $info[$formatter_type]; } } else { return $info; } } /** * Clears the file info cache. */ function file_info_cache_clear() { drupal_static_reset('file_info_file_types'); drupal_static_reset('file_info_formatter_types'); } /** * Construct a drupal_render() style array from an array of loaded files. * * @param $files * An array of files as returned by file_load_multiple(). * @param $view_mode * View mode. * @param $weight * An integer representing the weight of the first file in the list. * @param $langcode * A string indicating the language field values are to be shown in. If no * language is provided the current content language is used. * * @return * An array in the format expected by drupal_render(). */ function file_view_multiple($files, $view_mode = 'full', $weight = 0, $langcode = NULL) { if (empty($files)) { return array(); } field_attach_prepare_view('file', $files, $view_mode, $langcode); entity_prepare_view('file', $files, $langcode); $build = array(); foreach ($files as $file) { $build[$file->fid] = file_view($file, $view_mode, $langcode); $build[$file->fid]['#weight'] = $weight; $weight++; } $build['#sorted'] = TRUE; return $build; } /** * Generate an array for rendering the given file. * * @param $file * A file object. * @param $view_mode * View mode. * @param $langcode * (optional) A language code to use for rendering. Defaults to the global * content language of the current request. * * @return * An array as expected by drupal_render(). */ function file_view($file, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { $langcode = $GLOBALS['language_content']->language; } // Populate $file->content with a render() array. file_build_content($file, $view_mode, $langcode); $build = $file->content; // We don't need duplicate rendering info in $file->content. unset($file->content); $build += array( '#theme' => 'file_entity', '#file' => $file, '#view_mode' => $view_mode, '#language' => $langcode, ); // Add contextual links for this file, except when the file is already being // displayed on its own page. Modules may alter this behavior (for example, // to restrict contextual links to certain view modes) by implementing // hook_file_view_alter(). if (!empty($file->fid) && !($view_mode == 'full' && file_is_page($file))) { $build['#contextual_links']['file'] = array('file', array($file->fid)); } // Allow modules to modify the structured file. $type = 'file'; drupal_alter(array('file_view', 'entity_view'), $build, $type); return $build; } /** * Builds a structured array representing the file's content. * * @param $file * A file object. * @param $view_mode * View mode, e.g. 'default', 'full', etc. * @param $langcode * (optional) A language code to use for rendering. Defaults to the global * content language of the current request. */ function file_build_content($file, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { $langcode = $GLOBALS['language_content']->language; } // Remove previously built content, if exists. $file->content = array(); // Build the actual file display. // @todo Figure out how to clean this crap up. $file->content['file'] = file_view_file($file, $view_mode, $langcode); if (isset($file->content['file'])) { if (isset($file->content['file']['#theme']) && $file->content['file']['#theme'] != 'file_link') { unset($file->content['file']['#file']); } unset($file->content['file']['#view_mode']); unset($file->content['file']['#language']); } else { unset($file->content['file']); } // Build fields content. // In case of a multiple view, file_view_multiple() already ran the // 'prepare_view' step. An internal flag prevents the operation from running // twice. field_attach_prepare_view('file', array($file->fid => $file), $view_mode, $langcode); entity_prepare_view('file', array($file->fid => $file), $langcode); $file->content += field_attach_view('file', $file, $view_mode, $langcode); $links = array(); $file->content['links'] = array( '#theme' => 'links__file', '#pre_render' => array('drupal_pre_render_links'), '#attributes' => array('class' => array('links', 'inline')), ); $file->content['links']['file'] = array( '#theme' => 'links__file__file', '#links' => $links, '#attributes' => array('class' => array('links', 'inline')), ); // Allow modules to make their own additions to the file. module_invoke_all('file_view', $file, $view_mode, $langcode); module_invoke_all('entity_view', $file, 'file', $view_mode, $langcode); } /** * Generate an array for rendering just the file portion of a file entity. * * @param $file * A file object. * @param $displays * Can be either: * - the name of a view mode; * - or an array of custom display settings, as returned by file_displays(). * @param $langcode * (optional) A language code to use for rendering. Defaults to the global * content language of the current request. * * @return * An array as expected by drupal_render(). */ function file_view_file($file, $displays = 'full', $langcode = NULL) { if (!isset($langcode)) { $langcode = $GLOBALS['language_content']->language; } // Prepare incoming display specifications. if (is_string($displays)) { $view_mode = $displays; $displays = file_displays($file->type, $view_mode); } else { $view_mode = '_custom_display'; } drupal_alter('file_displays', $displays, $file, $view_mode); _file_sort_array_by_weight($displays); // Attempt to display the file with each of the possible displays. Stop after // the first successful one. See file_displays() for details. $element = NULL; foreach ($displays as $formatter_type => $display) { if (!empty($display['status'])) { $formatter_info = file_info_formatter_types($formatter_type); // Under normal circumstances, the UI prevents enabling formatters for // incompatible file types. In case this was somehow circumvented (for // example, a module updated its formatter definition without updating // existing display settings), perform an extra check here. if (isset($formatter_info['file types']) && !in_array($file->type, $formatter_info['file types'])) { continue; } if (isset($formatter_info['view callback']) && ($function = $formatter_info['view callback']) && function_exists($function)) { $display['type'] = $formatter_type; if (!empty($formatter_info['default settings'])) { if (empty($display['settings'])) { $display['settings'] = array(); } $display['settings'] += $formatter_info['default settings']; } $element = $function($file, $display, $langcode); if (isset($element)) { break; } } } } // If none of the configured formatters were able to display the file, attempt // to display the file using the file type's default view callback. if (!isset($element)) { $file_type_info = file_info_file_types($file->type); if (isset($file_type_info['default view callback']) && ($function = $file_type_info['default view callback']) && function_exists($function)) { $element = $function($file, $view_mode, $langcode); } } // If a render element was returned by a formatter or the file type's default // view callback, add some defaults to it and return it. if (isset($element)) { $element += array( '#file' => $file, '#view_mode' => $view_mode, '#language' => $langcode, ); return $element; } } /** * Returns an array of possible displays to use for a file type in a given view mode. * * It is common for a site to be configured with broadly defined file types * (e.g., 'video'), and to have different files of this type require different * displays (for example, the code required to display a YouTube video is * different than the code required to display a local QuickTime video). * Therefore, the site administrator can configure multiple displays for a given * file type. This function returns all of the displays that the administrator * enabled for the given file type in the given view mode. file_view_file() then * invokes each of these, and passes the specific file to display. Each display * implementation can inspect the file, and either return a render array (if it * is capable of displaying the file), or return nothing (if it is incapable of * displaying the file). The first render array returned is the one used. * * @param $file_type * The type of file. * @param $view_mode * The view mode. * * @return * An array keyed by the formatter type name. Each item in the array contains * the following key/value pairs: * - status: Whether this display is enabled. If not TRUE, file_view_file() * skips over it. * - weight: An integer that determines the order of precedence within the * returned array. The lowest weight display capable of displaying the file * is used. * - settings: An array of key/value pairs specific to the formatter type. See * hook_file_formatter_info() for details. * * @see hook_file_formatter_info() * @see file_view_file() */ function file_displays($file_type, $view_mode) { $cache = &drupal_static(__FUNCTION__, array()); // If the requested view mode isn't configured to use a custom display for its // fields, then don't use a custom display for its file either. if ($view_mode != 'default') { $view_mode_settings = field_view_mode_settings('file', $file_type); $view_mode = !empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default'; } if (!isset($cache[$file_type][$view_mode])) { // Load the display configurations for the file type and view mode. If none // exist for the view mode, use the default view mode. $displays = file_displays_load($file_type, $view_mode, TRUE); if (empty($displays) && $view_mode != 'default') { $cache[$file_type][$view_mode] = file_displays($file_type, 'default'); } else { // Convert the display objects to arrays and remove unnecessary keys. foreach ($displays as $formatter_name => $display) { $displays[$formatter_name] = array_intersect_key((array) $display, drupal_map_assoc(array('status', 'weight', 'settings'))); } $cache[$file_type][$view_mode] = $displays; } } return $cache[$file_type][$view_mode]; } /** * Returns an array of {file_display} objects for the file type and view mode. */ function file_displays_load($file_type, $view_mode, $key_by_formatter_name = FALSE) { ctools_include('export'); $display_names = array(); $prefix = $file_type . '__' . $view_mode . '__'; foreach (array_keys(file_info_formatter_types()) as $formatter_name) { $display_names[] = $prefix . $formatter_name; } $displays = ctools_export_load_object('file_display', 'names', $display_names); if ($key_by_formatter_name) { $prefix_length = strlen($prefix); $rekeyed_displays = array(); foreach ($displays as $name => $display) { $rekeyed_displays[substr($name, $prefix_length)] = $display; } $displays = $rekeyed_displays; } return $displays; } /** * Saves a {file_display} object to the database. */ function file_display_save($display) { ctools_include('export'); ctools_export_crud_save('file_display', $display); } /** * Creates a new {file_display} object. */ function file_display_new($file_type, $view_mode, $formatter_name) { ctools_include('export'); $display = ctools_export_crud_new('file_display'); $display->name = implode('__', array($file_type, $view_mode, $formatter_name)); return $display; } /** * Helper function to sort an array by the value of each item's 'weight' key, while preserving relative order of items that have equal weight. */ function _file_sort_array_by_weight(&$a) { $i=0; foreach ($a as $key => $item) { if (!isset($a[$key]['weight'])) { $a[$key]['weight'] = 0; } $original_weight[$key] = $a[$key]['weight']; $a[$key]['weight'] += $i/1000; $i++; } uasort($a, 'drupal_sort_weight'); foreach ($a as $key => $item) { $a[$key]['weight'] = $original_weight[$key]; } } /** * User sort function to sort by weight, then label/name. */ function _file_entity_sort_weight_label($a, $b) { $a_weight = isset($a['weight']) ? $a['weight'] : 0; $b_weight = isset($b['weight']) ? $b['weight'] : 0; if ($a_weight == $b_weight) { $a_label = isset($a['label']) ? $a['label'] : ''; $b_label = isset($b['label']) ? $b['label'] : ''; return strcasecmp($a_label, $b_label); } else { return $a_weight < $b_weight ? -1 : 1; } } /** * Returns a file object which can be passed to file_save(). * * @param $uri * A string containing the URI, path, or filename. * @param $use_existing * (Optional) If TRUE and there's an existing file in the {file_managed} * table with the passed in URI, then that file object is returned. * Otherwise, a new file object is returned. Default is TRUE. * * @return * A file object, or FALSE on error. * * @todo This should probably be named file_load_by_uri($uri, $create_if_not_exists). * @todo Remove this function when http://drupal.org/node/685818 is fixed. */ function file_uri_to_object($uri, $use_existing = TRUE) { $file = FALSE; $uri = file_stream_wrapper_uri_normalize($uri); if ($use_existing) { // We should always attempt to re-use a file if possible. $files = entity_load('file', FALSE, array('uri' => $uri)); $file = !empty($files) ? reset($files) : FALSE; } if (empty($file)) { $file = new stdClass(); $file->uid = $GLOBALS['user']->uid; $file->filename = basename($uri); $file->uri = $uri; $file->filemime = file_get_mimetype($uri); // This is gagged because some uris will not support it. $file->filesize = @filesize($uri); $file->timestamp = REQUEST_TIME; $file->status = FILE_STATUS_PERMANENT; } return $file; } /** * A list of operations which can be called on files. * * @return * An associave array of operations keyed by a system name. * - label: A string to show in the operations dropdown. * - callback (string): A callback function to call for the operation. This * function will be passed an array of file_ids which were selected. * - confirm (boolean): Whether or not this operation requires a confirm form * In the case where confirm is set to true, callback should be a function * which can return a confirm form. */ function hook_file_operations_info() { $operations = array( 'archive_and_email' => array( 'label' => t('Archive the selected files and email them'), 'callback' => 'file_archiver_confirm_form', 'confirm' => TRUE, ), ); return $operations; }