summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/file
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/file')
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.api.php60
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.css35
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.field.inc1024
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.install98
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.js155
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/file.module1013
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/application-octet-stream.pngbin0 -> 189 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/application-pdf.pngbin0 -> 346 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/application-x-executable.pngbin0 -> 189 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/audio-x-generic.pngbin0 -> 314 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/image-x-generic.pngbin0 -> 385 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/package-x-generic.pngbin0 -> 260 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/text-html.pngbin0 -> 265 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/text-plain.pngbin0 -> 220 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/text-x-generic.pngbin0 -> 220 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/text-x-script.pngbin0 -> 276 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/video-x-generic.pngbin0 -> 214 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/x-office-document.pngbin0 -> 196 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/x-office-presentation.pngbin0 -> 181 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/icons/x-office-spreadsheet.pngbin0 -> 183 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/tests/file.test1171
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.module69
24 files changed, 3650 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.api.php b/kolab.org/www/drupal-7.26/modules/file/file.api.php
new file mode 100644
index 0000000..df178c6
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.api.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Hooks for file module.
+ */
+
+/**
+ * Control download access to files.
+ *
+ * The hook is typically implemented to limit access based on the entity the
+ * file is referenced, e.g., only users with access to a node should be allowed
+ * to download files attached to that node.
+ *
+ * @param array $file_item
+ * The array of information about the file to check access for.
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The $entity to which $file is referenced.
+ *
+ * @return
+ * TRUE is access should be allowed by this entity or FALSE if denied. Note
+ * that denial may be overridden by another entity controller, making this
+ * grant permissive rather than restrictive.
+ *
+ * @see hook_field_access().
+ */
+function hook_file_download_access($file_item, $entity_type, $entity) {
+ if ($entity_type == 'node') {
+ return node_access('view', $entity);
+ }
+}
+
+/**
+ * Alter the access rules applied to a file download.
+ *
+ * Entities that implement file management set the access rules for their
+ * individual files. Module may use this hook to create custom access rules
+ * for file downloads.
+ *
+ * @see hook_file_download_access().
+ *
+ * @param $grants
+ * An array of grants gathered by hook_file_download_access(). The array is
+ * keyed by the module that defines the entity type's access control; the
+ * values are Boolean grant responses for each module.
+ * @param array $file_item
+ * The array of information about the file to alter access for.
+ * @param $entity_type
+ * The type of $entity; for example, 'node' or 'user'.
+ * @param $entity
+ * The $entity to which $file is referenced.
+ */
+function hook_file_download_access_alter(&$grants, $file_item, $entity_type, $entity) {
+ // For our example module, we always enforce the rules set by node module.
+ if (isset($grants['node'])) {
+ $grants = array('node' => $grants['node']);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.css b/kolab.org/www/drupal-7.26/modules/file/file.css
new file mode 100644
index 0000000..bd4a059
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.css
@@ -0,0 +1,35 @@
+/**
+ * @file
+ * Admin stylesheet for file module.
+ */
+
+/**
+ * Managed file element styles.
+ */
+.form-managed-file .form-file,
+.form-managed-file .form-submit {
+ margin: 0;
+}
+
+.form-managed-file input.progress-disabled {
+ float: none;
+ display: inline;
+}
+
+.form-managed-file div.ajax-progress,
+.form-managed-file div.throbber {
+ display: inline;
+ float: none;
+ padding: 1px 5px 2px 5px;
+}
+
+.form-managed-file div.ajax-progress-bar {
+ display: none;
+ margin-top: 4px;
+ width: 28em;
+ padding: 0;
+}
+
+.form-managed-file div.ajax-progress-bar div.bar {
+ margin: 0;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.field.inc b/kolab.org/www/drupal-7.26/modules/file/file.field.inc
new file mode 100644
index 0000000..d540c0a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.field.inc
@@ -0,0 +1,1024 @@
+<?php
+
+/**
+ * @file
+ * Field module functionality for the File module.
+ */
+
+/**
+ * Implements hook_field_info().
+ */
+function file_field_info() {
+ return array(
+ 'file' => array(
+ 'label' => t('File'),
+ 'description' => t('This field stores the ID of a file as an integer value.'),
+ 'settings' => array(
+ 'display_field' => 0,
+ 'display_default' => 0,
+ 'uri_scheme' => variable_get('file_default_scheme', 'public'),
+ ),
+ 'instance_settings' => array(
+ 'file_extensions' => 'txt',
+ 'file_directory' => '',
+ 'max_filesize' => '',
+ 'description_field' => 0,
+ ),
+ 'default_widget' => 'file_generic',
+ 'default_formatter' => 'file_default',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function file_field_settings_form($field, $instance, $has_data) {
+ $defaults = field_info_field_settings($field['type']);
+ $settings = array_merge($defaults, $field['settings']);
+
+ $form['#attached']['js'][] = drupal_get_path('module', 'file') . '/file.js';
+
+ $form['display_field'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable <em>Display</em> field'),
+ '#default_value' => $settings['display_field'],
+ '#description' => t('The display option allows users to choose if a file should be shown when viewing the content.'),
+ );
+ $form['display_default'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Files displayed by default'),
+ '#default_value' => $settings['display_default'],
+ '#description' => t('This setting only has an effect if the display option is enabled.'),
+ );
+
+ $scheme_options = array();
+ foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
+ $scheme_options[$scheme] = $stream_wrapper['name'];
+ }
+ $form['uri_scheme'] = array(
+ '#type' => 'radios',
+ '#title' => t('Upload destination'),
+ '#options' => $scheme_options,
+ '#default_value' => $settings['uri_scheme'],
+ '#description' => t('Select where the final files should be stored. Private file storage has significantly more overhead than public files, but allows restricted access to files within this field.'),
+ '#disabled' => $has_data,
+ );
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function file_field_instance_settings_form($field, $instance) {
+ $settings = $instance['settings'];
+
+ $form['file_directory'] = array(
+ '#type' => 'textfield',
+ '#title' => t('File directory'),
+ '#default_value' => $settings['file_directory'],
+ '#description' => t('Optional subdirectory within the upload destination where files will be stored. Do not include preceding or trailing slashes.'),
+ '#element_validate' => array('_file_generic_settings_file_directory_validate'),
+ '#weight' => 3,
+ );
+
+ // Make the extension list a little more human-friendly by comma-separation.
+ $extensions = str_replace(' ', ', ', $settings['file_extensions']);
+ $form['file_extensions'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Allowed file extensions'),
+ '#default_value' => $extensions,
+ '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
+ '#element_validate' => array('_file_generic_settings_extensions'),
+ '#weight' => 1,
+ // By making this field required, we prevent a potential security issue
+ // that would allow files of any type to be uploaded.
+ '#required' => TRUE,
+ );
+
+ $form['max_filesize'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum upload size'),
+ '#default_value' => $settings['max_filesize'],
+ '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
+ '#size' => 10,
+ '#element_validate' => array('_file_generic_settings_max_filesize'),
+ '#weight' => 5,
+ );
+
+ $form['description_field'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable <em>Description</em> field'),
+ '#default_value' => isset($settings['description_field']) ? $settings['description_field'] : '',
+ '#description' => t('The description field allows users to enter a description about the uploaded file.'),
+ '#parents' => array('instance', 'settings', 'description_field'),
+ '#weight' => 11,
+ );
+
+ return $form;
+}
+
+/**
+ * Element validate callback for the maximum upload size field.
+ *
+ * Ensure a size that can be parsed by parse_size() has been entered.
+ */
+function _file_generic_settings_max_filesize($element, &$form_state) {
+ if (!empty($element['#value']) && !is_numeric(parse_size($element['#value']))) {
+ form_error($element, t('The "!name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', array('!name' => t($element['title']))));
+ }
+}
+
+/**
+ * Element validate callback for the allowed file extensions field.
+ *
+ * This doubles as a convenience clean-up function and a validation routine.
+ * Commas are allowed by the end-user, but ultimately the value will be stored
+ * as a space-separated list for compatibility with file_validate_extensions().
+ */
+function _file_generic_settings_extensions($element, &$form_state) {
+ if (!empty($element['#value'])) {
+ $extensions = preg_replace('/([, ]+\.?)/', ' ', trim(strtolower($element['#value'])));
+ $extensions = array_filter(explode(' ', $extensions));
+ $extensions = implode(' ', array_unique($extensions));
+ if (!preg_match('/^([a-z0-9]+([.][a-z0-9])* ?)+$/', $extensions)) {
+ form_error($element, t('The list of allowed extensions is not valid, be sure to exclude leading dots and to separate extensions with a comma or space.'));
+ }
+ else {
+ form_set_value($element, $extensions, $form_state);
+ }
+ }
+}
+
+/**
+ * Element validate callback for the file destination field.
+ *
+ * Remove slashes from the beginning and end of the destination value and ensure
+ * that the file directory path is not included at the beginning of the value.
+ */
+function _file_generic_settings_file_directory_validate($element, &$form_state) {
+ // Strip slashes from the beginning and end of $widget['file_directory'].
+ $value = trim($element['#value'], '\\/');
+ form_set_value($element, $value, $form_state);
+}
+
+/**
+ * Implements hook_field_load().
+ */
+function file_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
+
+ $fids = array();
+ foreach ($entities as $id => $entity) {
+ // Load the files from the files table.
+ foreach ($items[$id] as $delta => $item) {
+ if (!empty($item['fid'])) {
+ $fids[] = $item['fid'];
+ }
+ }
+ }
+ $files = file_load_multiple($fids);
+
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ // If the file does not exist, mark the entire item as empty.
+ if (empty($item['fid']) || !isset($files[$item['fid']])) {
+ $items[$id][$delta] = NULL;
+ }
+ else {
+ $items[$id][$delta] = array_merge((array) $files[$item['fid']], $item);
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_prepare_view().
+ */
+function file_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
+ // Remove files specified to not be displayed.
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ if (!file_field_displayed($item, $field)) {
+ unset($items[$id][$delta]);
+ }
+ }
+ // Ensure consecutive deltas.
+ $items[$id] = array_values($items[$id]);
+ }
+}
+
+/**
+ * Implements hook_field_presave().
+ */
+function file_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ // Make sure that each file which will be saved with this object has a
+ // permanent status, so that it will not be removed when temporary files are
+ // cleaned up.
+ foreach ($items as $delta => $item) {
+ if (empty($item['fid'])) {
+ unset($items[$delta]);
+ continue;
+ }
+ $file = file_load($item['fid']);
+ if (empty($file)) {
+ unset($items[$delta]);
+ continue;
+ }
+ if (!$file->status) {
+ $file->status = FILE_STATUS_PERMANENT;
+ file_save($file);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_insert().
+ */
+function file_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Add a new usage of each uploaded file.
+ foreach ($items as $item) {
+ $file = (object) $item;
+ file_usage_add($file, 'file', $entity_type, $id);
+ }
+}
+
+/**
+ * Implements hook_field_update().
+ *
+ * Checks for files that have been removed from the object.
+ */
+function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // On new revisions, all files are considered to be a new usage and no
+ // deletion of previous file usages are necessary.
+ if (!empty($entity->revision)) {
+ foreach ($items as $item) {
+ $file = (object) $item;
+ file_usage_add($file, 'file', $entity_type, $id);
+ }
+ return;
+ }
+
+ // Build a display of the current FIDs.
+ $current_fids = array();
+ foreach ($items as $item) {
+ $current_fids[] = $item['fid'];
+ }
+
+ // Compare the original field values with the ones that are being saved. Use
+ // $entity->original to check this when possible, but if it isn't available,
+ // create a bare-bones entity and load its previous values instead.
+ if (isset($entity->original)) {
+ $original = $entity->original;
+ }
+ else {
+ $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
+ field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+ }
+ $original_fids = array();
+ if (!empty($original->{$field['field_name']}[$langcode])) {
+ foreach ($original->{$field['field_name']}[$langcode] as $original_item) {
+ $original_fids[] = $original_item['fid'];
+ if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
+ // Decrement the file usage count by 1 and delete the file if possible.
+ file_field_delete_file($original_item, $field, $entity_type, $id);
+ }
+ }
+ }
+
+ // Add new usage entries for newly added files.
+ foreach ($items as $item) {
+ if (!in_array($item['fid'], $original_fids)) {
+ $file = (object) $item;
+ file_usage_add($file, 'file', $entity_type, $id);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_delete().
+ */
+function file_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Delete all file usages within this entity.
+ foreach ($items as $delta => $item) {
+ file_field_delete_file($item, $field, $entity_type, $id, 0);
+ }
+}
+
+/**
+ * Implements hook_field_delete_revision().
+ */
+function file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ // Decrement the file usage count by 1 and delete the file if possible.
+ if (file_field_delete_file($item, $field, $entity_type, $id)) {
+ $items[$delta] = NULL;
+ }
+ }
+}
+
+/**
+ * Decrements the usage count for a file and attempts to delete it.
+ *
+ * This function only has an effect if the file being deleted is used only by
+ * File module.
+ *
+ * @param $item
+ * The field item that contains a file array.
+ * @param $field
+ * The field structure for the operation.
+ * @param $entity_type
+ * The type of $entity.
+ * @param $id
+ * The entity ID which contains the file being deleted.
+ * @param $count
+ * (optional) The number of references to decrement from the object
+ * containing the file. Defaults to 1.
+ *
+ * @return
+ * Boolean TRUE if the file was deleted, or an array of remaining references
+ * if the file is still in use by other modules. Boolean FALSE if an error
+ * was encountered.
+ */
+function file_field_delete_file($item, $field, $entity_type, $id, $count = 1) {
+ // To prevent the file field from deleting files it doesn't know about, check
+ // the file reference count. Temporary files can be deleted because they
+ // are not yet associated with any content at all.
+ $file = (object) $item;
+ $file_usage = file_usage_list($file);
+ if ($file->status == 0 || !empty($file_usage['file'])) {
+ file_usage_delete($file, 'file', $entity_type, $id, $count);
+ return file_delete($file);
+ }
+
+ // Even if the file is not deleted, return TRUE to indicate the file field
+ // record can be removed from the field database tables.
+ return TRUE;
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function file_field_is_empty($item, $field) {
+ return empty($item['fid']);
+}
+
+/**
+ * Determines whether a file should be displayed when outputting field content.
+ *
+ * @param $item
+ * A field item array.
+ * @param $field
+ * A field array.
+ *
+ * @return
+ * Boolean TRUE if the file will be displayed, FALSE if the file is hidden.
+ */
+function file_field_displayed($item, $field) {
+ if (!empty($field['settings']['display_field'])) {
+ return (bool) $item['display'];
+ }
+ return TRUE;
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function file_field_formatter_info() {
+ return array(
+ 'file_default' => array(
+ 'label' => t('Generic file'),
+ 'field types' => array('file'),
+ ),
+ 'file_table' => array(
+ 'label' => t('Table of files'),
+ 'field types' => array('file'),
+ ),
+ 'file_url_plain' => array(
+ 'label' => t('URL to file'),
+ 'field types' => array('file'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function file_field_widget_info() {
+ return array(
+ 'file_generic' => array(
+ 'label' => t('File'),
+ 'field types' => array('file'),
+ 'settings' => array(
+ 'progress_indicator' => 'throbber',
+ ),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ 'default value' => FIELD_BEHAVIOR_NONE,
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function file_field_widget_settings_form($field, $instance) {
+ $widget = $instance['widget'];
+ $settings = $widget['settings'];
+
+ $form['progress_indicator'] = array(
+ '#type' => 'radios',
+ '#title' => t('Progress indicator'),
+ '#options' => array(
+ 'throbber' => t('Throbber'),
+ 'bar' => t('Bar with progress meter'),
+ ),
+ '#default_value' => $settings['progress_indicator'],
+ '#description' => t('The throbber display does not show the status of uploads but takes up less space. The progress bar is helpful for monitoring progress on large uploads.'),
+ '#weight' => 16,
+ '#access' => file_progress_implementation(),
+ );
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function file_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+
+ $defaults = array(
+ 'fid' => 0,
+ 'display' => !empty($field['settings']['display_default']),
+ 'description' => '',
+ );
+
+ // Load the items for form rebuilds from the field state as they might not be
+ // in $form_state['values'] because of validation limitations. Also, they are
+ // only passed in as $items when editing existing entities.
+ $field_state = field_form_get_state($element['#field_parents'], $field['field_name'], $langcode, $form_state);
+ if (isset($field_state['items'])) {
+ $items = $field_state['items'];
+ }
+
+ // Essentially we use the managed_file type, extended with some enhancements.
+ $element_info = element_info('managed_file');
+ $element += array(
+ '#type' => 'managed_file',
+ '#upload_location' => file_field_widget_uri($field, $instance),
+ '#upload_validators' => file_field_widget_upload_validators($field, $instance),
+ '#value_callback' => 'file_field_widget_value',
+ '#process' => array_merge($element_info['#process'], array('file_field_widget_process')),
+ '#progress_indicator' => $instance['widget']['settings']['progress_indicator'],
+ // Allows this field to return an array instead of a single value.
+ '#extended' => TRUE,
+ );
+
+ if ($field['cardinality'] == 1) {
+ // Set the default value.
+ $element['#default_value'] = !empty($items) ? $items[0] : $defaults;
+ // If there's only one field, return it as delta 0.
+ if (empty($element['#default_value']['fid'])) {
+ $element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators']));
+ }
+ $elements = array($element);
+ }
+ else {
+ // If there are multiple values, add an element for each existing one.
+ foreach ($items as $item) {
+ $elements[$delta] = $element;
+ $elements[$delta]['#default_value'] = $item;
+ $elements[$delta]['#weight'] = $delta;
+ $delta++;
+ }
+ // And then add one more empty row for new uploads except when this is a
+ // programmed form as it is not necessary.
+ if (($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) && empty($form_state['programmed'])) {
+ $elements[$delta] = $element;
+ $elements[$delta]['#default_value'] = $defaults;
+ $elements[$delta]['#weight'] = $delta;
+ $elements[$delta]['#required'] = ($element['#required'] && $delta == 0);
+ }
+ // The group of elements all-together need some extra functionality
+ // after building up the full list (like draggable table rows).
+ $elements['#file_upload_delta'] = $delta;
+ $elements['#theme'] = 'file_widget_multiple';
+ $elements['#theme_wrappers'] = array('fieldset');
+ $elements['#process'] = array('file_field_widget_process_multiple');
+ $elements['#title'] = $element['#title'];
+ $elements['#description'] = $element['#description'];
+ $elements['#field_name'] = $element['#field_name'];
+ $elements['#language'] = $element['#language'];
+ $elements['#display_field'] = $field['settings']['display_field'];
+
+ // Add some properties that will eventually be added to the file upload
+ // field. These are added here so that they may be referenced easily through
+ // a hook_form_alter().
+ $elements['#file_upload_title'] = t('Add a new file');
+ $elements['#file_upload_description'] = theme('file_upload_help', array('description' => '', 'upload_validators' => $elements[0]['#upload_validators']));
+ }
+
+ return $elements;
+}
+
+/**
+ * Retrieves the upload validators for a file field.
+ *
+ * @param $field
+ * A field array.
+ *
+ * @return
+ * An array suitable for passing to file_save_upload() or the file field
+ * element's '#upload_validators' property.
+ */
+function file_field_widget_upload_validators($field, $instance) {
+ // Cap the upload size according to the PHP limit.
+ $max_filesize = parse_size(file_upload_max_size());
+ if (!empty($instance['settings']['max_filesize']) && parse_size($instance['settings']['max_filesize']) < $max_filesize) {
+ $max_filesize = parse_size($instance['settings']['max_filesize']);
+ }
+
+ $validators = array();
+
+ // There is always a file size limit due to the PHP server limit.
+ $validators['file_validate_size'] = array($max_filesize);
+
+ // Add the extension check if necessary.
+ if (!empty($instance['settings']['file_extensions'])) {
+ $validators['file_validate_extensions'] = array($instance['settings']['file_extensions']);
+ }
+
+ return $validators;
+}
+
+/**
+ * Determines the URI for a file field instance.
+ *
+ * @param $field
+ * A field array.
+ * @param $instance
+ * A field instance array.
+ * @param $data
+ * An array of token objects to pass to token_replace().
+ *
+ * @return
+ * A file directory URI with tokens replaced.
+ *
+ * @see token_replace()
+ */
+function file_field_widget_uri($field, $instance, $data = array()) {
+ $destination = trim($instance['settings']['file_directory'], '/');
+
+ // Replace tokens.
+ $destination = token_replace($destination, $data);
+
+ return $field['settings']['uri_scheme'] . '://' . $destination;
+}
+
+/**
+ * The #value_callback for the file_generic field element.
+ */
+function file_field_widget_value($element, $input = FALSE, $form_state) {
+ if ($input) {
+ // Checkboxes lose their value when empty.
+ // If the display field is present make sure its unchecked value is saved.
+ $field = field_widget_field($element, $form_state);
+ if (empty($input['display'])) {
+ $input['display'] = $field['settings']['display_field'] ? 0 : 1;
+ }
+ }
+
+ // We depend on the managed file element to handle uploads.
+ $return = file_managed_file_value($element, $input, $form_state);
+
+ // Ensure that all the required properties are returned even if empty.
+ $return += array(
+ 'fid' => 0,
+ 'display' => 1,
+ 'description' => '',
+ );
+
+ return $return;
+}
+
+/**
+ * An element #process callback for the file_generic field type.
+ *
+ * Expands the file_generic type to include the description and display fields.
+ */
+function file_field_widget_process($element, &$form_state, $form) {
+ $item = $element['#value'];
+ $item['fid'] = $element['fid']['#value'];
+
+ $field = field_widget_field($element, $form_state);
+ $instance = field_widget_instance($element, $form_state);
+ $settings = $instance['widget']['settings'];
+
+ $element['#theme'] = 'file_widget';
+
+ // Add the display field if enabled.
+ if (!empty($field['settings']['display_field']) && $item['fid']) {
+ $element['display'] = array(
+ '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
+ '#title' => t('Include file in display'),
+ '#value' => isset($item['display']) ? $item['display'] : $field['settings']['display_default'],
+ '#attributes' => array('class' => array('file-display')),
+ );
+ }
+ else {
+ $element['display'] = array(
+ '#type' => 'hidden',
+ '#value' => '1',
+ );
+ }
+
+ // Add the description field if enabled.
+ if (!empty($instance['settings']['description_field']) && $item['fid']) {
+ $element['description'] = array(
+ '#type' => variable_get('file_description_type', 'textfield'),
+ '#title' => t('Description'),
+ '#value' => isset($item['description']) ? $item['description'] : '',
+ '#maxlength' => variable_get('file_description_length', 128),
+ '#description' => t('The description may be used as the label of the link to the file.'),
+ );
+ }
+
+ // Adjust the Ajax settings so that on upload and remove of any individual
+ // file, the entire group of file fields is updated together.
+ if ($field['cardinality'] != 1) {
+ $parents = array_slice($element['#array_parents'], 0, -1);
+ $new_path = 'file/ajax/' . implode('/', $parents) . '/' . $form['form_build_id']['#value'];
+ $field_element = drupal_array_get_nested_value($form, $parents);
+ $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
+ foreach (element_children($element) as $key) {
+ if (isset($element[$key]['#ajax'])) {
+ $element[$key]['#ajax']['path'] = $new_path;
+ $element[$key]['#ajax']['wrapper'] = $new_wrapper;
+ }
+ }
+ unset($element['#prefix'], $element['#suffix']);
+ }
+
+ // Add another submit handler to the upload and remove buttons, to implement
+ // functionality needed by the field widget. This submit handler, along with
+ // the rebuild logic in file_field_widget_form() requires the entire field,
+ // not just the individual item, to be valid.
+ foreach (array('upload_button', 'remove_button') as $key) {
+ $element[$key]['#submit'][] = 'file_field_widget_submit';
+ $element[$key]['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1));
+ }
+
+ return $element;
+}
+
+/**
+ * An element #process callback for a group of file_generic fields.
+ *
+ * Adds the weight field to each row so it can be ordered and adds a new Ajax
+ * wrapper around the entire group so it can be replaced all at once.
+ */
+function file_field_widget_process_multiple($element, &$form_state, $form) {
+ $element_children = element_children($element, TRUE);
+ $count = count($element_children);
+
+ foreach ($element_children as $delta => $key) {
+ if ($key != $element['#file_upload_delta']) {
+ $description = _file_field_get_description_from_element($element[$key]);
+ $element[$key]['_weight'] = array(
+ '#type' => 'weight',
+ '#title' => $description ? t('Weight for @title', array('@title' => $description)) : t('Weight for new file'),
+ '#title_display' => 'invisible',
+ '#delta' => $count,
+ '#default_value' => $delta,
+ );
+ }
+ else {
+ // The title needs to be assigned to the upload field so that validation
+ // errors include the correct widget label.
+ $element[$key]['#title'] = $element['#title'];
+ $element[$key]['_weight'] = array(
+ '#type' => 'hidden',
+ '#default_value' => $delta,
+ );
+ }
+ }
+
+ // Add a new wrapper around all the elements for Ajax replacement.
+ $element['#prefix'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
+ $element['#suffix'] = '</div>';
+
+ return $element;
+}
+
+/**
+ * Retrieves the file description from a field field element.
+ *
+ * This helper function is used by file_field_widget_process_multiple().
+ *
+ * @param $element
+ * The element being processed.
+ *
+ * @return
+ * A description of the file suitable for use in the administrative interface.
+ */
+function _file_field_get_description_from_element($element) {
+ // Use the actual file description, if it's available.
+ if (!empty($element['#default_value']['description'])) {
+ return $element['#default_value']['description'];
+ }
+ // Otherwise, fall back to the filename.
+ if (!empty($element['#default_value']['filename'])) {
+ return $element['#default_value']['filename'];
+ }
+ // This is probably a newly uploaded file; no description is available.
+ return FALSE;
+}
+
+/**
+ * Form submission handler for upload/remove button of file_field_widget_form().
+ *
+ * This runs in addition to and after file_managed_file_submit().
+ *
+ * @see file_managed_file_submit()
+ * @see file_field_widget_form()
+ * @see file_field_widget_process()
+ */
+function file_field_widget_submit($form, &$form_state) {
+ // During the form rebuild, file_field_widget_form() will create field item
+ // widget elements using re-indexed deltas, so clear out $form_state['input']
+ // to avoid a mismatch between old and new deltas. The rebuilt elements will
+ // have #default_value set appropriately for the current state of the field,
+ // so nothing is lost in doing this.
+ $parents = array_slice($form_state['triggering_element']['#parents'], 0, -2);
+ drupal_array_set_nested_value($form_state['input'], $parents, NULL);
+
+ $button = $form_state['triggering_element'];
+
+ // Go one level up in the form, to the widgets container.
+ $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+ $field_name = $element['#field_name'];
+ $langcode = $element['#language'];
+ $parents = $element['#field_parents'];
+
+ $submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2));
+ foreach ($submitted_values as $delta => $submitted_value) {
+ if (!$submitted_value['fid']) {
+ unset($submitted_values[$delta]);
+ }
+ }
+
+ // Re-index deltas after removing empty items.
+ $submitted_values = array_values($submitted_values);
+
+ // Update form_state values.
+ drupal_array_set_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values);
+
+ // Update items.
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+ $field_state['items'] = $submitted_values;
+ field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+}
+
+/**
+ * Returns HTML for an individual file upload widget.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - element: A render element representing the widget.
+ *
+ * @ingroup themeable
+ */
+function theme_file_widget($variables) {
+ $element = $variables['element'];
+ $output = '';
+
+ // The "form-managed-file" class is required for proper Ajax functionality.
+ $output .= '<div class="file-widget form-managed-file clearfix">';
+ if ($element['fid']['#value'] != 0) {
+ // Add the file size after the file name.
+ $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> ';
+ }
+ $output .= drupal_render_children($element);
+ $output .= '</div>';
+
+ return $output;
+}
+
+/**
+ * Returns HTML for a group of file upload widgets.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - element: A render element representing the widgets.
+ *
+ * @ingroup themeable
+ */
+function theme_file_widget_multiple($variables) {
+ $element = $variables['element'];
+
+ // Special ID and classes for draggable tables.
+ $weight_class = $element['#id'] . '-weight';
+ $table_id = $element['#id'] . '-table';
+
+ // Build up a table of applicable fields.
+ $headers = array();
+ $headers[] = t('File information');
+ if ($element['#display_field']) {
+ $headers[] = array(
+ 'data' => t('Display'),
+ 'class' => array('checkbox'),
+ );
+ }
+ $headers[] = t('Weight');
+ $headers[] = t('Operations');
+
+ // Get our list of widgets in order (needed when the form comes back after
+ // preview or failed validation).
+ $widgets = array();
+ foreach (element_children($element) as $key) {
+ $widgets[] = &$element[$key];
+ }
+ usort($widgets, '_field_sort_items_value_helper');
+
+ $rows = array();
+ foreach ($widgets as $key => &$widget) {
+ // Save the uploading row for last.
+ if ($widget['#file'] == FALSE) {
+ $widget['#title'] = $element['#file_upload_title'];
+ $widget['#description'] = $element['#file_upload_description'];
+ continue;
+ }
+
+ // Delay rendering of the buttons, so that they can be rendered later in the
+ // "operations" column.
+ $operations_elements = array();
+ foreach (element_children($widget) as $sub_key) {
+ if (isset($widget[$sub_key]['#type']) && $widget[$sub_key]['#type'] == 'submit') {
+ hide($widget[$sub_key]);
+ $operations_elements[] = &$widget[$sub_key];
+ }
+ }
+
+ // Delay rendering of the "Display" option and the weight selector, so that
+ // each can be rendered later in its own column.
+ if ($element['#display_field']) {
+ hide($widget['display']);
+ }
+ hide($widget['_weight']);
+
+ // Render everything else together in a column, without the normal wrappers.
+ $widget['#theme_wrappers'] = array();
+ $information = drupal_render($widget);
+
+ // Render the previously hidden elements, using render() instead of
+ // drupal_render(), to undo the earlier hide().
+ $operations = '';
+ foreach ($operations_elements as $operation_element) {
+ $operations .= render($operation_element);
+ }
+ $display = '';
+ if ($element['#display_field']) {
+ unset($widget['display']['#title']);
+ $display = array(
+ 'data' => render($widget['display']),
+ 'class' => array('checkbox'),
+ );
+ }
+ $widget['_weight']['#attributes']['class'] = array($weight_class);
+ $weight = render($widget['_weight']);
+
+ // Arrange the row with all of the rendered columns.
+ $row = array();
+ $row[] = $information;
+ if ($element['#display_field']) {
+ $row[] = $display;
+ }
+ $row[] = $weight;
+ $row[] = $operations;
+ $rows[] = array(
+ 'data' => $row,
+ 'class' => isset($widget['#attributes']['class']) ? array_merge($widget['#attributes']['class'], array('draggable')) : array('draggable'),
+ );
+ }
+
+ drupal_add_tabledrag($table_id, 'order', 'sibling', $weight_class);
+
+ $output = '';
+ $output = empty($rows) ? '' : theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => $table_id)));
+ $output .= drupal_render_children($element);
+ return $output;
+}
+
+
+/**
+ * Returns HTML for help text based on file upload validators.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - description: The normal description for this field, specified by the
+ * user.
+ * - upload_validators: An array of upload validators as used in
+ * $element['#upload_validators'].
+ *
+ * @ingroup themeable
+ */
+function theme_file_upload_help($variables) {
+ $description = $variables['description'];
+ $upload_validators = $variables['upload_validators'];
+
+ $descriptions = array();
+
+ if (strlen($description)) {
+ $descriptions[] = $description;
+ }
+ if (isset($upload_validators['file_validate_size'])) {
+ $descriptions[] = t('Files must be less than !size.', array('!size' => '<strong>' . format_size($upload_validators['file_validate_size'][0]) . '</strong>'));
+ }
+ if (isset($upload_validators['file_validate_extensions'])) {
+ $descriptions[] = t('Allowed file types: !extensions.', array('!extensions' => '<strong>' . check_plain($upload_validators['file_validate_extensions'][0]) . '</strong>'));
+ }
+ if (isset($upload_validators['file_validate_image_resolution'])) {
+ $max = $upload_validators['file_validate_image_resolution'][0];
+ $min = $upload_validators['file_validate_image_resolution'][1];
+ if ($min && $max && $min == $max) {
+ $descriptions[] = t('Images must be exactly !size pixels.', array('!size' => '<strong>' . $max . '</strong>'));
+ }
+ elseif ($min && $max) {
+ $descriptions[] = t('Images must be between !min and !max pixels.', array('!min' => '<strong>' . $min . '</strong>', '!max' => '<strong>' . $max . '</strong>'));
+ }
+ elseif ($min) {
+ $descriptions[] = t('Images must be larger than !min pixels.', array('!min' => '<strong>' . $min . '</strong>'));
+ }
+ elseif ($max) {
+ $descriptions[] = t('Images must be smaller than !max pixels.', array('!max' => '<strong>' . $max . '</strong>'));
+ }
+ }
+
+ return implode('<br />', $descriptions);
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function file_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+
+ switch ($display['type']) {
+ case 'file_default':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array(
+ '#theme' => 'file_link',
+ '#file' => (object) $item,
+ );
+ }
+ break;
+
+ case 'file_url_plain':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => empty($item['uri']) ? '' : file_create_url($item['uri']));
+ }
+ break;
+
+ case 'file_table':
+ if (!empty($items)) {
+ // Display all values in a single element..
+ $element[0] = array(
+ '#theme' => 'file_formatter_table',
+ '#items' => $items,
+ );
+ }
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * Returns HTML for a file attachments table.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - items: An array of file attachments.
+ *
+ * @ingroup themeable
+ */
+function theme_file_formatter_table($variables) {
+ $header = array(t('Attachment'), t('Size'));
+ $rows = array();
+ foreach ($variables['items'] as $delta => $item) {
+ $rows[] = array(
+ theme('file_link', array('file' => (object) $item)),
+ format_size($item['filesize']),
+ );
+ }
+
+ return empty($rows) ? '' : theme('table', array('header' => $header, 'rows' => $rows));
+}
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.info b/kolab.org/www/drupal-7.26/modules/file/file.info
new file mode 100644
index 0000000..b96e435
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.info
@@ -0,0 +1,13 @@
+name = File
+description = Defines a file field type.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = tests/file.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.install b/kolab.org/www/drupal-7.26/modules/file/file.install
new file mode 100644
index 0000000..47ee4fd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.install
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for File module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function file_field_schema($field) {
+ return array(
+ 'columns' => array(
+ 'fid' => array(
+ 'description' => 'The {file_managed}.fid being referenced in this field.',
+ 'type' => 'int',
+ 'not null' => FALSE,
+ 'unsigned' => TRUE,
+ ),
+ 'display' => array(
+ 'description' => 'Flag to control whether this file should be displayed when viewing content.',
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 1,
+ ),
+ 'description' => array(
+ 'description' => 'A description of the file.',
+ 'type' => 'text',
+ 'not null' => FALSE,
+ ),
+ ),
+ 'indexes' => array(
+ 'fid' => array('fid'),
+ ),
+ 'foreign keys' => array(
+ 'fid' => array(
+ 'table' => 'file_managed',
+ 'columns' => array('fid' => 'fid'),
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements hook_requirements().
+ *
+ * Display information about getting upload progress bars working.
+ */
+function file_requirements($phase) {
+ $requirements = array();
+
+ // Check the server's ability to indicate upload progress.
+ if ($phase == 'runtime') {
+ $implementation = file_progress_implementation();
+ $apache = strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== FALSE;
+ $fastcgi = strpos($_SERVER['SERVER_SOFTWARE'], 'mod_fastcgi') !== FALSE || strpos($_SERVER["SERVER_SOFTWARE"], 'mod_fcgi') !== FALSE;
+ $description = NULL;
+ if (!$apache) {
+ $value = t('Not enabled');
+ $description = t('Your server is not capable of displaying file upload progress. File upload progress requires an Apache server running PHP with mod_php.');
+ $severity = REQUIREMENT_INFO;
+ }
+ elseif ($fastcgi) {
+ $value = t('Not enabled');
+ $description = t('Your server is not capable of displaying file upload progress. File upload progress requires PHP be run with mod_php and not as FastCGI.');
+ $severity = REQUIREMENT_INFO;
+ }
+ elseif (!$implementation && extension_loaded('apc')) {
+ $value = t('Not enabled');
+ $description = t('Your server is capable of displaying file upload progress through APC, but it is not enabled. Add <code>apc.rfc1867 = 1</code> to your php.ini configuration. Alternatively, it is recommended to use <a href="http://pecl.php.net/package/uploadprogress">PECL uploadprogress</a>, which supports more than one simultaneous upload.');
+ $severity = REQUIREMENT_INFO;
+ }
+ elseif (!$implementation) {
+ $value = t('Not enabled');
+ $description = t('Your server is capable of displaying file upload progress, but does not have the required libraries. It is recommended to install the <a href="http://pecl.php.net/package/uploadprogress">PECL uploadprogress library</a> (preferred) or to install <a href="http://us2.php.net/apc">APC</a>.');
+ $severity = REQUIREMENT_INFO;
+ }
+ elseif ($implementation == 'apc') {
+ $value = t('Enabled (<a href="http://php.net/manual/en/apc.configuration.php#ini.apc.rfc1867">APC RFC1867</a>)');
+ $description = t('Your server is capable of displaying file upload progress using APC RFC1867. Note that only one upload at a time is supported. It is recommended to use the <a href="http://pecl.php.net/package/uploadprogress">PECL uploadprogress library</a> if possible.');
+ $severity = REQUIREMENT_OK;
+ }
+ elseif ($implementation == 'uploadprogress') {
+ $value = t('Enabled (<a href="http://pecl.php.net/package/uploadprogress">PECL uploadprogress</a>)');
+ $severity = REQUIREMENT_OK;
+ }
+ $requirements['file_progress'] = array(
+ 'title' => t('Upload progress'),
+ 'value' => $value,
+ 'severity' => $severity,
+ 'description' => $description,
+ );
+ }
+
+ return $requirements;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.js b/kolab.org/www/drupal-7.26/modules/file/file.js
new file mode 100644
index 0000000..0135a3b
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.js
@@ -0,0 +1,155 @@
+/**
+ * @file
+ * Provides JavaScript additions to the managed file field type.
+ *
+ * This file provides progress bar support (if available), popup windows for
+ * file previews, and disabling of other file fields during Ajax uploads (which
+ * prevents separate file fields from accidentally uploading files).
+ */
+
+(function ($) {
+
+/**
+ * Attach behaviors to managed file element upload fields.
+ */
+Drupal.behaviors.fileValidateAutoAttach = {
+ attach: function (context, settings) {
+ if (settings.file && settings.file.elements) {
+ $.each(settings.file.elements, function(selector) {
+ var extensions = settings.file.elements[selector];
+ $(selector, context).bind('change', {extensions: extensions}, Drupal.file.validateExtension);
+ });
+ }
+ },
+ detach: function (context, settings) {
+ if (settings.file && settings.file.elements) {
+ $.each(settings.file.elements, function(selector) {
+ $(selector, context).unbind('change', Drupal.file.validateExtension);
+ });
+ }
+ }
+};
+
+/**
+ * Attach behaviors to the file upload and remove buttons.
+ */
+Drupal.behaviors.fileButtons = {
+ attach: function (context) {
+ $('input.form-submit', context).bind('mousedown', Drupal.file.disableFields);
+ $('div.form-managed-file input.form-submit', context).bind('mousedown', Drupal.file.progressBar);
+ },
+ detach: function (context) {
+ $('input.form-submit', context).unbind('mousedown', Drupal.file.disableFields);
+ $('div.form-managed-file input.form-submit', context).unbind('mousedown', Drupal.file.progressBar);
+ }
+};
+
+/**
+ * Attach behaviors to links within managed file elements.
+ */
+Drupal.behaviors.filePreviewLinks = {
+ attach: function (context) {
+ $('div.form-managed-file .file a, .file-widget .file a', context).bind('click',Drupal.file.openInNewWindow);
+ },
+ detach: function (context){
+ $('div.form-managed-file .file a, .file-widget .file a', context).unbind('click', Drupal.file.openInNewWindow);
+ }
+};
+
+/**
+ * File upload utility functions.
+ */
+Drupal.file = Drupal.file || {
+ /**
+ * Client-side file input validation of file extensions.
+ */
+ validateExtension: function (event) {
+ // Remove any previous errors.
+ $('.file-upload-js-error').remove();
+
+ // Add client side validation for the input[type=file].
+ var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
+ if (extensionPattern.length > 1 && this.value.length > 0) {
+ var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
+ if (!acceptableMatch.test(this.value)) {
+ var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", {
+ // According to the specifications of HTML5, a file upload control
+ // should not reveal the real local path to the file that a user
+ // has selected. Some web browsers implement this restriction by
+ // replacing the local path with "C:\fakepath\", which can cause
+ // confusion by leaving the user thinking perhaps Drupal could not
+ // find the file because it messed up the file path. To avoid this
+ // confusion, therefore, we strip out the bogus fakepath string.
+ '%filename': this.value.replace('C:\\fakepath\\', ''),
+ '%extensions': extensionPattern.replace(/\|/g, ', ')
+ });
+ $(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
+ this.value = '';
+ return false;
+ }
+ }
+ },
+ /**
+ * Prevent file uploads when using buttons not intended to upload.
+ */
+ disableFields: function (event){
+ var clickedButton = this;
+
+ // Only disable upload fields for Ajax buttons.
+ if (!$(clickedButton).hasClass('ajax-processed')) {
+ return;
+ }
+
+ // Check if we're working with an "Upload" button.
+ var $enabledFields = [];
+ if ($(this).closest('div.form-managed-file').length > 0) {
+ $enabledFields = $(this).closest('div.form-managed-file').find('input.form-file');
+ }
+
+ // Temporarily disable upload fields other than the one we're currently
+ // working with. Filter out fields that are already disabled so that they
+ // do not get enabled when we re-enable these fields at the end of behavior
+ // processing. Re-enable in a setTimeout set to a relatively short amount
+ // of time (1 second). All the other mousedown handlers (like Drupal's Ajax
+ // behaviors) are excuted before any timeout functions are called, so we
+ // don't have to worry about the fields being re-enabled too soon.
+ // @todo If the previous sentence is true, why not set the timeout to 0?
+ var $fieldsToTemporarilyDisable = $('div.form-managed-file input.form-file').not($enabledFields).not(':disabled');
+ $fieldsToTemporarilyDisable.attr('disabled', 'disabled');
+ setTimeout(function (){
+ $fieldsToTemporarilyDisable.attr('disabled', false);
+ }, 1000);
+ },
+ /**
+ * Add progress bar support if possible.
+ */
+ progressBar: function (event) {
+ var clickedButton = this;
+ var $progressId = $(clickedButton).closest('div.form-managed-file').find('input.file-progress');
+ if ($progressId.length) {
+ var originalName = $progressId.attr('name');
+
+ // Replace the name with the required identifier.
+ $progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]);
+
+ // Restore the original name after the upload begins.
+ setTimeout(function () {
+ $progressId.attr('name', originalName);
+ }, 1000);
+ }
+ // Show the progress bar if the upload takes longer than half a second.
+ setTimeout(function () {
+ $(clickedButton).closest('div.form-managed-file').find('div.ajax-progress-bar').slideDown();
+ }, 500);
+ },
+ /**
+ * Open links to files within forms in a new window.
+ */
+ openInNewWindow: function (event) {
+ $(this).attr('target', '_blank');
+ window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550');
+ return false;
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/file/file.module b/kolab.org/www/drupal-7.26/modules/file/file.module
new file mode 100644
index 0000000..3d351fa
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/file.module
@@ -0,0 +1,1013 @@
+<?php
+
+/**
+ * @file
+ * Defines a "managed_file" Form API field and a "file" field for Field module.
+ */
+
+// Load all Field module hooks for File.
+require_once DRUPAL_ROOT . '/modules/file/file.field.inc';
+
+/**
+ * Implements hook_help().
+ */
+function file_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#file':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The File module defines a <em>File</em> field type for the Field module, which lets you manage and validate uploaded files attached to content on your site (see the <a href="@field-help">Field module help page</a> for more information about fields). For more information, see the online handbook entry for <a href="@file">File module</a>.', array('@field-help' => url('admin/help/field'), '@file' => 'http://drupal.org/documentation/modules/file')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Attaching files to content') . '</dt>';
+ $output .= '<dd>' . t('The File module allows users to attach files to content (e.g., PDF files, spreadsheets, etc.), when a <em>File</em> field is added to a given content type using the <a href="@fieldui-help">Field UI module</a>. You can add validation options to your File field, such as specifying a maximum file size and allowed file extensions.', array('@fieldui-help' => url('admin/help/field_ui'))) . '</dd>';
+ $output .= '<dt>' . t('Managing attachment display') . '</dt>';
+ $output .= '<dd>' . t('When you attach a file to content, you can specify whether it is <em>listed</em> or not. Listed files are displayed automatically in a section at the bottom of your content; non-listed files are available for embedding in your content, but are not included in the list at the bottom.') . '</dd>';
+ $output .= '<dt>' . t('Managing file locations') . '</dt>';
+ $output .= '<dd>' . t("When you create a File field, you can specify a directory where the files will be stored, which can be within either the <em>public</em> or <em>private</em> files directory. Files in the public directory can be accessed directly through the web server; when public files are listed, direct links to the files are used, and anyone who knows a file's URL can download the file. Files in the private directory are not accessible directly through the web server; when private files are listed, the links are Drupal path requests. This adds to server load and download time, since Drupal must start up and resolve the path for each file download request, but allows for access restrictions.") . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function file_menu() {
+ $items = array();
+
+ $items['file/ajax'] = array(
+ 'page callback' => 'file_ajax_upload',
+ 'delivery callback' => 'ajax_deliver',
+ 'access arguments' => array('access content'),
+ 'theme callback' => 'ajax_base_page_theme',
+ 'type' => MENU_CALLBACK,
+ );
+ $items['file/progress'] = array(
+ 'page callback' => 'file_ajax_progress',
+ 'access arguments' => array('access content'),
+ 'theme callback' => 'ajax_base_page_theme',
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
+ * Implements hook_element_info().
+ *
+ * The managed file element may be used anywhere in Drupal.
+ */
+function file_element_info() {
+ $file_path = drupal_get_path('module', 'file');
+ $types['managed_file'] = array(
+ '#input' => TRUE,
+ '#process' => array('file_managed_file_process'),
+ '#value_callback' => 'file_managed_file_value',
+ '#element_validate' => array('file_managed_file_validate'),
+ '#pre_render' => array('file_managed_file_pre_render'),
+ '#theme' => 'file_managed_file',
+ '#theme_wrappers' => array('form_element'),
+ '#progress_indicator' => 'throbber',
+ '#progress_message' => NULL,
+ '#upload_validators' => array(),
+ '#upload_location' => NULL,
+ '#size' => 22,
+ '#extended' => FALSE,
+ '#attached' => array(
+ 'css' => array($file_path . '/file.css'),
+ 'js' => array($file_path . '/file.js'),
+ ),
+ );
+ return $types;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function file_theme() {
+ return array(
+ // file.module.
+ 'file_link' => array(
+ 'variables' => array('file' => NULL, 'icon_directory' => NULL),
+ ),
+ 'file_icon' => array(
+ 'variables' => array('file' => NULL, 'icon_directory' => NULL),
+ ),
+ 'file_managed_file' => array(
+ 'render element' => 'element',
+ ),
+
+ // file.field.inc.
+ 'file_widget' => array(
+ 'render element' => 'element',
+ ),
+ 'file_widget_multiple' => array(
+ 'render element' => 'element',
+ ),
+ 'file_formatter_table' => array(
+ 'variables' => array('items' => NULL),
+ ),
+ 'file_upload_help' => array(
+ 'variables' => array('description' => NULL, 'upload_validators' => NULL),
+ ),
+ );
+}
+
+/**
+ * Implements hook_file_download().
+ *
+ * This function takes an extra parameter $field_type so that it may
+ * be re-used by other File-like modules, such as Image.
+ */
+function file_file_download($uri, $field_type = 'file') {
+ global $user;
+
+ // Get the file record based on the URI. If not in the database just return.
+ $files = file_load_multiple(array(), array('uri' => $uri));
+ if (count($files)) {
+ foreach ($files as $item) {
+ // Since some database servers sometimes use a case-insensitive comparison
+ // by default, double check that the filename is an exact match.
+ if ($item->uri === $uri) {
+ $file = $item;
+ break;
+ }
+ }
+ }
+ if (!isset($file)) {
+ return;
+ }
+
+ // Find out which (if any) fields of this type contain the file.
+ $references = file_get_file_references($file, NULL, FIELD_LOAD_CURRENT, $field_type);
+
+ // Stop processing if there are no references in order to avoid returning
+ // headers for files controlled by other modules. Make an exception for
+ // temporary files where the host entity has not yet been saved (for example,
+ // an image preview on a node/add form) in which case, allow download by the
+ // file's owner.
+ if (empty($references) && ($file->status == FILE_STATUS_PERMANENT || $file->uid != $user->uid)) {
+ return;
+ }
+
+ // Default to allow access.
+ $denied = FALSE;
+ // Loop through all references of this file. If a reference explicitly allows
+ // access to the field to which this file belongs, no further checks are done
+ // and download access is granted. If a reference denies access, eventually
+ // existing additional references are checked. If all references were checked
+ // and no reference denied access, access is granted as well. If at least one
+ // reference denied access, access is denied.
+ foreach ($references as $field_name => $field_references) {
+ foreach ($field_references as $entity_type => $type_references) {
+ foreach ($type_references as $id => $reference) {
+ // Try to load $entity and $field.
+ $entity = entity_load($entity_type, array($id));
+ $entity = reset($entity);
+ $field = field_info_field($field_name);
+
+ // Load the field item that references the file.
+ $field_item = NULL;
+ if ($entity) {
+ // Load all field items for that entity.
+ $field_items = field_get_items($entity_type, $entity, $field_name);
+
+ // Find the field item with the matching URI.
+ foreach ($field_items as $item) {
+ if ($item['uri'] == $uri) {
+ $field_item = $item;
+ break;
+ }
+ }
+ }
+
+ // Check that $entity, $field and $field_item were loaded successfully
+ // and check if access to that field is not disallowed. If any of these
+ // checks fail, stop checking access for this reference.
+ if (empty($entity) || empty($field) || empty($field_item) || !field_access('view', $field, $entity_type, $entity)) {
+ $denied = TRUE;
+ break;
+ }
+
+ // Invoke hook and collect grants/denies for download access.
+ // Default to FALSE and let entities overrule this ruling.
+ $grants = array('system' => FALSE);
+ foreach (module_implements('file_download_access') as $module) {
+ $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field_item, $entity_type, $entity)));
+ }
+ // Allow other modules to alter the returned grants/denies.
+ drupal_alter('file_download_access', $grants, $field_item, $entity_type, $entity);
+
+ if (in_array(TRUE, $grants)) {
+ // If TRUE is returned, access is granted and no further checks are
+ // necessary.
+ $denied = FALSE;
+ break 3;
+ }
+
+ if (in_array(FALSE, $grants)) {
+ // If an implementation returns FALSE, access to this entity is denied
+ // but the file could belong to another entity to which the user might
+ // have access. Continue with these.
+ $denied = TRUE;
+ }
+ }
+ }
+ }
+
+ // Access specifically denied.
+ if ($denied) {
+ return -1;
+ }
+
+ // Access is granted.
+ $headers = file_get_content_headers($file);
+ return $headers;
+}
+
+/**
+ * Menu callback; Shared Ajax callback for file uploads and deletions.
+ *
+ * This rebuilds the form element for a particular field item. As long as the
+ * form processing is properly encapsulated in the widget element the form
+ * should rebuild correctly using FAPI without the need for additional callbacks
+ * or processing.
+ */
+function file_ajax_upload() {
+ $form_parents = func_get_args();
+ $form_build_id = (string) array_pop($form_parents);
+
+ if (empty($_POST['form_build_id']) || $form_build_id != $_POST['form_build_id']) {
+ // Invalid request.
+ drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
+ $commands = array();
+ $commands[] = ajax_command_replace(NULL, theme('status_messages'));
+ return array('#type' => 'ajax', '#commands' => $commands);
+ }
+
+ list($form, $form_state) = ajax_get_form();
+
+ if (!$form) {
+ // Invalid form_build_id.
+ drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
+ $commands = array();
+ $commands[] = ajax_command_replace(NULL, theme('status_messages'));
+ return array('#type' => 'ajax', '#commands' => $commands);
+ }
+
+ // Get the current element and count the number of files.
+ $current_element = $form;
+ foreach ($form_parents as $parent) {
+ $current_element = $current_element[$parent];
+ }
+ $current_file_count = isset($current_element['#file_upload_delta']) ? $current_element['#file_upload_delta'] : 0;
+
+ // Process user input. $form and $form_state are modified in the process.
+ drupal_process_form($form['#form_id'], $form, $form_state);
+
+ // Retrieve the element to be rendered.
+ foreach ($form_parents as $parent) {
+ $form = $form[$parent];
+ }
+
+ // Add the special Ajax class if a new file was added.
+ if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) {
+ $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content';
+ }
+ // Otherwise just add the new content class on a placeholder.
+ else {
+ $form['#suffix'] .= '<span class="ajax-new-content"></span>';
+ }
+
+ $output = theme('status_messages') . drupal_render($form);
+ $js = drupal_add_js();
+ $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
+
+ $commands = array();
+ $commands[] = ajax_command_replace(NULL, $output, $settings);
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Menu callback for upload progress.
+ *
+ * @param $key
+ * The unique key for this upload process.
+ */
+function file_ajax_progress($key) {
+ $progress = array(
+ 'message' => t('Starting upload...'),
+ 'percentage' => -1,
+ );
+
+ $implementation = file_progress_implementation();
+ if ($implementation == 'uploadprogress') {
+ $status = uploadprogress_get_info($key);
+ if (isset($status['bytes_uploaded']) && !empty($status['bytes_total'])) {
+ $progress['message'] = t('Uploading... (@current of @total)', array('@current' => format_size($status['bytes_uploaded']), '@total' => format_size($status['bytes_total'])));
+ $progress['percentage'] = round(100 * $status['bytes_uploaded'] / $status['bytes_total']);
+ }
+ }
+ elseif ($implementation == 'apc') {
+ $status = apc_fetch('upload_' . $key);
+ if (isset($status['current']) && !empty($status['total'])) {
+ $progress['message'] = t('Uploading... (@current of @total)', array('@current' => format_size($status['current']), '@total' => format_size($status['total'])));
+ $progress['percentage'] = round(100 * $status['current'] / $status['total']);
+ }
+ }
+
+ drupal_json_output($progress);
+}
+
+/**
+ * Determines the preferred upload progress implementation.
+ *
+ * @return
+ * A string indicating which upload progress system is available. Either "apc"
+ * or "uploadprogress". If neither are available, returns FALSE.
+ */
+function file_progress_implementation() {
+ static $implementation;
+ if (!isset($implementation)) {
+ $implementation = FALSE;
+
+ // We prefer the PECL extension uploadprogress because it supports multiple
+ // simultaneous uploads. APC only supports one at a time.
+ if (extension_loaded('uploadprogress')) {
+ $implementation = 'uploadprogress';
+ }
+ elseif (extension_loaded('apc') && ini_get('apc.rfc1867')) {
+ $implementation = 'apc';
+ }
+ }
+ return $implementation;
+}
+
+/**
+ * Implements hook_file_delete().
+ */
+function file_file_delete($file) {
+ // TODO: Remove references to a file that is in-use.
+}
+
+/**
+ * Process function to expand the managed_file element type.
+ *
+ * Expands the file type to include Upload and Remove buttons, as well as
+ * support for a default value.
+ */
+function file_managed_file_process($element, &$form_state, $form) {
+ $fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0;
+
+ // Set some default element properties.
+ $element['#progress_indicator'] = empty($element['#progress_indicator']) ? 'none' : $element['#progress_indicator'];
+ $element['#file'] = $fid ? file_load($fid) : FALSE;
+ $element['#tree'] = TRUE;
+
+ $ajax_settings = array(
+ 'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'],
+ 'wrapper' => $element['#id'] . '-ajax-wrapper',
+ 'effect' => 'fade',
+ 'progress' => array(
+ 'type' => $element['#progress_indicator'],
+ 'message' => $element['#progress_message'],
+ ),
+ );
+
+ // Set up the buttons first since we need to check if they were clicked.
+ $element['upload_button'] = array(
+ '#name' => implode('_', $element['#parents']) . '_upload_button',
+ '#type' => 'submit',
+ '#value' => t('Upload'),
+ '#validate' => array(),
+ '#submit' => array('file_managed_file_submit'),
+ '#limit_validation_errors' => array($element['#parents']),
+ '#ajax' => $ajax_settings,
+ '#weight' => -5,
+ );
+
+ // Force the progress indicator for the remove button to be either 'none' or
+ // 'throbber', even if the upload button is using something else.
+ $ajax_settings['progress']['type'] = ($element['#progress_indicator'] == 'none') ? 'none' : 'throbber';
+ $ajax_settings['progress']['message'] = NULL;
+ $ajax_settings['effect'] = 'none';
+ $element['remove_button'] = array(
+ '#name' => implode('_', $element['#parents']) . '_remove_button',
+ '#type' => 'submit',
+ '#value' => t('Remove'),
+ '#validate' => array(),
+ '#submit' => array('file_managed_file_submit'),
+ '#limit_validation_errors' => array($element['#parents']),
+ '#ajax' => $ajax_settings,
+ '#weight' => -5,
+ );
+
+ $element['fid'] = array(
+ '#type' => 'hidden',
+ '#value' => $fid,
+ );
+
+ // Add progress bar support to the upload if possible.
+ if ($element['#progress_indicator'] == 'bar' && $implementation = file_progress_implementation()) {
+ $upload_progress_key = mt_rand();
+
+ if ($implementation == 'uploadprogress') {
+ $element['UPLOAD_IDENTIFIER'] = array(
+ '#type' => 'hidden',
+ '#value' => $upload_progress_key,
+ '#attributes' => array('class' => array('file-progress')),
+ // Uploadprogress extension requires this field to be at the top of the
+ // form.
+ '#weight' => -20,
+ );
+ }
+ elseif ($implementation == 'apc') {
+ $element['APC_UPLOAD_PROGRESS'] = array(
+ '#type' => 'hidden',
+ '#value' => $upload_progress_key,
+ '#attributes' => array('class' => array('file-progress')),
+ // Uploadprogress extension requires this field to be at the top of the
+ // form.
+ '#weight' => -20,
+ );
+ }
+
+ // Add the upload progress callback.
+ $element['upload_button']['#ajax']['progress']['path'] = 'file/progress/' . $upload_progress_key;
+ }
+
+ // The file upload field itself.
+ $element['upload'] = array(
+ '#name' => 'files[' . implode('_', $element['#parents']) . ']',
+ '#type' => 'file',
+ '#title' => t('Choose a file'),
+ '#title_display' => 'invisible',
+ '#size' => $element['#size'],
+ '#theme_wrappers' => array(),
+ '#weight' => -10,
+ );
+
+ if ($fid && $element['#file']) {
+ $element['filename'] = array(
+ '#type' => 'markup',
+ '#markup' => theme('file_link', array('file' => $element['#file'])) . ' ',
+ '#weight' => -10,
+ );
+ }
+
+ // Add the extension list to the page as JavaScript settings.
+ if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
+ $extension_list = implode(',', array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0])));
+ $element['upload']['#attached']['js'] = array(
+ array(
+ 'type' => 'setting',
+ 'data' => array('file' => array('elements' => array('#' . $element['#id'] . '-upload' => $extension_list)))
+ )
+ );
+ }
+
+ // Prefix and suffix used for Ajax replacement.
+ $element['#prefix'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
+ $element['#suffix'] = '</div>';
+
+ return $element;
+}
+
+/**
+ * The #value_callback for a managed_file type element.
+ */
+function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) {
+ $fid = 0;
+
+ // Find the current value of this field from the form state.
+ $form_state_fid = $form_state['values'];
+ foreach ($element['#parents'] as $parent) {
+ $form_state_fid = isset($form_state_fid[$parent]) ? $form_state_fid[$parent] : 0;
+ }
+
+ if ($element['#extended'] && isset($form_state_fid['fid'])) {
+ $fid = $form_state_fid['fid'];
+ }
+ elseif (is_numeric($form_state_fid)) {
+ $fid = $form_state_fid;
+ }
+
+ // Process any input and save new uploads.
+ if ($input !== FALSE) {
+ $return = $input;
+
+ // Uploads take priority over all other values.
+ if ($file = file_managed_file_save_upload($element)) {
+ $fid = $file->fid;
+ }
+ else {
+ // Check for #filefield_value_callback values.
+ // Because FAPI does not allow multiple #value_callback values like it
+ // does for #element_validate and #process, this fills the missing
+ // functionality to allow File fields to be extended through FAPI.
+ if (isset($element['#file_value_callbacks'])) {
+ foreach ($element['#file_value_callbacks'] as $callback) {
+ $callback($element, $input, $form_state);
+ }
+ }
+ // Load file if the FID has changed to confirm it exists.
+ if (isset($input['fid']) && $file = file_load($input['fid'])) {
+ $fid = $file->fid;
+ }
+ }
+ }
+
+ // If there is no input, set the default value.
+ else {
+ if ($element['#extended']) {
+ $default_fid = isset($element['#default_value']['fid']) ? $element['#default_value']['fid'] : 0;
+ $return = isset($element['#default_value']) ? $element['#default_value'] : array('fid' => 0);
+ }
+ else {
+ $default_fid = isset($element['#default_value']) ? $element['#default_value'] : 0;
+ $return = array('fid' => 0);
+ }
+
+ // Confirm that the file exists when used as a default value.
+ if ($default_fid && $file = file_load($default_fid)) {
+ $fid = $file->fid;
+ }
+ }
+
+ $return['fid'] = $fid;
+
+ return $return;
+}
+
+/**
+ * An #element_validate callback for the managed_file element.
+ */
+function file_managed_file_validate(&$element, &$form_state) {
+ // If referencing an existing file, only allow if there are existing
+ // references. This prevents unmanaged files from being deleted if this
+ // item were to be deleted.
+ $clicked_button = end($form_state['triggering_element']['#parents']);
+ if ($clicked_button != 'remove_button' && !empty($element['fid']['#value'])) {
+ if ($file = file_load($element['fid']['#value'])) {
+ if ($file->status == FILE_STATUS_PERMANENT) {
+ $references = file_usage_list($file);
+ if (empty($references)) {
+ form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title'])));
+ }
+ }
+ }
+ else {
+ form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title'])));
+ }
+ }
+
+ // Check required property based on the FID.
+ if ($element['#required'] && empty($element['fid']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) {
+ form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title'])));
+ }
+
+ // Consolidate the array value of this field to a single FID.
+ if (!$element['#extended']) {
+ form_set_value($element, $element['fid']['#value'], $form_state);
+ }
+}
+
+/**
+ * Form submission handler for upload / remove buttons of managed_file elements.
+ *
+ * @see file_managed_file_process()
+ */
+function file_managed_file_submit($form, &$form_state) {
+ // Determine whether it was the upload or the remove button that was clicked,
+ // and set $element to the managed_file element that contains that button.
+ $parents = $form_state['triggering_element']['#array_parents'];
+ $button_key = array_pop($parents);
+ $element = drupal_array_get_nested_value($form, $parents);
+
+ // No action is needed here for the upload button, because all file uploads on
+ // the form are processed by file_managed_file_value() regardless of which
+ // button was clicked. Action is needed here for the remove button, because we
+ // only remove a file in response to its remove button being clicked.
+ if ($button_key == 'remove_button') {
+ // If it's a temporary file we can safely remove it immediately, otherwise
+ // it's up to the implementing module to clean up files that are in use.
+ if ($element['#file'] && $element['#file']->status == 0) {
+ file_delete($element['#file']);
+ }
+ // Update both $form_state['values'] and $form_state['input'] to reflect
+ // that the file has been removed, so that the form is rebuilt correctly.
+ // $form_state['values'] must be updated in case additional submit handlers
+ // run, and for form building functions that run during the rebuild, such as
+ // when the managed_file element is part of a field widget.
+ // $form_state['input'] must be updated so that file_managed_file_value()
+ // has correct information during the rebuild.
+ $values_element = $element['#extended'] ? $element['fid'] : $element;
+ form_set_value($values_element, NULL, $form_state);
+ drupal_array_set_nested_value($form_state['input'], $values_element['#parents'], NULL);
+ }
+
+ // Set the form to rebuild so that $form is correctly updated in response to
+ // processing the file removal. Since this function did not change $form_state
+ // if the upload button was clicked, a rebuild isn't necessary in that
+ // situation and setting $form_state['redirect'] to FALSE would suffice.
+ // However, we choose to always rebuild, to keep the form processing workflow
+ // consistent between the two buttons.
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Saves any files that have been uploaded into a managed_file element.
+ *
+ * @param $element
+ * The FAPI element whose values are being saved.
+ *
+ * @return
+ * The file object representing the file that was saved, or FALSE if no file
+ * was saved.
+ */
+function file_managed_file_save_upload($element) {
+ $upload_name = implode('_', $element['#parents']);
+ if (empty($_FILES['files']['name'][$upload_name])) {
+ return FALSE;
+ }
+
+ $destination = isset($element['#upload_location']) ? $element['#upload_location'] : NULL;
+ if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
+ watchdog('file', 'The upload directory %directory for the file field !name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $destination, '!name' => $element['#field_name']));
+ form_set_error($upload_name, t('The file could not be uploaded.'));
+ return FALSE;
+ }
+
+ if (!$file = file_save_upload($upload_name, $element['#upload_validators'], $destination)) {
+ watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name));
+ form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title'])));
+ return FALSE;
+ }
+
+ return $file;
+}
+
+/**
+ * Returns HTML for a managed file element.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - element: A render element representing the file.
+ *
+ * @ingroup themeable
+ */
+function theme_file_managed_file($variables) {
+ $element = $variables['element'];
+
+ $attributes = array();
+ if (isset($element['#id'])) {
+ $attributes['id'] = $element['#id'];
+ }
+ if (!empty($element['#attributes']['class'])) {
+ $attributes['class'] = (array) $element['#attributes']['class'];
+ }
+ $attributes['class'][] = 'form-managed-file';
+
+ // This wrapper is required to apply JS behaviors and CSS styling.
+ $output = '';
+ $output .= '<div' . drupal_attributes($attributes) . '>';
+ $output .= drupal_render_children($element);
+ $output .= '</div>';
+ return $output;
+}
+
+/**
+ * #pre_render callback to hide display of the upload or remove controls.
+ *
+ * Upload controls are hidden when a file is already uploaded. Remove controls
+ * are hidden when there is no file attached. Controls are hidden here instead
+ * of in file_managed_file_process(), because #access for these buttons depends
+ * on the managed_file element's #value. See the documentation of form_builder()
+ * for more detailed information about the relationship between #process,
+ * #value, and #access.
+ *
+ * Because #access is set here, it affects display only and does not prevent
+ * JavaScript or other untrusted code from submitting the form as though access
+ * were enabled. The form processing functions for these elements should not
+ * assume that the buttons can't be "clicked" just because they are not
+ * displayed.
+ *
+ * @see file_managed_file_process()
+ * @see form_builder()
+ */
+function file_managed_file_pre_render($element) {
+ // If we already have a file, we don't want to show the upload controls.
+ if (!empty($element['#value']['fid'])) {
+ $element['upload']['#access'] = FALSE;
+ $element['upload_button']['#access'] = FALSE;
+ }
+ // If we don't already have a file, there is nothing to remove.
+ else {
+ $element['remove_button']['#access'] = FALSE;
+ }
+ return $element;
+}
+
+/**
+ * Returns HTML for a link to a file.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - file: A file object to which the link will be created.
+ * - icon_directory: (optional) A path to a directory of icons to be used for
+ * files. Defaults to the value of the "file_icon_directory" variable.
+ *
+ * @ingroup themeable
+ */
+function theme_file_link($variables) {
+ $file = $variables['file'];
+ $icon_directory = $variables['icon_directory'];
+
+ $url = file_create_url($file->uri);
+ $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory));
+
+ // Set options as per anchor format described at
+ // http://microformats.org/wiki/file-format-examples
+ $options = array(
+ 'attributes' => array(
+ 'type' => $file->filemime . '; length=' . $file->filesize,
+ ),
+ );
+
+ // Use the description as the link text if available.
+ if (empty($file->description)) {
+ $link_text = $file->filename;
+ }
+ else {
+ $link_text = $file->description;
+ $options['attributes']['title'] = check_plain($file->filename);
+ }
+
+ return '<span class="file">' . $icon . ' ' . l($link_text, $url, $options) . '</span>';
+}
+
+/**
+ * Returns HTML for an image with an appropriate icon for the given file.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - file: A file object for which to make an icon.
+ * - icon_directory: (optional) A path to a directory of icons to be used for
+ * files. Defaults to the value of the "file_icon_directory" variable.
+ *
+ * @ingroup themeable
+ */
+function theme_file_icon($variables) {
+ $file = $variables['file'];
+ $icon_directory = $variables['icon_directory'];
+
+ $mime = check_plain($file->filemime);
+ $icon_url = file_icon_url($file, $icon_directory);
+ return '<img class="file-icon" alt="" title="' . $mime . '" src="' . $icon_url . '" />';
+}
+
+/**
+ * Creates a URL to the icon for a file object.
+ *
+ * @param $file
+ * A file object.
+ * @param $icon_directory
+ * (optional) A path to a directory of icons to be used for files. Defaults to
+ * the value of the "file_icon_directory" variable.
+ *
+ * @return
+ * A URL string to the icon, or FALSE if an appropriate icon cannot be found.
+ */
+function file_icon_url($file, $icon_directory = NULL) {
+ if ($icon_path = file_icon_path($file, $icon_directory)) {
+ return base_path() . $icon_path;
+ }
+ return FALSE;
+}
+
+/**
+ * Creates a path to the icon for a file object.
+ *
+ * @param $file
+ * A file object.
+ * @param $icon_directory
+ * (optional) A path to a directory of icons to be used for files. Defaults to
+ * the value of the "file_icon_directory" variable.
+ *
+ * @return
+ * A string to the icon as a local path, or FALSE if an appropriate icon could
+ * not be found.
+ */
+function file_icon_path($file, $icon_directory = NULL) {
+ // Use the default set of icons if none specified.
+ if (!isset($icon_directory)) {
+ $icon_directory = variable_get('file_icon_directory', drupal_get_path('module', 'file') . '/icons');
+ }
+
+ // If there's an icon matching the exact mimetype, go for it.
+ $dashed_mime = strtr($file->filemime, array('/' => '-'));
+ $icon_path = $icon_directory . '/' . $dashed_mime . '.png';
+ if (file_exists($icon_path)) {
+ return $icon_path;
+ }
+
+ // For a few mimetypes, we can "manually" map to a generic icon.
+ $generic_mime = (string) file_icon_map($file);
+ $icon_path = $icon_directory . '/' . $generic_mime . '.png';
+ if ($generic_mime && file_exists($icon_path)) {
+ return $icon_path;
+ }
+
+ // Use generic icons for each category that provides such icons.
+ foreach (array('audio', 'image', 'text', 'video') as $category) {
+ if (strpos($file->filemime, $category . '/') === 0) {
+ $icon_path = $icon_directory . '/' . $category . '-x-generic.png';
+ if (file_exists($icon_path)) {
+ return $icon_path;
+ }
+ }
+ }
+
+ // Try application-octet-stream as last fallback.
+ $icon_path = $icon_directory . '/application-octet-stream.png';
+ if (file_exists($icon_path)) {
+ return $icon_path;
+ }
+
+ // No icon can be found.
+ return FALSE;
+}
+
+/**
+ * Determines the generic icon MIME package based on a file's MIME type.
+ *
+ * @param $file
+ * A file object.
+ *
+ * @return
+ * The generic icon MIME package expected for this file.
+ */
+function file_icon_map($file) {
+ switch ($file->filemime) {
+ // Word document types.
+ case 'application/msword':
+ case 'application/vnd.ms-word.document.macroEnabled.12':
+ case 'application/vnd.oasis.opendocument.text':
+ case 'application/vnd.oasis.opendocument.text-template':
+ case 'application/vnd.oasis.opendocument.text-master':
+ case 'application/vnd.oasis.opendocument.text-web':
+ case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
+ case 'application/vnd.stardivision.writer':
+ case 'application/vnd.sun.xml.writer':
+ case 'application/vnd.sun.xml.writer.template':
+ case 'application/vnd.sun.xml.writer.global':
+ case 'application/vnd.wordperfect':
+ case 'application/x-abiword':
+ case 'application/x-applix-word':
+ case 'application/x-kword':
+ case 'application/x-kword-crypt':
+ return 'x-office-document';
+
+ // Spreadsheet document types.
+ case 'application/vnd.ms-excel':
+ case 'application/vnd.ms-excel.sheet.macroEnabled.12':
+ case 'application/vnd.oasis.opendocument.spreadsheet':
+ case 'application/vnd.oasis.opendocument.spreadsheet-template':
+ case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
+ case 'application/vnd.stardivision.calc':
+ case 'application/vnd.sun.xml.calc':
+ case 'application/vnd.sun.xml.calc.template':
+ case 'application/vnd.lotus-1-2-3':
+ case 'application/x-applix-spreadsheet':
+ case 'application/x-gnumeric':
+ case 'application/x-kspread':
+ case 'application/x-kspread-crypt':
+ return 'x-office-spreadsheet';
+
+ // Presentation document types.
+ case 'application/vnd.ms-powerpoint':
+ case 'application/vnd.ms-powerpoint.presentation.macroEnabled.12':
+ case 'application/vnd.oasis.opendocument.presentation':
+ case 'application/vnd.oasis.opendocument.presentation-template':
+ case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
+ case 'application/vnd.stardivision.impress':
+ case 'application/vnd.sun.xml.impress':
+ case 'application/vnd.sun.xml.impress.template':
+ case 'application/x-kpresenter':
+ return 'x-office-presentation';
+
+ // Compressed archive types.
+ case 'application/zip':
+ case 'application/x-zip':
+ case 'application/stuffit':
+ case 'application/x-stuffit':
+ case 'application/x-7z-compressed':
+ case 'application/x-ace':
+ case 'application/x-arj':
+ case 'application/x-bzip':
+ case 'application/x-bzip-compressed-tar':
+ case 'application/x-compress':
+ case 'application/x-compressed-tar':
+ case 'application/x-cpio-compressed':
+ case 'application/x-deb':
+ case 'application/x-gzip':
+ case 'application/x-java-archive':
+ case 'application/x-lha':
+ case 'application/x-lhz':
+ case 'application/x-lzop':
+ case 'application/x-rar':
+ case 'application/x-rpm':
+ case 'application/x-tzo':
+ case 'application/x-tar':
+ case 'application/x-tarz':
+ case 'application/x-tgz':
+ return 'package-x-generic';
+
+ // Script file types.
+ case 'application/ecmascript':
+ case 'application/javascript':
+ case 'application/mathematica':
+ case 'application/vnd.mozilla.xul+xml':
+ case 'application/x-asp':
+ case 'application/x-awk':
+ case 'application/x-cgi':
+ case 'application/x-csh':
+ case 'application/x-m4':
+ case 'application/x-perl':
+ case 'application/x-php':
+ case 'application/x-ruby':
+ case 'application/x-shellscript':
+ case 'text/vnd.wap.wmlscript':
+ case 'text/x-emacs-lisp':
+ case 'text/x-haskell':
+ case 'text/x-literate-haskell':
+ case 'text/x-lua':
+ case 'text/x-makefile':
+ case 'text/x-matlab':
+ case 'text/x-python':
+ case 'text/x-sql':
+ case 'text/x-tcl':
+ return 'text-x-script';
+
+ // HTML aliases.
+ case 'application/xhtml+xml':
+ return 'text-html';
+
+ // Executable types.
+ case 'application/x-macbinary':
+ case 'application/x-ms-dos-executable':
+ case 'application/x-pef-executable':
+ return 'application-x-executable';
+
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * @defgroup file-module-api File module public API functions
+ * @{
+ * These functions may be used to determine if and where a file is in use.
+ */
+
+/**
+ * Retrieves a list of references to a file.
+ *
+ * @param $file
+ * A file object.
+ * @param $field
+ * (optional) A field array to be used for this check. If given, limits the
+ * reference check to the given field.
+ * @param $age
+ * (optional) A constant that specifies which references to count. Use
+ * FIELD_LOAD_REVISION to retrieve all references within all revisions or
+ * FIELD_LOAD_CURRENT to retrieve references only in the current revisions.
+ * @param $field_type
+ * (optional) The name of a field type. If given, limits the reference check
+ * to fields of the given type.
+ *
+ * @return
+ * An integer value.
+ */
+function file_get_file_references($file, $field = NULL, $age = FIELD_LOAD_REVISION, $field_type = 'file') {
+ $references = drupal_static(__FUNCTION__, array());
+ $fields = isset($field) ? array($field['field_name'] => $field) : field_info_fields();
+
+ foreach ($fields as $field_name => $file_field) {
+ if ((empty($field_type) || $file_field['type'] == $field_type) && !isset($references[$field_name])) {
+ // Get each time this file is used within a field.
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($file_field, 'fid', $file->fid)
+ ->age($age);
+ $references[$field_name] = $query->execute();
+ }
+ }
+
+ return isset($field) ? $references[$field['field_name']] : array_filter($references);
+}
+
+/**
+ * @} End of "defgroup file-module-api".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/application-octet-stream.png b/kolab.org/www/drupal-7.26/modules/file/icons/application-octet-stream.png
new file mode 100644
index 0000000..d545321
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/application-octet-stream.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/application-pdf.png b/kolab.org/www/drupal-7.26/modules/file/icons/application-pdf.png
new file mode 100644
index 0000000..36107d6
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/application-pdf.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/application-x-executable.png b/kolab.org/www/drupal-7.26/modules/file/icons/application-x-executable.png
new file mode 100644
index 0000000..d545321
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/application-x-executable.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/audio-x-generic.png b/kolab.org/www/drupal-7.26/modules/file/icons/audio-x-generic.png
new file mode 100644
index 0000000..28d7f50
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/audio-x-generic.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/image-x-generic.png b/kolab.org/www/drupal-7.26/modules/file/icons/image-x-generic.png
new file mode 100644
index 0000000..c1b814f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/image-x-generic.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/package-x-generic.png b/kolab.org/www/drupal-7.26/modules/file/icons/package-x-generic.png
new file mode 100644
index 0000000..21fc382
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/package-x-generic.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/text-html.png b/kolab.org/www/drupal-7.26/modules/file/icons/text-html.png
new file mode 100644
index 0000000..9c7c793
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/text-html.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/text-plain.png b/kolab.org/www/drupal-7.26/modules/file/icons/text-plain.png
new file mode 100644
index 0000000..0680484
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/text-plain.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/text-x-generic.png b/kolab.org/www/drupal-7.26/modules/file/icons/text-x-generic.png
new file mode 100644
index 0000000..0680484
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/text-x-generic.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/text-x-script.png b/kolab.org/www/drupal-7.26/modules/file/icons/text-x-script.png
new file mode 100644
index 0000000..f9ecca8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/text-x-script.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/video-x-generic.png b/kolab.org/www/drupal-7.26/modules/file/icons/video-x-generic.png
new file mode 100644
index 0000000..a2b71f9
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/video-x-generic.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/x-office-document.png b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-document.png
new file mode 100644
index 0000000..40db538
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-document.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/x-office-presentation.png b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-presentation.png
new file mode 100644
index 0000000..fb119e5
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-presentation.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/icons/x-office-spreadsheet.png b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-spreadsheet.png
new file mode 100644
index 0000000..9af7b61
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/icons/x-office-spreadsheet.png
Binary files differ
diff --git a/kolab.org/www/drupal-7.26/modules/file/tests/file.test b/kolab.org/www/drupal-7.26/modules/file/tests/file.test
new file mode 100644
index 0000000..69e711a
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/tests/file.test
@@ -0,0 +1,1171 @@
+<?php
+
+/**
+ * @file
+ * Tests for file.module.
+ */
+
+/**
+ * Provides methods specifically for testing File module's field handling.
+ */
+class FileFieldTestCase extends DrupalWebTestCase {
+ protected $admin_user;
+
+ function setUp() {
+ // Since this is a base class for many test cases, support the same
+ // flexibility that DrupalWebTestCase::setUp() has for the modules to be
+ // passed in as either an array or a variable number of string arguments.
+ $modules = func_get_args();
+ if (isset($modules[0]) && is_array($modules[0])) {
+ $modules = $modules[0];
+ }
+ $modules[] = 'file';
+ $modules[] = 'file_module_test';
+ parent::setUp($modules);
+ $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
+ $this->drupalLogin($this->admin_user);
+ }
+
+ /**
+ * Retrieves a sample file of the specified type.
+ */
+ function getTestFile($type_name, $size = NULL) {
+ // Get a file to upload.
+ $file = current($this->drupalGetTestFiles($type_name, $size));
+
+ // Add a filesize property to files as would be read by file_load().
+ $file->filesize = filesize($file->uri);
+
+ return $file;
+ }
+
+ /**
+ * Retrieves the fid of the last inserted file.
+ */
+ function getLastFileId() {
+ return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
+ }
+
+ /**
+ * Creates a new file field.
+ *
+ * @param $name
+ * The name of the new field (all lowercase), exclude the "field_" prefix.
+ * @param $type_name
+ * The node type that this field will be added to.
+ * @param $field_settings
+ * A list of field settings that will be added to the defaults.
+ * @param $instance_settings
+ * A list of instance settings that will be added to the instance defaults.
+ * @param $widget_settings
+ * A list of widget settings that will be added to the widget defaults.
+ */
+ function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
+ $field = array(
+ 'field_name' => $name,
+ 'type' => 'file',
+ 'settings' => array(),
+ 'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
+ );
+ $field['settings'] = array_merge($field['settings'], $field_settings);
+ field_create_field($field);
+
+ $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
+ }
+
+ /**
+ * Attaches a file field to an entity.
+ *
+ * @param $name
+ * The name of the new field (all lowercase), exclude the "field_" prefix.
+ * @param $entity_type
+ * The entity type this field will be added to.
+ * @param $bundle
+ * The bundle this field will be added to.
+ * @param $field_settings
+ * A list of field settings that will be added to the defaults.
+ * @param $instance_settings
+ * A list of instance settings that will be added to the instance defaults.
+ * @param $widget_settings
+ * A list of widget settings that will be added to the widget defaults.
+ */
+ function attachFileField($name, $entity_type, $bundle, $instance_settings = array(), $widget_settings = array()) {
+ $instance = array(
+ 'field_name' => $name,
+ 'label' => $name,
+ 'entity_type' => $entity_type,
+ 'bundle' => $bundle,
+ 'required' => !empty($instance_settings['required']),
+ 'settings' => array(),
+ 'widget' => array(
+ 'type' => 'file_generic',
+ 'settings' => array(),
+ ),
+ );
+ $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+ $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+ field_create_instance($instance);
+ }
+
+ /**
+ * Updates an existing file field with new settings.
+ */
+ function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) {
+ $instance = field_info_instance('node', $name, $type_name);
+ $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+ $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+
+ field_update_instance($instance);
+ }
+
+ /**
+ * Uploads a file to a node.
+ */
+ function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, $extras = array()) {
+ $langcode = LANGUAGE_NONE;
+ $edit = array(
+ "title" => $this->randomName(),
+ 'revision' => (string) (int) $new_revision,
+ );
+
+ if (is_numeric($nid_or_type)) {
+ $nid = $nid_or_type;
+ }
+ else {
+ // Add a new node.
+ $extras['type'] = $nid_or_type;
+ $node = $this->drupalCreateNode($extras);
+ $nid = $node->nid;
+ // Save at least one revision to better simulate a real site.
+ $this->drupalCreateNode(get_object_vars($node));
+ $node = node_load($nid, NULL, TRUE);
+ $this->assertNotEqual($nid, $node->vid, 'Node revision exists.');
+ }
+
+ // Attach a file to the node.
+ $edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($file->uri);
+ $this->drupalPost("node/$nid/edit", $edit, t('Save'));
+
+ return $nid;
+ }
+
+ /**
+ * Removes a file from a node.
+ *
+ * Note that if replacing a file, it must first be removed then added again.
+ */
+ function removeNodeFile($nid, $new_revision = TRUE) {
+ $edit = array(
+ 'revision' => (string) (int) $new_revision,
+ );
+
+ $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ }
+
+ /**
+ * Replaces a file within a node.
+ */
+ function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
+ $edit = array(
+ 'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => drupal_realpath($file->uri),
+ 'revision' => (string) (int) $new_revision,
+ );
+
+ $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ }
+
+ /**
+ * Asserts that a file exists physically on disk.
+ */
+ function assertFileExists($file, $message = NULL) {
+ $message = isset($message) ? $message : format_string('File %file exists on the disk.', array('%file' => $file->uri));
+ $this->assertTrue(is_file($file->uri), $message);
+ }
+
+ /**
+ * Asserts that a file exists in the database.
+ */
+ function assertFileEntryExists($file, $message = NULL) {
+ entity_get_controller('file')->resetCache();
+ $db_file = file_load($file->fid);
+ $message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->uri));
+ $this->assertEqual($db_file->uri, $file->uri, $message);
+ }
+
+ /**
+ * Asserts that a file does not exist on disk.
+ */
+ function assertFileNotExists($file, $message = NULL) {
+ $message = isset($message) ? $message : format_string('File %file exists on the disk.', array('%file' => $file->uri));
+ $this->assertFalse(is_file($file->uri), $message);
+ }
+
+ /**
+ * Asserts that a file does not exist in the database.
+ */
+ function assertFileEntryNotExists($file, $message) {
+ entity_get_controller('file')->resetCache();
+ $message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->uri));
+ $this->assertFalse(file_load($file->fid), $message);
+ }
+
+ /**
+ * Asserts that a file's status is set to permanent in the database.
+ */
+ function assertFileIsPermanent($file, $message = NULL) {
+ $message = isset($message) ? $message : format_string('File %file is permanent.', array('%file' => $file->uri));
+ $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
+ }
+}
+
+/**
+ * Tests the 'managed_file' element type.
+ *
+ * @todo Create a FileTestCase base class and move FileFieldTestCase methods
+ * that aren't related to fields into it.
+ */
+class FileManagedFileElementTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Managed file element test',
+ 'description' => 'Tests the managed_file element type.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests the managed_file element type.
+ */
+ function testManagedFile() {
+ // Check that $element['#size'] is passed to the child upload element.
+ $this->drupalGet('file/test');
+ $this->assertFieldByXpath('//input[@name="files[nested_file]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.');
+
+ // Perform the tests with all permutations of $form['#tree'] and
+ // $element['#extended'].
+ foreach (array(0, 1) as $tree) {
+ foreach (array(0, 1) as $extended) {
+ $test_file = $this->getTestFile('text');
+ $path = 'file/test/' . $tree . '/' . $extended;
+ $input_base_name = $tree ? 'nested_file' : 'file';
+
+ // Submit without a file.
+ $this->drupalPost($path, array(), t('Save'));
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submitted without a file.');
+
+ // Submit a new file, without using the Upload button.
+ $last_fid_prior = $this->getLastFileId();
+ $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+ $this->drupalPost($path, $edit, t('Save'));
+ $last_fid = $this->getLastFileId();
+ $this->assertTrue($last_fid > $last_fid_prior, 'New file got saved.');
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Submit handler has correct file info.');
+
+ // Submit no new input, but with a default file.
+ $this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Empty submission did not change an existing file.');
+
+ // Now, test the Upload and Remove buttons, with and without Ajax.
+ foreach (array(FALSE, TRUE) as $ajax) {
+ // Upload, then Submit.
+ $last_fid_prior = $this->getLastFileId();
+ $this->drupalGet($path);
+ $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+ if ($ajax) {
+ $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
+ }
+ else {
+ $this->drupalPost(NULL, $edit, t('Upload'));
+ }
+ $last_fid = $this->getLastFileId();
+ $this->assertTrue($last_fid > $last_fid_prior, 'New file got uploaded.');
+ $this->drupalPost(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Submit handler has correct file info.');
+
+ // Remove, then Submit.
+ $this->drupalGet($path . '/' . $last_fid);
+ if ($ajax) {
+ $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
+ }
+ else {
+ $this->drupalPost(NULL, array(), t('Remove'));
+ }
+ $this->drupalPost(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submission after file removal was successful.');
+
+ // Upload, then Remove, then Submit.
+ $this->drupalGet($path);
+ $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+ if ($ajax) {
+ $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
+ $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
+ }
+ else {
+ $this->drupalPost(NULL, $edit, t('Upload'));
+ $this->drupalPost(NULL, array(), t('Remove'));
+ }
+ $this->drupalPost(NULL, array(), t('Save'));
+ $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submission after file upload and removal was successful.');
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Tests file field widget.
+ */
+class FileFieldWidgetTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File field widget test',
+ 'description' => 'Tests the file field widget, single and multi-valued, with and without AJAX, with public and private files.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests upload and remove buttons for a single-valued File field.
+ */
+ function testSingleValuedWidget() {
+ // Use 'page' instead of 'article', so that the 'article' image field does
+ // not conflict with this test. If in the future the 'page' type gets its
+ // own default file or image field, this test can be made more robust by
+ // using a custom node type.
+ $type_name = 'page';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $test_file = $this->getTestFile('text');
+
+ foreach (array('nojs', 'js') as $type) {
+ // Create a new node with the uploaded file and ensure it got uploaded
+ // successfully.
+ // @todo This only tests a 'nojs' submission, because drupalPostAJAX()
+ // does not yet support file uploads.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'New file saved to disk on node creation.');
+
+ // Ensure the file can be downloaded.
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
+
+ // Ensure the edit page has a remove button instead of an upload button.
+ $this->drupalGet("node/$nid/edit");
+ $this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), 'Node with file does not display the "Upload" button.');
+ $this->assertFieldByXpath('//input[@type="submit"]', t('Remove'), 'Node with file displays the "Remove" button.');
+
+ // "Click" the remove button (emulating either a nojs or js submission).
+ switch ($type) {
+ case 'nojs':
+ $this->drupalPost(NULL, array(), t('Remove'));
+ break;
+ case 'js':
+ $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]');
+ $this->drupalPostAJAX(NULL, array(), array((string) $button[0]['name'] => (string) $button[0]['value']));
+ break;
+ }
+
+ // Ensure the page now has an upload button instead of a remove button.
+ $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), 'After clicking the "Remove" button, it is no longer displayed.');
+ $this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), 'After clicking the "Remove" button, the "Upload" button is displayed.');
+
+ // Save the node and ensure it does not have the file.
+ $this->drupalPost(NULL, array(), t('Save'));
+ $node = node_load($nid, NULL, TRUE);
+ $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), 'File was successfully removed from the node.');
+ }
+ }
+
+ /**
+ * Tests upload and remove buttons for multiple multi-valued File fields.
+ */
+ function testMultiValuedWidget() {
+ // Use 'page' instead of 'article', so that the 'article' image field does
+ // not conflict with this test. If in the future the 'page' type gets its
+ // own default file or image field, this test can be made more robust by
+ // using a custom node type.
+ $type_name = 'page';
+ $field_name = strtolower($this->randomName());
+ $field_name2 = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name, array('cardinality' => 3));
+ $this->createFileField($field_name2, $type_name, array('cardinality' => 3));
+
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $field2 = field_info_field($field_name2);
+ $instance2 = field_info_instance('node', $field_name2, $type_name);
+
+ $test_file = $this->getTestFile('text');
+
+ foreach (array('nojs', 'js') as $type) {
+ // Visit the node creation form, and upload 3 files for each field. Since
+ // the field has cardinality of 3, ensure the "Upload" button is displayed
+ // until after the 3rd file, and after that, isn't displayed. Because
+ // SimpleTest triggers the last button with a given name, so upload to the
+ // second field first.
+ // @todo This is only testing a non-Ajax upload, because drupalPostAJAX()
+ // does not yet emulate jQuery's file upload.
+ //
+ $this->drupalGet("node/add/$type_name");
+ foreach (array($field_name2, $field_name) as $each_field_name) {
+ for ($delta = 0; $delta < 3; $delta++) {
+ $edit = array('files[' . $each_field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri));
+ // If the Upload button doesn't exist, drupalPost() will automatically
+ // fail with an assertion message.
+ $this->drupalPost(NULL, $edit, t('Upload'));
+ }
+ }
+ $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), 'After uploading 3 files for each field, the "Upload" button is no longer displayed.');
+
+ $num_expected_remove_buttons = 6;
+
+ foreach (array($field_name, $field_name2) as $current_field_name) {
+ // How many uploaded files for the current field are remaining.
+ $remaining = 3;
+ // Test clicking each "Remove" button. For extra robustness, test them out
+ // of sequential order. They are 0-indexed, and get renumbered after each
+ // iteration, so array(1, 1, 0) means:
+ // - First remove the 2nd file.
+ // - Then remove what is then the 2nd file (was originally the 3rd file).
+ // - Then remove the first file.
+ foreach (array(1,1,0) as $delta) {
+ // Ensure we have the expected number of Remove buttons, and that they
+ // are numbered sequentially.
+ $buttons = $this->xpath('//input[@type="submit" and @value="Remove"]');
+ $this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, format_string('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type)));
+ foreach ($buttons as $i => $button) {
+ $key = $i >= $remaining ? $i - $remaining : $i;
+ $check_field_name = $field_name2;
+ if ($current_field_name == $field_name && $i < $remaining) {
+ $check_field_name = $field_name;
+ }
+
+ $this->assertIdentical((string) $button['name'], $check_field_name . '_' . LANGUAGE_NONE . '_' . $key. '_remove_button');
+ }
+
+ // "Click" the remove button (emulating either a nojs or js submission).
+ $button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button';
+ switch ($type) {
+ case 'nojs':
+ // drupalPost() takes a $submit parameter that is the value of the
+ // button whose click we want to emulate. Since we have multiple
+ // buttons with the value "Remove", and want to control which one we
+ // use, we change the value of the other ones to something else.
+ // Since non-clicked buttons aren't included in the submitted POST
+ // data, and since drupalPost() will result in $this being updated
+ // with a newly rebuilt form, this doesn't cause problems.
+ foreach ($buttons as $button) {
+ if ($button['name'] != $button_name) {
+ $button['value'] = 'DUMMY';
+ }
+ }
+ $this->drupalPost(NULL, array(), t('Remove'));
+ break;
+ case 'js':
+ // drupalPostAJAX() lets us target the button precisely, so we don't
+ // require the workaround used above for nojs.
+ $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove')));
+ break;
+ }
+ $num_expected_remove_buttons--;
+ $remaining--;
+
+ // Ensure an "Upload" button for the current field is displayed with the
+ // correct name.
+ $upload_button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $remaining . '_upload_button';
+ $buttons = $this->xpath('//input[@type="submit" and @value="Upload" and @name=:name]', array(':name' => $upload_button_name));
+ $this->assertTrue(is_array($buttons) && count($buttons) == 1, format_string('The upload button is displayed with the correct name (JSMode=%type).', array('%type' => $type)));
+
+ // Ensure only at most one button per field is displayed.
+ $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
+ $expected = $current_field_name == $field_name ? 1 : 2;
+ $this->assertTrue(is_array($buttons) && count($buttons) == $expected, format_string('After removing a file, only one "Upload" button for each possible field is displayed (JSMode=%type).', array('%type' => $type)));
+ }
+ }
+
+ // Ensure the page now has no Remove buttons.
+ $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), format_string('After removing all files, there is no "Remove" button displayed (JSMode=%type).', array('%type' => $type)));
+
+ // Save the node and ensure it does not have any files.
+ $this->drupalPost(NULL, array('title' => $this->randomName()), t('Save'));
+ $matches = array();
+ preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
+ $nid = $matches[1];
+ $node = node_load($nid, NULL, TRUE);
+ $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), 'Node was successfully saved without any files.');
+ }
+ }
+
+ /**
+ * Tests a file field with a "Private files" upload destination setting.
+ */
+ function testPrivateFileSetting() {
+ // Use 'page' instead of 'article', so that the 'article' image field does
+ // not conflict with this test. If in the future the 'page' type gets its
+ // own default file or image field, this test can be made more robust by
+ // using a custom node type.
+ $type_name = 'page';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $test_file = $this->getTestFile('text');
+
+ // Change the field setting to make its files private, and upload a file.
+ $edit = array('field[settings][uri_scheme]' => 'private');
+ $this->drupalPost("admin/structure/types/manage/$type_name/fields/$field_name", $edit, t('Save settings'));
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'New file saved to disk on node creation.');
+
+ // Ensure the private file is available to the user who uploaded it.
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
+
+ // Ensure we can't change 'uri_scheme' field settings while there are some
+ // entities with uploaded files.
+ $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name");
+ $this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and @disabled="disabled"]', 'public', 'Upload destination setting disabled.');
+
+ // Delete node and confirm that setting could be changed.
+ node_delete($nid);
+ $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name");
+ $this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and not(@disabled)]', 'public', 'Upload destination setting enabled.');
+ }
+
+ /**
+ * Tests that download restrictions on private files work on comments.
+ */
+ function testPrivateFileComment() {
+ $user = $this->drupalCreateUser(array('access comments'));
+
+ // Remove access comments permission from anon user.
+ $edit = array(
+ DRUPAL_ANONYMOUS_RID . '[access comments]' => FALSE,
+ );
+ $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
+
+ // Create a new field.
+ $edit = array(
+ 'fields[_add_new_field][label]' => $label = $this->randomName(),
+ 'fields[_add_new_field][field_name]' => $name = strtolower($this->randomName()),
+ 'fields[_add_new_field][type]' => 'file',
+ 'fields[_add_new_field][widget_type]' => 'file_generic',
+ );
+ $this->drupalPost('admin/structure/types/manage/article/comment/fields', $edit, t('Save'));
+ $edit = array('field[settings][uri_scheme]' => 'private');
+ $this->drupalPost(NULL, $edit, t('Save field settings'));
+ $this->drupalPost(NULL, array(), t('Save settings'));
+
+ // Create node.
+ $text_file = $this->getTestFile('text');
+ $edit = array(
+ 'title' => $this->randomName(),
+ );
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($edit['title']);
+
+ // Add a comment with a file.
+ $text_file = $this->getTestFile('text');
+ $edit = array(
+ 'files[field_' . $name . '_' . LANGUAGE_NONE . '_' . 0 . ']' => drupal_realpath($text_file->uri),
+ 'comment_body[' . LANGUAGE_NONE . '][0][value]' => $comment_body = $this->randomName(),
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Get the comment ID.
+ preg_match('/comment-([0-9]+)/', $this->getUrl(), $matches);
+ $cid = $matches[1];
+
+ // Log in as normal user.
+ $this->drupalLogin($user);
+
+ $comment = comment_load($cid);
+ $comment_file = (object) $comment->{'field_' . $name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($comment_file, 'New file saved to disk on node creation.');
+ // Test authenticated file download.
+ $url = file_create_url($comment_file->uri);
+ $this->assertNotEqual($url, NULL, 'Confirmed that the URL is valid');
+ $this->drupalGet(file_create_url($comment_file->uri));
+ $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
+
+ // Test anonymous file download.
+ $this->drupalLogout();
+ $this->drupalGet(file_create_url($comment_file->uri));
+ $this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
+
+ // Unpublishes node.
+ $this->drupalLogin($this->admin_user);
+ $edit = array(
+ 'status' => FALSE,
+ );
+ $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
+
+ // Ensures normal user can no longer download the file.
+ $this->drupalLogin($user);
+ $this->drupalGet(file_create_url($comment_file->uri));
+ $this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
+ }
+
+}
+
+/**
+ * Tests file handling with node revisions.
+ */
+class FileFieldRevisionTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File field revision test',
+ 'description' => 'Test creating and deleting revisions with files attached.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests creating multiple revisions of a node and managing attached files.
+ *
+ * Expected behaviors:
+ * - Adding a new revision will make another entry in the field table, but
+ * the original file will not be duplicated.
+ * - Deleting a revision should not delete the original file if the file
+ * is in use by another revision.
+ * - When the last revision that uses a file is deleted, the original file
+ * should be deleted also.
+ */
+ function testRevisions() {
+ $type_name = 'article';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ // Attach the same fields to users.
+ $this->attachFileField($field_name, 'user', 'user');
+
+ $test_file = $this->getTestFile('text');
+
+ // Create a new node with the uploaded file.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+ // Check that the file exists on disk and in the database.
+ $node = node_load($nid, NULL, TRUE);
+ $node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $node_vid_r1 = $node->vid;
+ $this->assertFileExists($node_file_r1, 'New file saved to disk on node creation.');
+ $this->assertFileEntryExists($node_file_r1, 'File entry exists in database on node creation.');
+ $this->assertFileIsPermanent($node_file_r1, 'File is permanent.');
+
+ // Upload another file to the same node in a new revision.
+ $this->replaceNodeFile($test_file, $field_name, $nid);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $node_vid_r2 = $node->vid;
+ $this->assertFileExists($node_file_r2, 'Replacement file exists on disk after creating new revision.');
+ $this->assertFileEntryExists($node_file_r2, 'Replacement file entry exists in database after creating new revision.');
+ $this->assertFileIsPermanent($node_file_r2, 'Replacement file is permanent.');
+
+ // Check that the original file is still in place on the first revision.
+ $node = node_load($nid, $node_vid_r1, TRUE);
+ $this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NONE][0], 'Original file still in place after replacing file in new revision.');
+ $this->assertFileExists($node_file_r1, 'Original file still in place after replacing file in new revision.');
+ $this->assertFileEntryExists($node_file_r1, 'Original file entry still in place after replacing file in new revision');
+ $this->assertFileIsPermanent($node_file_r1, 'Original file is still permanent.');
+
+ // Save a new version of the node without any changes.
+ // Check that the file is still the same as the previous revision.
+ $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save'));
+ $node = node_load($nid, NULL, TRUE);
+ $node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $node_vid_r3 = $node->vid;
+ $this->assertEqual($node_file_r2, $node_file_r3, 'Previous revision file still in place after creating a new revision without a new file.');
+ $this->assertFileIsPermanent($node_file_r3, 'New revision file is permanent.');
+
+ // Revert to the first revision and check that the original file is active.
+ $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
+ $node = node_load($nid, NULL, TRUE);
+ $node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $node_vid_r4 = $node->vid;
+ $this->assertEqual($node_file_r1, $node_file_r4, 'Original revision file still in place after reverting to the original revision.');
+ $this->assertFileIsPermanent($node_file_r4, 'Original revision file still permanent after reverting to the original revision.');
+
+ // Delete the second revision and check that the file is kept (since it is
+ // still being used by the third revision).
+ $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
+ $this->assertFileExists($node_file_r3, 'Second file is still available after deleting second revision, since it is being used by the third revision.');
+ $this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting second revision, since it is being used by the third revision.');
+ $this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting second revision, since it is being used by the third revision.');
+
+ // Attach the second file to a user.
+ $user = $this->drupalCreateUser();
+ $edit = (array) $user;
+ $edit[$field_name][LANGUAGE_NONE][0] = (array) $node_file_r3;
+ user_save($user, $edit);
+ $this->drupalGet('user/' . $user->uid . '/edit');
+
+ // Delete the third revision and check that the file is not deleted yet.
+ $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
+ $this->assertFileExists($node_file_r3, 'Second file is still available after deleting third revision, since it is being used by the user.');
+ $this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting third revision, since it is being used by the user.');
+ $this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting third revision, since it is being used by the user.');
+
+ // Delete the user and check that the file is also deleted.
+ user_delete($user->uid);
+ // TODO: This seems like a bug in File API. Clearing the stat cache should
+ // not be necessary here. The file really is deleted, but stream wrappers
+ // doesn't seem to think so unless we clear the PHP file stat() cache.
+ clearstatcache();
+ $this->assertFileNotExists($node_file_r3, 'Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
+ $this->assertFileEntryNotExists($node_file_r3, 'Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
+
+ // Delete the entire node and check that the original file is deleted.
+ $this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
+ $this->assertFileNotExists($node_file_r1, 'Original file is deleted after deleting the entire node with two revisions remaining.');
+ $this->assertFileEntryNotExists($node_file_r1, 'Original file entry is deleted after deleting the entire node with two revisions remaining.');
+ }
+}
+
+/**
+ * Tests that formatters are working properly.
+ */
+class FileFieldDisplayTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File field display tests',
+ 'description' => 'Test the display of file fields in node and views.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests normal formatter display on node display.
+ */
+ function testNodeDisplay() {
+ $field_name = strtolower($this->randomName());
+ $type_name = 'article';
+ $field_settings = array(
+ 'display_field' => '1',
+ 'display_default' => '1',
+ );
+ $instance_settings = array(
+ 'description_field' => '1',
+ );
+ $widget_settings = array();
+ $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ // Create a new node *without* the file field set, and check that the field
+ // is not shown for each node display.
+ $node = $this->drupalCreateNode(array('type' => $type_name));
+ $file_formatters = array('file_default', 'file_table', 'file_url_plain', 'hidden');
+ foreach ($file_formatters as $formatter) {
+ $edit = array(
+ "fields[$field_name][type]" => $formatter,
+ );
+ $this->drupalPost("admin/structure/types/manage/$type_name/display", $edit, t('Save'));
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertNoText($field_name, format_string('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter)));
+ }
+
+ $test_file = $this->getTestFile('text');
+
+ // Create a new node with the uploaded file.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $this->drupalGet('node/' . $nid . '/edit');
+
+ // Check that the default formatter is displaying with the file name.
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $default_output = theme('file_link', array('file' => $node_file));
+ $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
+
+ // Turn the "display" option off and check that the file is no longer displayed.
+ $edit = array($field_name . '[' . LANGUAGE_NONE . '][0][display]' => FALSE);
+ $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
+
+ $this->assertNoRaw($default_output, 'Field is hidden when "display" option is unchecked.');
+
+ }
+}
+
+/**
+ * Tests various validations.
+ */
+class FileFieldValidateTestCase extends FileFieldTestCase {
+ protected $field;
+ protected $node_type;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'File field validation tests',
+ 'description' => 'Tests validation functions such as file type, max file size, max size per node, and required.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests the required property on file fields.
+ */
+ function testRequired() {
+ $type_name = 'article';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $test_file = $this->getTestFile('text');
+
+ // Try to post a new node without uploading a file.
+ $langcode = LANGUAGE_NONE;
+ $edit = array("title" => $this->randomName());
+ $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+ $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), 'Node save failed when required file field was empty.');
+
+ // Create a new node with the uploaded file.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $this->assertTrue($nid !== FALSE, format_string('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name)));
+
+ $node = node_load($nid, NULL, TRUE);
+
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'File exists after uploading to the required field.');
+ $this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required field.');
+
+ // Try again with a multiple value field.
+ field_delete_field($field_name);
+ $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
+
+ // Try to post a new node without uploading a file in the multivalue field.
+ $edit = array('title' => $this->randomName());
+ $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+ $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), 'Node save failed when required multiple value file field was empty.');
+
+ // Create a new node with the uploaded file into the multivalue field.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'File exists after uploading to the required multiple value field.');
+ $this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required multipel value field.');
+
+ // Remove our file field.
+ field_delete_field($field_name);
+ }
+
+ /**
+ * Tests the max file size validator.
+ */
+ function testFileMaxSize() {
+ $type_name = 'article';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $small_file = $this->getTestFile('text', 131072); // 128KB.
+ $large_file = $this->getTestFile('text', 1310720); // 1.2MB
+
+ // Test uploading both a large and small file with different increments.
+ $sizes = array(
+ '1M' => 1048576,
+ '1024K' => 1048576,
+ '1048576' => 1048576,
+ );
+
+ foreach ($sizes as $max_filesize => $file_limit) {
+ // Set the max file upload size.
+ $this->updateFileField($field_name, $type_name, array('max_filesize' => $max_filesize));
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ // Create a new node with the small file, which should pass.
+ $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+ $this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+
+ // Check that uploading the large file fails (1M limit).
+ $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+ $error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
+ $this->assertRaw($error_message, format_string('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
+ }
+
+ // Turn off the max filesize.
+ $this->updateFileField($field_name, $type_name, array('max_filesize' => ''));
+
+ // Upload the big file successfully.
+ $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+ $this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+
+ // Remove our file field.
+ field_delete_field($field_name);
+ }
+
+ /**
+ * Tests file extension checking.
+ */
+ function testFileExtension() {
+ $type_name = 'article';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $test_file = $this->getTestFile('image');
+ list(, $test_file_extension) = explode('.', $test_file->filename);
+
+ // Disable extension checking.
+ $this->updateFileField($field_name, $type_name, array('file_extensions' => ''));
+
+ // Check that the file can be uploaded with no extension checking.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'File exists after uploading a file with no extension checking.');
+ $this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with no extension checking.');
+
+ // Enable extension checking for text files.
+ $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
+
+ // Check that the file with the wrong extension cannot be uploaded.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
+ $this->assertRaw($error_message, 'Node save failed when file uploaded with the wrong extension.');
+
+ // Enable extension checking for text and image files.
+ $this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
+
+ // Check that the file can be uploaded with extension checking.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertFileExists($node_file, 'File exists after uploading a file with extension checking.');
+ $this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with extension checking.');
+
+ // Remove our file field.
+ field_delete_field($field_name);
+ }
+}
+
+/**
+ * Tests that files are uploaded to proper locations.
+ */
+class FileFieldPathTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File field file path tests',
+ 'description' => 'Test that files are uploaded to the proper location with token support.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Tests the normal formatter display on node display.
+ */
+ function testUploadPath() {
+ $field_name = strtolower($this->randomName());
+ $type_name = 'article';
+ $field = $this->createFileField($field_name, $type_name);
+ $test_file = $this->getTestFile('text');
+
+ // Create a new node.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+ // Check that the file was uploaded to the file root.
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+ // Change the path to contain multiple subdirectories.
+ $field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
+
+ // Upload a new file into the subdirectories.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+ // Check that the file was uploaded into the subdirectory.
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ $this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+ // Check the path when used with tokens.
+ // Change the path to contain multiple token directories.
+ $field = $this->updateFileField($field_name, $type_name, array('file_directory' => '[current-user:uid]/[current-user:name]'));
+
+ // Upload a new file into the token subdirectories.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+ // Check that the file was uploaded into the subdirectory.
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ // Do token replacement using the same user which uploaded the file, not
+ // the user running the test case.
+ $data = array('user' => $this->admin_user);
+ $subdirectory = token_replace('[user:uid]/[user:name]', $data);
+ $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
+ }
+
+ /**
+ * Asserts that a file is uploaded to the right location.
+ *
+ * @param $expected_path
+ * The location where the file is expected to be uploaded. Duplicate file
+ * names to not need to be taken into account.
+ * @param $actual_path
+ * Where the file was actually uploaded.
+ * @param $message
+ * The message to display with this assertion.
+ */
+ function assertPathMatch($expected_path, $actual_path, $message) {
+ // Strip off the extension of the expected path to allow for _0, _1, etc.
+ // suffixes when the file hits a duplicate name.
+ $pos = strrpos($expected_path, '.');
+ $base_path = substr($expected_path, 0, $pos);
+ $extension = substr($expected_path, $pos + 1);
+
+ $result = preg_match('/' . preg_quote($base_path, '/') . '(_[0-9]+)?\.' . preg_quote($extension, '/') . '/', $actual_path);
+ $this->assertTrue($result, $message);
+ }
+}
+
+/**
+ * Tests the file token replacement in strings.
+ */
+class FileTokenReplaceTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File token replacement',
+ 'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
+ 'group' => 'File',
+ );
+ }
+
+ /**
+ * Creates a file, then tests the tokens generated from it.
+ */
+ function testFileTokenReplacement() {
+ global $language;
+ $url_options = array(
+ 'absolute' => TRUE,
+ 'language' => $language,
+ );
+
+ // Create file field.
+ $type_name = 'article';
+ $field_name = 'field_' . strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name);
+ $field = field_info_field($field_name);
+ $instance = field_info_instance('node', $field_name, $type_name);
+
+ $test_file = $this->getTestFile('text');
+ // Coping a file to test uploads with non-latin filenames.
+ $filename = drupal_dirname($test_file->uri) . '/текстовый файл.txt';
+ $test_file = file_copy($test_file, $filename);
+
+ // Create a new node with the uploaded file.
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+ // Load the node and the file.
+ $node = node_load($nid, NULL, TRUE);
+ $file = file_load($node->{$field_name}[LANGUAGE_NONE][0]['fid']);
+
+ // Generate and test sanitized tokens.
+ $tests = array();
+ $tests['[file:fid]'] = $file->fid;
+ $tests['[file:name]'] = check_plain($file->filename);
+ $tests['[file:path]'] = check_plain($file->uri);
+ $tests['[file:mime]'] = check_plain($file->filemime);
+ $tests['[file:size]'] = format_size($file->filesize);
+ $tests['[file:url]'] = check_plain(file_create_url($file->uri));
+ $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language->language);
+ $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language->language);
+ $tests['[file:owner]'] = check_plain(format_username($this->admin_user));
+ $tests['[file:owner:uid]'] = $file->uid;
+
+ // Test to make sure that we generated something for each token.
+ $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('file' => $file), array('language' => $language));
+ $this->assertEqual($output, $expected, format_string('Sanitized file token %token replaced.', array('%token' => $input)));
+ }
+
+ // Generate and test unsanitized tokens.
+ $tests['[file:name]'] = $file->filename;
+ $tests['[file:path]'] = $file->uri;
+ $tests['[file:mime]'] = $file->filemime;
+ $tests['[file:size]'] = format_size($file->filesize);
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
+ $this->assertEqual($output, $expected, format_string('Unsanitized file token %token replaced.', array('%token' => $input)));
+ }
+ }
+}
+
+/**
+ * Tests file access on private nodes.
+ */
+class FilePrivateTestCase extends FileFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Private file test',
+ 'description' => 'Uploads a test to a private node and checks access.',
+ 'group' => 'File',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('node_access_test', 'field_test'));
+ node_access_rebuild();
+ variable_set('node_access_test_private', TRUE);
+ }
+
+ /**
+ * Tests file access for file uploaded to a private node.
+ */
+ function testPrivateFile() {
+ // Use 'page' instead of 'article', so that the 'article' image field does
+ // not conflict with this test. If in the future the 'page' type gets its
+ // own default file or image field, this test can be made more robust by
+ // using a custom node type.
+ $type_name = 'page';
+ $field_name = strtolower($this->randomName());
+ $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
+
+ // Create a field with no view access - see field_test_field_access().
+ $no_access_field_name = 'field_no_view_access';
+ $this->createFileField($no_access_field_name, $type_name, array('uri_scheme' => 'private'));
+
+ $test_file = $this->getTestFile('text');
+ $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+ // Ensure the file can be downloaded.
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
+ $this->drupalLogOut();
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
+
+ // Test with the field that should deny access through field access.
+ $this->drupalLogin($this->admin_user);
+ $nid = $this->uploadNodeFile($test_file, $no_access_field_name, $type_name, TRUE, array('private' => TRUE));
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$no_access_field_name}[LANGUAGE_NONE][0];
+ // Ensure the file cannot be downloaded.
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission.');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.info b/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.info
new file mode 100644
index 0000000..4f94acf
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.info
@@ -0,0 +1,12 @@
+name = File test
+description = Provides hooks for testing File module functionality.
+package = Core
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.module b/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.module
new file mode 100644
index 0000000..f66c749
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/file/tests/file_module_test.module
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Provides File module pages for testing purposes.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function file_module_test_menu() {
+ $items = array();
+
+ $items['file/test'] = array(
+ 'title' => 'Managed file test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('file_module_test_form'),
+ 'access arguments' => array('access content'),
+ );
+
+ return $items;
+}
+
+/**
+ * Form constructor for testing a 'managed_file' element.
+ *
+ * @see file_module_test_form_submit()
+ * @ingroup forms
+ */
+function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FALSE, $default_fid = NULL) {
+ $form['#tree'] = (bool) $tree;
+
+ $form['nested']['file'] = array(
+ '#type' => 'managed_file',
+ '#title' => t('Managed file'),
+ '#upload_location' => 'public://test',
+ '#progress_message' => t('Please wait...'),
+ '#extended' => (bool) $extended,
+ '#size' => 13,
+ );
+ if ($default_fid) {
+ $form['nested']['file']['#default_value'] = $extended ? array('fid' => $default_fid) : $default_fid;
+ }
+
+ $form['textfield'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Type a value and ensure it stays'),
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+
+ return $form;
+}
+
+/**
+ * Form submission handler for file_module_test_form().
+ */
+function file_module_test_form_submit($form, &$form_state) {
+ if ($form['#tree']) {
+ $fid = $form['nested']['file']['#extended'] ? $form_state['values']['nested']['file']['fid'] : $form_state['values']['nested']['file'];
+ }
+ else {
+ $fid = $form['nested']['file']['#extended'] ? $form_state['values']['file']['fid'] : $form_state['values']['file'];
+ }
+ drupal_set_message(t('The file id is %fid.', array('%fid' => $fid)));
+}