filename); $uri = entity_uri('file', $file); // Set the file path as the canonical URL to prevent duplicate content. drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE); // Set the non-aliased path as a default shortlink. drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE); return file_view($file, 'full'); } /** * Menu callback; download a single file entity. */ function file_entity_download_page($file) { // Ensure there is a valid token to download this file. if (!variable_get('file_entity_allow_insecure_download', FALSE)) { if (!isset($_GET['token']) || $_GET['token'] !== file_entity_get_download_token($file)) { return MENU_ACCESS_DENIED; } } // If the file does not exist it can cause problems with file_transfer(). if (!is_file($file->uri)) { return MENU_NOT_FOUND; } // @todo Why basename? Why not drupal_basename which was working up until recently. $headers = array( 'Content-Type' => mime_header_encode($file->filemime), 'Content-Disposition' => 'attachment; filename="' . mime_header_encode(basename($file->uri)) . '"', 'Content-Length' => $file->filesize, 'Content-Transfer-Encoding' => 'binary', 'Pragma' => 'no-cache', 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', 'Expires' => '0', ); // Let other modules alter the download headers. drupal_alter('file_download_headers', $headers, $file); // Let other modules know the file is being downloaded. module_invoke_all('file_transfer', $file->uri, $headers); if (file_entity_file_is_local($file)) { // For local files, transfer the file and do not reveal the actual URL. file_transfer($file->uri, $headers); } else if (file_uri_scheme($file->uri) == 's3') { $url = file_create_url($file->uri); drupal_goto($url); } else { // For remote files, just redirect the user to that file's actual URL. $headers['Location'] = file_create_url($file->uri); foreach ($headers as $name => $value) { drupal_add_http_header($name, $value); } drupal_send_headers(); drupal_exit(); } } /** * Form callback for adding a file via an upload form. * * This is a multi step form which has 1-3 pages: * - Upload file * - Choose filetype * If there is only one candidate (based on mimetype) we will skip this step. * - Edit fields * Skip this step if there are no fields on this entity type. */ function file_entity_add_upload($form, &$form_state, $options = array()) { if (!is_array($options)) { $options = array($options); } $step = (isset($form_state['step']) && in_array($form_state['step'], array(1, 2, 3, 4))) ? $form_state['step'] : 1; $form['#step'] = $step; $form['#options'] = $options + array( 'types' => array(), 'enabledPlugins' => array(), 'schemes' => array(), 'max_filesize' => '', 'uri_scheme' => file_default_scheme(), 'plugins' => '' ); switch ($step) { case 1: return file_entity_add_upload_step_upload($form, $form_state, $options); case 2: return file_entity_add_upload_step_filetype($form, $form_state, $options); case 3: return file_entity_add_upload_step_scheme($form, $form_state, $options); case 4: return file_entity_add_upload_step_fields($form, $form_state, $options); } } /** * Generate form fields for the first step in the add file wizard. */ function file_entity_add_upload_step_upload($form, &$form_state, array $options = array()) { $form['upload'] = array( '#type' => 'managed_file', '#title' => t('Upload a new file'), '#upload_location' => file_entity_upload_destination_uri($options), '#upload_validators' => file_entity_get_upload_validators($options), '#progress_indicator' => 'bar', '#required' => TRUE, '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'), '#default_value' => isset($form_state['storage']['upload']) ? $form_state['storage']['upload'] : NULL, ); $form['actions'] = array('#type' => 'actions'); $form['actions']['next'] = array( '#type' => 'submit', '#value' => t('Next'), ); form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages'); return $form; } /** * Generate form fields for the second step in the add file wizard. */ function file_entity_add_upload_step_filetype($form, &$form_state, array $options = array()) { $file = file_load($form_state['storage']['upload']); $selected_files = $form['#options']['types']; $form['type'] = array( '#type' => 'radios', '#title' => t('File type'), '#options' => file_entity_get_filetype_candidates($file, $selected_files), '#default_value' => isset($form_state['storage']['type']) ? $form_state['storage']['type'] : NULL, '#required' => TRUE, ); $form['actions'] = array('#type' => 'actions'); $form['actions']['previous'] = array( '#type' => 'submit', '#value' => t('Previous'), '#limit_validation_errors' => array(), '#submit' => array('file_entity_add_upload_submit'), ); $form['actions']['next'] = array( '#type' => 'submit', '#value' => t('Next'), ); return $form; } /** * Generate form fields for the third step in the add file wizard. */ function file_entity_add_upload_step_scheme($form, &$form_state, array $options = array()) { $file = file_load($form_state['storage']['upload']); $schemes = array(); foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) { $schemes[$scheme] = check_plain($info['description']); } // Remove any schemes not found in the instance settings. if (!empty($options['schemes'])) { $schemes = array_intersect_key($schemes, array_flip($options['schemes'])); } // Determine which scheme to use as the default value. if (isset($form_state['storage']['scheme'])) { $fallback_scheme = $form_state['storage']['scheme']; } elseif (!empty($options['uri_scheme'])) { $fallback_scheme = $options['uri_scheme']; } else { $fallback_scheme = file_default_scheme(); } $form['scheme'] = array( '#type' => 'radios', '#title' => t('Destination'), '#options' => $schemes, '#default_value' => $fallback_scheme, '#required' => TRUE, ); $form['actions'] = array('#type' => 'actions'); $form['actions']['previous'] = array( '#type' => 'submit', '#value' => t('Previous'), '#limit_validation_errors' => array(), '#submit' => array('file_entity_add_upload_submit'), ); $form['actions']['next'] = array( '#type' => 'submit', '#value' => t('Next'), ); return $form; } /** * Generate form fields for the fourth step in the add file wizard. */ function file_entity_add_upload_step_fields($form, &$form_state, array $options = array()) { // Load the file and overwrite the filetype set on the previous screen. $file = file_load($form_state['storage']['upload']); $file->type = $form_state['storage']['type']; // Let users modify the filename here. $form['filename'] = array( '#type' => 'textfield', '#title' => t('Name'), '#default_value' => $file->filename, '#required' => TRUE, '#maxlength' => 255, '#weight' => -10, ); // Add fields. field_attach_form('file', $file, $form, $form_state); $form['actions'] = array('#type' => 'actions'); $form['actions']['previous'] = array( '#type' => 'submit', '#value' => t('Previous'), '#limit_validation_errors' => array(), '#submit' => array('file_entity_add_upload_submit'), ); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save'), ); return $form; } /** * Page callback to show file usage information. */ function file_entity_usage_page($file) { drupal_set_title(t('Usage of @type @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH); $rows = array(); $occured_entities = array(); // Determine all of the locations where a file is used, then loop through the // occurrences and filter out any duplicates. foreach (file_usage_list($file) as $module => $type) { foreach ($type as $entity_type => $entity_ids) { // There are cases where the actual entity doesn't exist. // We have to handle this. $entity_info = entity_get_info($entity_type); $entities = empty($entity_info) ? NULL : entity_load($entity_type, array_keys($entity_ids)); foreach ($entity_ids as $entity_id => $count) { // If this entity has already been listed in the table, just add any // additional usage to the total count column in the table row and // continue on to the next iteration of the loop. if (isset($occured_entities[$entity_type][$entity_id])) { $rows[$occured_entities[$entity_type][$entity_id]][2] += $count; continue; } // Retrieve the label and the URI of the entity. $label = empty($entities[$entity_id]) ? $module : entity_label($entity_type, $entities[$entity_id]); $entity_uri = empty($entities[$entity_id]) ? NULL : entity_uri($entity_type, $entities[$entity_id]); // Link the label to the URI when possible. if (!empty($entity_uri['path']) && $entity_type != 'paragraphs_item') { $entity_label = l($label, $entity_uri['path']); } // For paragraphs items, we are searching for usages in nodes. elseif ($entity_type == 'paragraphs_item') { $paragraph_fields = field_read_fields(array('type' => 'paragraphs')); foreach ($paragraph_fields as $paragraph_field) { $field_name = $paragraph_field['field_name']; $query = new EntityFieldQuery(); $query->entityCondition('entity_type', 'node') ->fieldCondition($field_name, 'value', $entity_id, '='); $nid = $query->execute(); if (!empty($nid)) { $nid = implode(array_keys($nid['node'])); if ($nid) { $node = node_load($nid); $entity_label = l($node->title, 'node/' . $nid); } } } } else { $entity_label = check_plain($label); } $rows[] = array($entity_label, $entity_type, $count); // Record the occurrence of the entity to ensure that it isn't listed in // the table again. $occured_entities[$entity_type][$entity_id] = count($rows) - 1; } } } $header = array(t('Entity'), t('Entity type'), t('Use count')); $build['usage_table'] = array( '#theme' => 'table', '#header' => $header, '#rows' => $rows, '#caption' => t('This table lists all of the places where @filename is used.', array('@filename' => $file->filename)), '#empty' => t('This file is not currently used.'), ); return $build; } /** * Get the candidate filetypes for a given file. * * Only filetypes for which the user has access to create entities are returned. * * @param array $file * An upload file array from form_state. * * @return array * An array of file type bundles that support the file's mime type. */ function file_entity_get_filetype_candidates($file, $selected_files = array()) { $types = module_invoke_all('file_type', $file); drupal_alter('file_type', $types, $file); // If no file types are selected in field instance settings, allow all // available types. if (!empty($selected_files)) { // Limit file type candidates to field allowed types. $types = array_intersect($types, $selected_files); } $candidates = array(); foreach ($types as $type) { $file->type = $type; if (file_entity_access('create', $file)) { $candidates[$type] = file_entity_type_get_name($file); } } return $candidates; } /** * Submit handler for the add file form. */ function file_entity_add_upload_submit($form, &$form_state) { $form_state['storage'] = isset($form_state['storage']) ? $form_state['storage'] : array(); $form_state['storage'] = array_merge($form_state['storage'], $form_state['values']); // Field selected allowed types. $selected_files = $form['#options']['types']; // This var is set to TRUE when we are ready to save the file. $save = FALSE; $trigger = $form_state['triggering_element']['#id']; $triggered_next = $trigger == 'edit-next' || (strpos($trigger, 'edit-next--') === 0); $triggered_previous = $trigger == 'edit-previous' || (strpos($trigger, 'edit-previous--') === 0); $step_delta = ($triggered_previous) ? -1 : 1; $steps_to_check = array(2, 3); if ($triggered_previous) { // If the previous button was hit, // the step checking order should be reversed 3, 2. $steps_to_check = array_reverse($steps_to_check); } foreach ($steps_to_check as $step) { // Check if we can skip step 2 and 3. if (($form['#step'] == $step - 1 && $triggered_next) || ($form['#step'] == $step + 1 && $triggered_previous)) { $file = file_load($form_state['storage']['upload']); if ($step == 2) { // Check if we can skip step 2. $candidates = file_entity_get_filetype_candidates($file, $selected_files); if (count($candidates) == 1) { $candidates_keys = array_keys($candidates); // There is only one possible filetype for this file. // Skip the second page. $form['#step'] += $step_delta; $form_state['storage']['type'] = reset($candidates_keys); } elseif (!$candidates || variable_get('file_entity_file_upload_wizard_skip_file_type', FALSE)) { // Do not assign the file a file type. $form['#step'] += $step_delta; $form_state['storage']['type'] = FILE_TYPE_NONE; } } else { // Check if we can skip step 3. $options = $form['#options']; $schemes = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE); // Remove any schemes not found in the instance settings. if (!empty($options['schemes'])) { $schemes = array_intersect_key($schemes, $options['schemes']); } if (!file_entity_file_is_writeable($file)) { // The file is read-only (remote) and must use its provided scheme. $form['#step'] += $step_delta; $form_state['storage']['scheme'] = file_uri_scheme($file->uri); } elseif (count($schemes) == 1) { // There is only one possible stream wrapper for this file. // Skip the third page. $form['#step'] += $step_delta; $form_state['storage']['scheme'] = key($schemes); } elseif (variable_get('file_entity_file_upload_wizard_skip_scheme', FALSE)) { $form['#step'] += $step_delta; // Fallback to the URI scheme specified in the field settings // otherwise use the default file scheme. if (!empty($options['uri_scheme'])) { $form_state['storage']['scheme'] = $options['uri_scheme']; } else { $form_state['storage']['scheme'] = file_default_scheme(); } } } } } // We have the filetype, check if we can skip step 4. if ($form['#step'] == 3 && $triggered_next) { $file = file_load($form_state['storage']['upload']); $form_state['file'] = $file; if (!field_info_instances('file', $form_state['storage']['type'])) { // This filetype doesn't have fields, save the file. $save = TRUE; } elseif (variable_get('file_entity_file_upload_wizard_skip_fields', FALSE)) { // Save the file with blanks fields. $save = TRUE; } // Allow other modules to choose to skip or complete step 4. drupal_alter('file_entity_file_upload_skip_fields', $save, $form_state); } // Form id's can vary depending on how many other forms are displayed, so we // need to do string comparissons. e.g edit-submit--2. if ($triggered_next) { $form_state['step'] = $form['#step'] + 1; } elseif ($triggered_previous) { $form_state['step'] = $form['#step'] - 1; } elseif (strpos($trigger, 'edit-submit') !== FALSE) { $save = TRUE; } if ($save) { $file = file_load($form_state['storage']['upload']); if ($file) { if (file_uri_scheme($file->uri) != $form_state['storage']['scheme']) { $file_destination = $form_state['storage']['scheme'] . '://' . file_uri_target($file->uri); $file_destination = file_stream_wrapper_uri_normalize($file_destination); if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) { // Only re-assign the file object if file_move() did not fail. $file = $moved_file; } } $file->type = $form_state['storage']['type']; $file->display = TRUE; // Change the file from temporary to permanent. $file->status = FILE_STATUS_PERMANENT; // Save the form fields. // Keep in mind that the values for the Field API fields must be in // $form_state['values'] and not in ['storage']. This is true as long as // the fields are on the last page of the multi step form. entity_form_submit_build_entity('file', $file, $form, $form_state); file_save($file); $form_state['file'] = $file; drupal_set_message(t('@type %name was uploaded.', array('@type' => file_entity_type_get_name($file), '%name' => $file->filename))); } else { drupal_set_message(t('An error occurred and no file was uploaded.'), 'error'); return; } // Figure out destination. if (user_access('administer files')) { $path = 'admin/content/file'; } else { $path = 'file/' . $file->fid; } $form_state['redirect'] = $path; } else { $form_state['rebuild'] = TRUE; } // Clear the page and block caches. cache_clear_all(); } /** * Determines the upload location for the file add upload form. * * @param array $params * An array of parameters from the media browser. * @param array $data * (optional) An array of token objects to pass to token_replace(). * * @return string * A file directory URI with tokens replaced. * * @see token_replace() */ function file_entity_upload_destination_uri(array $params, array $data = array()) { $params += array( 'uri_scheme' => file_default_scheme(), 'file_directory' => variable_get('file_entity_default_file_directory', ''), ); $destination = trim($params['file_directory'], '/'); // Replace tokens. $destination = decode_entities(token_replace($destination, $data)); return $params['uri_scheme'] . '://' . $destination; } /** * Form for uploading multiple files. */ function file_entity_add_upload_multiple($form, &$form_state, $params = array()) { $form = file_entity_add_upload($form, $form_state, $params); unset($form['upload']['#title']); // The validators will be set from plupload anyway. This isn't pretty, // but don't allow it to show up twice. unset($form['upload']['#description']); $form['upload']['#type'] = 'plupload'; // Ensure that we call the plupload_element_pre_render function. // If it isn't called, it doesn't set the JS settings that transfers the // list of allowed file extensions to the PLUpload widget. // We override the 'file_entity_upload_validators_pre_render' setting if it // exists, because both pre-render hooks adds the upload-help with list of // allowed file extensions. $index = array_search('file_entity_upload_validators_pre_render', $form['upload']['#pre_render']); if ($index !== FALSE) { $form['upload']['#pre_render'][$index] = 'plupload_element_pre_render'; } else { $form['upload']['#pre_render'][] = 'plupload_element_pre_render'; } $form['submit']['#value'] = t('Start upload'); return $form; } /** * Submit handler for the multiple upload form. */ function file_entity_add_upload_multiple_submit($form, &$form_state) { $upload_location = !empty($form['upload']['#upload_location']) ? $form['upload']['#upload_location'] . '/' : variable_get('file_default_scheme', 'public') . '://'; // Ensure writable destination directory for the files. file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); // We can't use file_save_upload() because of // http://www.jacobsingh.name/content/tight-coupling-no-not. foreach ($form_state['values']['upload'] as $uploaded_file) { if ($uploaded_file['status'] == 'done') { $source = $uploaded_file['tmppath']; $destination = file_stream_wrapper_uri_normalize($upload_location . $uploaded_file['name']); // Rename it to its original name, and put it in its final home. // Note - not using file_move here because if we call file_get_mime // (in file_uri_to_object) while it has a .tmp extension, it horks. $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME); $file = file_uri_to_object($destination); $file->status = FILE_STATUS_PERMANENT; file_save($file); $form_state['files'][$file->fid] = $file; } else { // @todo: move this to element validate or something. form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name']))); } } // Redirect to the file edit page. if (file_entity_access('update', $file) && module_exists('media_bulk_upload')) { $destination = array(); if (isset($_GET['destination'])) { $destination = drupal_get_destination(); unset($_GET['destination']); } elseif (user_access('administer files')) { $destination = array('destination' => 'admin/content/file'); } $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination)); } else { $form_state['redirect'] = user_access('administer files') ? 'admin/content/file' : ''; } // Clear the page and block caches. cache_clear_all(); } /** * Page callback: Form constructor for the file edit form. * * Path: file/%file/edit * * @param object $file * A file object from file_load(). * * @see file_entity_menu() * * @todo Rename this form to file_edit_form to ease into core. */ function file_entity_edit($form, &$form_state, $file) { drupal_set_title(t('Edit @type @title', array('@type' => $file->type, '@title' => $file->filename)), PASS_THROUGH); $form_state['file'] = $file; $form['#attributes']['class'][] = 'file-form'; if (!empty($file->type)) { $form['#attributes']['class'][] = 'file-' . $file->type . '-form'; } // Basic file information. // These elements are just values so they are not even sent to the client. foreach (array('fid', 'type', 'uid', 'timestamp') as $key) { $form[$key] = array( '#type' => 'value', '#value' => isset($file->$key) ? $file->$key : NULL, ); } $form['filename'] = array( '#type' => 'textfield', '#title' => t('Name'), '#default_value' => $file->filename, '#required' => TRUE, '#maxlength' => 255, '#weight' => -10, ); // Add a 'replace this file' upload field if the file is writeable. if (file_entity_file_is_writeable($file)) { // Set up replacement file validation. $replacement_options = !empty($form_state['#upload_options']) ? $form_state['#upload_options'] : array(); // The replacement file must have an extension valid for the original type. $file_extensions = array(); $file_type_name = isset($file->type) ? $file->type : file_get_type($file); if (!empty($replacement_options['file_extensions'])) { $file_extensions = explode(' ', $replacement_options['file_extensions']); } elseif ($file_type_name && ($file_type = file_type_load($file_type_name))) { $file_extensions = file_type_get_valid_extensions($file_type); } // Set allowed file extensions. if (!empty($file_extensions)) { // Set to type based file extensions. $replacement_options['file_extensions'] = implode(' ', $file_extensions); } else { // Fallback to the extension of the current file. $replacement_options['file_extensions'] = pathinfo($file->uri, PATHINFO_EXTENSION); } $form['replace_upload'] = array( '#type' => 'file', '#title' => t('Replace file'), '#description' => t('This file will replace the existing file. This action cannot be undone.'), '#upload_validators' => file_entity_get_upload_validators($replacement_options), '#pre_render' => array('file_entity_upload_validators_pre_render'), ); $form['replace_keep_original_filename'] = array( '#type' => 'checkbox', '#title' => t('Keep original filename'), '#default_value' => variable_get('file_entity_file_replace_options_keep_original_filename', FALSE), '#description' => t('Rename the newly uploaded file to the name of the original file. This action cannot be undone.'), ); } $form['preview'] = file_view_file($file, 'preview'); $form['additional_settings'] = array( '#type' => 'vertical_tabs', '#weight' => 99, ); // File destination information for administrators. $form['destination'] = array( '#type' => 'fieldset', '#access' => user_access('administer files') && file_entity_file_is_writeable($file), '#title' => t('Destination'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#group' => 'additional_settings', '#attributes' => array( 'class' => array('file-form-destination'), ), '#attached' => array( 'js' => array( drupal_get_path('module', 'file_entity') . '/file_entity.js', ), ), ); $options = array(); foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) { $options[$scheme] = check_plain($info['name']); } $form['destination']['scheme'] = array( '#type' => 'radios', '#title' => t('Destination'), '#options' => $options, '#default_value' => file_uri_scheme($file->uri), ); // File user information for administrators. $form['user'] = array( '#type' => 'fieldset', '#access' => user_access('administer files'), '#title' => t('User information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#group' => 'additional_settings', '#attributes' => array( 'class' => array('file-form-user'), ), '#attached' => array( 'js' => array( drupal_get_path('module', 'file_entity') . '/file_entity.js', array( 'type' => 'setting', 'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))), ), ), ), '#weight' => 90, ); $form['user']['name'] = array( '#type' => 'textfield', '#title' => t('Associated with'), '#maxlength' => 60, '#autocomplete_path' => 'user/autocomplete', '#default_value' => (!empty($file->uid) && $user = user_load($file->uid)) ? $user->name : '', '#weight' => -1, '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))), ); // Add the buttons. $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save'), '#weight' => 5, '#submit' => array('file_entity_edit_submit'), '#validate' => array('file_entity_edit_validate'), ); $form['actions']['delete'] = array( '#type' => 'submit', '#value' => t('Delete'), '#weight' => 10, '#submit' => array('file_entity_edit_delete_submit'), '#access' => file_entity_access('delete', $file), ); // Build the URL for the cancel button taking into account that there might be // a "destination" that includes query string variables. $parameters = drupal_get_query_parameters(); $destination = isset($parameters['destination']) ? $parameters['destination'] : 'file/' . $file->fid; $url = drupal_parse_url($destination); $form['actions']['cancel'] = array( '#type' => 'link', '#title' => t('Cancel'), '#href' => $url['path'], '#options' => array('query' => $url['query']), '#weight' => 15, ); $langcode = function_exists('entity_language') ? entity_language('file', $file) : NULL; field_attach_form('file', $file, $form, $form_state, $langcode); return $form; } /** * Form validation handler for file_entity_edit(). */ function file_entity_edit_validate($form, &$form_state) { $file = (object) $form_state['values']; // Validate the "associated user" field. if (!empty($file->name) && !($account = user_load_by_name($file->name))) { // The use of empty() is mandatory in the context of usernames // as the empty string denotes the anonymous user. In case we // are dealing with an anonymous user we set the user ID to 0. form_set_error('name', t('The username %name does not exist.', array('%name' => $file->name))); } // Handle the replacement file if uploaded. if (isset($form_state['values']['replace_upload'])) { // Save the file as a temporary file. $file = file_save_upload('replace_upload', $form['replace_upload']['#upload_validators']); if (!empty($file)) { // Put the temporary file in form_values so we can save it on submit. $form_state['values']['replace_upload'] = $file; } elseif ($file === FALSE) { // File uploaded failed. form_set_error('replace_upload', t('The replacement file could not be uploaded.')); } } // Run entity form validation. entity_form_field_validate('file', $form, $form_state); } /** * Form submission handler for the 'Save' button for file_entity_edit(). */ function file_entity_edit_submit($form, &$form_state) { $file = $form_state['file']; $orphaned_uri = ''; // Check if a replacement file has been uploaded. if (!empty($form_state['values']['replace_upload'])) { $replacement = $form_state['values']['replace_upload']; // Existing image metadata is stored in $file->height and $file->width. // Loop through the replacement metadata and update existing values. if (!empty($replacement->metadata)) { foreach ($replacement->metadata as $metadata_key => $metadata_value) { if (isset($file->{$metadata_key})) { $file->{$metadata_key} = $metadata_value; } } } // Move file from temp to permanent home. if (!empty($form_state['values']['replace_keep_original_filename']) && $form_state['values']['replace_keep_original_filename']) { $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($file->uri); } else { $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri); } $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME; if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) { // @todo Add watchdog() about replaced file here? // Remove temporary file. file_delete($replacement); // Update if the uri target has changed. if ($new_file_uri != $file->uri) { // Store the original file uri to delete if save is successful. $orphaned_uri = $file->uri; // Update file entity uri. $file->uri = $new_file_uri; } } } // Run entity form submit handling and save the file. entity_form_submit_build_entity('file', $file, $form, $form_state); // A user might assign the associated user by entering a user name in the file // edit form, which we then need to translate to a user ID. if (isset($file->name)) { // The use of isset() is mandatory in the context of user IDs, because // user ID 0 denotes the anonymous user. if ($user = user_load_by_name($file->name)) { $file->uid = $user->uid; } else { // Anonymous user. $file->uid = 0; } } elseif ($file->uid) { $user = user_load($file->uid); $file->name = $user->name; } if (file_uri_scheme($file->uri) != $form_state['values']['scheme']) { $file_destination = $form_state['values']['scheme'] . '://' . file_uri_target($file->uri); $file_destination = file_stream_wrapper_uri_normalize($file_destination); $file_destination_dirname = drupal_dirname($file_destination); // Create the directory in case it doesn't exist. file_prepare_directory($file_destination_dirname, FILE_CREATE_DIRECTORY); if ($moved_file = file_move($file, $file_destination, FILE_EXISTS_RENAME)) { // Only re-assign the file object if file_move() did not fail. $file = $moved_file; } } file_save($file); $args = array( '@type' => file_entity_type_get_name($file), '%title' => entity_label('file', $file), ); watchdog('file', '@type: updated %title.', $args); drupal_set_message(t('@type %title has been updated.', $args)); // Clean up orphaned file. if (!empty($orphaned_uri)) { file_unmanaged_delete($orphaned_uri); $args['@orphaned'] = file_uri_target($orphaned_uri); watchdog('file', '@type: deleted orphaned file @orphaned for %title.', $args); drupal_set_message(t('The replaced @type @orphaned has been deleted.', $args)); } $form_state['redirect'] = 'file/' . $file->fid; // Clear the page and block caches. cache_clear_all(); } /** * Form submission handler for the 'Delete' button for file_entity_edit(). */ function file_entity_edit_delete_submit($form, &$form_state) { $fid = $form_state['values']['fid']; $destination = array(); if (isset($_GET['destination'])) { $destination = drupal_get_destination(); unset($_GET['destination']); } $form_state['redirect'] = array('file/' . $fid . '/delete', array('query' => $destination)); // Clear the page and block caches. cache_clear_all(); } /** * Page callback: Form constructor for the file deletion confirmation form. * * Path: file/%file/delete * * @param object $file * A file object from file_load(). * * @see file_entity_menu() */ function file_entity_delete_form($form, &$form_state, $file) { $form_state['file'] = $file; $form['fid'] = array( '#type' => 'value', '#value' => $file->fid, ); $description = t('This action cannot be undone.'); if ($references = file_usage_list($file)) { $description .= ' ' . t('This file is currently in use and may cause problems if deleted.'); } return confirm_form($form, t('Are you sure you want to delete the file %title?', array( '%title' => entity_label('file', $file), )), 'file/' . $file->fid, $description, t('Delete') ); } /** * Form submission handler for file_entity_delete_form(). */ function file_entity_delete_form_submit($form, &$form_state) { if ($form_state['values']['confirm'] && $file = file_load($form_state['values']['fid'])) { // Use file_delete_multiple() rather than file_delete() since we want to // avoid unwanted validation and usage checking. file_delete_multiple(array($file->fid)); $args = array( '@type' => file_entity_type_get_name($file), '%title' => entity_label('file', $file), ); watchdog('file', '@type: deleted %title.', $args); drupal_set_message(t('@type %title has been deleted.', $args)); } $form_state['redirect'] = ''; // Clear the page and block caches. cache_clear_all(); } /** * Form constructor for file deletion confirmation form. * * @param array $files * An array of file objects. */ function file_entity_multiple_delete_form($form, &$form_state, array $files) { $form['files'] = array( '#prefix' => '', '#tree' => TRUE, ); $files_have_usage = FALSE; foreach ($files as $fid => $file) { $title = entity_label('file', $file); $usage = file_usage_list($file); if (!empty($usage)) { $files_have_usage = TRUE; $title = t('@title (in use)', array('@title' => $title)); } else { $title = check_plain($title); } $form['files'][$fid] = array( '#type' => 'hidden', '#value' => $fid, '#prefix' => '
  • ', '#suffix' => $title . "
  • \n", ); } $form['operation'] = array( '#type' => 'hidden', '#value' => 'delete', ); $description = t('This action cannot be undone.'); if ($files_have_usage) { $description .= ' ' . t('Some of the files are currently in use and may cause problems if deleted.'); } return confirm_form( $form, format_plural(count($files), 'Are you sure you want to delete this file?', 'Are you sure you want to delete these files?'), 'admin/content/file', $description, t('Delete') ); } /** * Form submission handler for file_entity_multiple_delete_form(). */ function file_entity_multiple_delete_form_submit($form, &$form_state) { if ($form_state['values']['confirm'] && $fids = array_keys($form_state['values']['files'])) { file_delete_multiple($fids); $count = count($fids); watchdog('file', 'Deleted @count files.', array('@count' => $count)); drupal_set_message(format_plural($count, 'Deleted one file.', 'Deleted @count files.')); } $form_state['redirect'] = 'admin/content/file'; // Clear the page and block caches. cache_clear_all(); } /** * Page callback for the file edit form. * * @deprecated * Use drupal_get_form('file_entity_edit') */ function file_entity_page_edit($file) { return drupal_get_form('file_entity_edit', $file); } /** * Page callback for the file deletion confirmation form. * * @deprecated * Use drupal_get_form('file_entity_delete_form') */ function file_entity_page_delete($file) { return drupal_get_form('file_entity_delete_form'); } /** * Retrieves the upload validators for a file. * * @param array $options * (optional) An array of options for file validation. * * @return array * An array suitable for passing to file_save_upload() or for a managed_file * or upload element's '#upload_validators' property. */ function file_entity_get_upload_validators(array $options = array()) { // Set up file upload validators. $validators = array(); // Validate file extensions. If there are no file extensions in $params and // there are no Media defaults, there is no file extension validation. if (!empty($options['file_extensions'])) { $validators['file_validate_extensions'] = array($options['file_extensions']); } else { $validators['file_validate_extensions'] = array(variable_get('file_entity_default_allowed_extensions', 'jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp mp3 mov mp4 m4a m4v mpeg avi ogg oga ogv weba webp webm')); } // Cap the upload size according to the system or user defined limit. $max_filesize = parse_size(file_upload_max_size()); $file_entity_max_filesize = parse_size(variable_get('file_entity_max_filesize', '')); // If the user defined a size limit, use the smaller of the two. if (!empty($file_entity_max_filesize)) { $max_filesize = min($max_filesize, $file_entity_max_filesize); } if (!empty($options['max_filesize']) && $options['max_filesize'] < $max_filesize) { $max_filesize = parse_size($options['max_filesize']); } // There is always a file size limit due to the PHP server limit. $validators['file_validate_size'] = array($max_filesize); // Add image validators. $options += array('min_resolution' => 0, 'max_resolution' => 0); if ($options['min_resolution'] || $options['max_resolution']) { $validators['file_validate_image_resolution'] = array($options['max_resolution'], $options['min_resolution']); } // Add other custom upload validators from options. if (!empty($options['upload_validators'])) { $validators += $options['upload_validators']; } return $validators; } function file_entity_upload_archive_form($form, &$form_state) { $options = array( 'file_extensions' => archiver_get_extensions(), ); $form['upload'] = array( '#type' => 'managed_file', '#title' => t('Upload an archive file'), '#upload_location' => NULL, // Upload to the temporary directory. '#upload_validators' => file_entity_get_upload_validators($options), '#progress_indicator' => 'bar', '#required' => TRUE, '#pre_render' => array('file_managed_file_pre_render', 'file_entity_upload_validators_pre_render'), ); $form['pattern'] = array( '#type' => 'textfield', '#title' => t('Pattern'), '#description' => t('Only files matching this pattern will be imported. For example, to import all jpg and gif files, the pattern would be *.jpg|*.gif. Use .* to extract all files in the archive.'), '#default_value' => '.*', '#required' => TRUE, ); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); form_load_include($form_state, 'inc', 'file_entity', 'file_entity.pages'); return $form; } /** * Upload a file. */ function file_entity_upload_archive_form_submit($form, &$form_state) { $form_state['files'] = array(); if ($archive = file_load($form_state['values']['upload'])) { if ($archiver = archiver_get_archiver($archive->uri)) { $files = $archiver->listContents(); $destination = pathinfo($archive->filename, PATHINFO_FILENAME); // Prepare a temporary directory to extract the archive to before saving // it is content. $extract_location = 'temporary://' . $destination; $extract_location = file_destination($extract_location, FILE_EXISTS_RENAME); if (!file_prepare_directory($extract_location, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) { throw new Exception(t('Unable to prepar, a temporary directory %dir for extraction.', array('%dir' => $extract_location))); } // Prepare target directory where files are going to be saved. $target_dir = file_default_scheme() . '://' . $destination; $target_dir = file_destination($target_dir, FILE_EXISTS_RENAME); if (!file_prepare_directory($target_dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) { throw new Exception(t('Unable to prepar, a directory %dir for extraction.', array('%dir' => $target_dir))); } $archiver->extract($extract_location); $pattern = '/' . $form_state['values']['pattern'] . '/'; if ($extracted_files = file_scan_directory($extract_location, $pattern)) { foreach ($extracted_files as $extracted_file) { $file = new stdClass(); $file->fid = NULL; $file->uid = $archive->uid; $file->filename = $extracted_file->filename; $file->origname = $extracted_file->filename; $file->status = FILE_STATUS_PERMANENT; $file->filemime = file_get_mimetype($extracted_file->filename); // destination uri should match the current file directory hierarchy. $file->uri = $target_dir . str_replace($extract_location, '', $extracted_file->uri); // Rename potentially executable files, to help prevent exploits (i.e. will // rename filename.php.foo and filename.php to filename.php.foo.txt and // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads' // evaluates to TRUE. if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { $file->filemime = 'text/plain'; $file->uri .= '.txt'; $file->filename .= '.txt'; } // prepare destination path for the extracted file while keeping the // directory hierarchy of the file. $destination = pathinfo($file->uri, PATHINFO_DIRNAME); if (!file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) { throw new Exception(t('Unable to prepar, a directory %dir for extraction.', array('%dir' => $destination))); } if (!file_unmanaged_move($extracted_file->uri, $file->uri)) { throw new Exception(t('Could not move uploaded file %file to destination %destination.', array('%file' => $extracted_file->filename, '%destination' => $file->uri))); return FALSE; } file_save($file); $form_state['files'][$file->fid] = $file; } } // Delete extract location file_unmanaged_delete_recursive($extract_location); drupal_set_message(t('Extracted %file and added @count new files.', array('%file' => $archive->filename, '@count' => count($files)))); } else { throw new Exception(t('Cannot extract %file, not a valid archive.', array('%file' => $archive->uri))); } } // Redirect to the file edit page. if (file_entity_access('edit') && module_exists('multiform')) { $destination = array('destination' => 'admin/content/file'); if (isset($_GET['destination'])) { $destination = drupal_get_destination(); unset($_GET['destination']); } $form_state['redirect'] = array('admin/content/file/edit-multiple/' . implode(' ', array_keys($form_state['files'])), array('query' => $destination)); } else { $form_state['redirect'] = 'admin/content/file'; } }