You are here

file_admin.module in File admin 7

Enhances file administration by adding published, promote, and sticky fields.

File

file_admin.module
View source
<?php

/**
 * @file
 * Enhances file administration by adding published, promote, and sticky fields.
 */

/**
 * File is not published.
 */
define('FILE_NOT_PUBLISHED', 0);

/**
 * File is published.
 */
define('FILE_PUBLISHED', 1);

/**
 * File is not promoted.
 */
define('FILE_NOT_PROMOTED', 0);

/**
 * File is promoted.
 */
define('FILE_PROMOTED', 1);

/**
 * File is not sticky at top of a list.
 */
define('FILE_NOT_STICKY', 0);

/**
 * File is sticky at top of a list.
 */
define('FILE_STICKY', 1);

/**
 * Implements hook_permission().
 */
function file_admin_permission() {
  $perms = array(
    'view any unpublished files' => array(
      'title' => t('View any unpublished file details'),
      'description' => t('For viewing file details, not for downloading files.'),
    ),
    'view own unpublished files' => array(
      'title' => t('View own unpublished file details'),
      'description' => t('For viewing file details, not for downloading files.'),
    ),
  );
  return $perms;
}

/**
 * Implements hook_entity_presave().
 *
 * Set created timestamp and file admin settings when entity is created.
 */
function file_admin_entity_presave($entity, $type) {
  if ($type == 'file' && !isset($entity->fid)) {
    $entity->created = REQUEST_TIME;
    $file_options = variable_get('file_admin_file_options_' . $entity->type, array(
      'published' => 'published',
    ));
    foreach (array(
      'published',
      'promote',
      'sticky',
    ) as $key) {
      $entity->{$key} = (int) (isset($file_options[$key]) && $file_options[$key]);
    }
  }
}

/**
 * Implements hook_file_entity_access().
 *
 * Restrict view access to file entities based on published status.
 */
function file_admin_file_entity_access($op, $file, $account) {

  // Deny view access to unpublished files unless the user has "bypass file
  // access" permission or has "view own published files" and is the author of
  // the file.
  if ($op == 'view' && $file->published == FILE_NOT_PUBLISHED && !user_access('view any unpublished files', $account) && !(user_access('view own unpublished files', $account) && $account->uid == $file->uid && $account->uid != 0)) {
    return FILE_ENTITY_ACCESS_DENY;
  }
  return FILE_ENTITY_ACCESS_IGNORE;
}

/**
 * Implements hook_form_FORM_ID_alter() for file_entity_admin_file.
 *
 * Add a published field to the file administration form.
 */
function file_admin_form_file_entity_admin_file_alter(&$form, &$form_state) {

  // Only act if we're in a list display mode.
  // TODO: find a less awkward way to determine the mode.
  if (is_null(arg(3)) && !empty($form['admin']['files']['#options'])) {
    $header =& $form['admin']['files']['#header'];
    $operations_header = array_pop($header);
    $header['published'] = array(
      'data' => t('Published'),
      'field' => 'f.published',
    );
    $header['operations'] = $operations_header;
    $files = db_query('SELECT fid, published FROM {file_managed} WHERE fid IN (:fids)', array(
      ':fids' => array_keys($form['admin']['files']['#options']),
    ))
      ->fetchAllAssoc('fid');
    foreach ($form['admin']['files']['#options'] as $fid => &$option) {
      $operations_row = array_pop($option);
      $option['published'] = $files[$fid]->published ? t('published') : t('not published');
      $option['operations'] = $operations_row;
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for file_entity_edit.
 *
 * Add file settings defaults.
 */
function file_admin_form_file_entity_file_type_form_alter(&$form, &$form_state) {
  $file_type = $form['#file_type']->type;
  $form['additional_settings'] = array(
    '#type' => 'vertical_tabs',
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'file_admin') . '/file_admin.js',
      ),
    ),
  );
  $form['submission'] = array(
    '#type' => 'fieldset',
    '#title' => t('Submission form settings'),
    '#collapsible' => TRUE,
    '#group' => 'additional_settings',
  );
  $form['submission']['help'] = array(
    '#type' => 'textarea',
    '#title' => t('Explanation or submission guidelines'),
    '#default_value' => variable_get('file_admin_help_' . $file_type, ''),
    '#description' => t('This text will be displayed at the top of the page when creating or editing files of this type.'),
  );
  $form['workflow'] = array(
    '#type' => 'fieldset',
    '#title' => t('Publishing options'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'additional_settings',
  );
  $form['workflow']['file_options'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Default options'),
    '#default_value' => variable_get('file_admin_file_options_' . $file_type, array(
      'published' => 'published',
    )),
    '#options' => array(
      'published' => t('Published'),
      'promote' => t('Promoted'),
      'sticky' => t('Sticky at top of lists'),
    ),
    '#description' => t('Users with the <em>Administer files</em> permission will be able to override these options.'),
  );
  $form['display'] = array(
    '#type' => 'fieldset',
    '#title' => t('Display settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'additional_settings',
  );
  $form['display']['file_submitted'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display owner and date information.'),
    '#default_value' => variable_get('file_submitted_' . $file_type, FALSE),
    '#description' => t('Owner username and upload date will be displayed.'),
  );
  $form['submit']['#weight'] = 10;
  $form['#submit'][] = 'file_admin_file_entity_file_type_form_submit';
}

/**
 * Submit handler for file_entity_file_type_form.
 */
function file_admin_file_entity_file_type_form_submit($form, &$form_state) {
  $file_type = $form['#file_type']->type;
  foreach (array(
    'help',
    'file_options',
  ) as $variable) {
    variable_set('file_admin_' . $variable . '_' . $file_type, $form_state['values'][$variable]);
  }
  $file_type = $form['#file_type']->type;
  variable_set('file_submitted_' . $file_type, $form_state['values']['file_submitted']);
}

/**
 * Implements hook_views_api().
 */
function file_admin_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'file_admin') . '/includes',
  );
}

/**
 * Helper to build the Workflow fieldset.
 *
 * @see file_admin_form_file_entity_edit_alter()
 * @see file_admin_form_file_entity_add_upload_alter()
 * @see file_entity_edit()
 */
function _file_admin_add_file_admin_fieldset(&$form, $form_state, $file) {
  $file->date = format_date($file->created, 'custom', 'Y-m-d H:i:s O');
  if (!isset($form['additional_settings'])) {
    $form['additional_settings'] = array(
      '#type' => 'vertical_tabs',
      '#attached' => array(
        'js' => array(
          drupal_get_path('module', 'file_admin') . '/file_admin.js',
        ),
      ),
    );
  }

  // Basic file information.
  // This element is just a value so it is not even sent to the client.
  $form['created'] = array(
    '#type' => 'value',
    '#value' => isset($file->created) ? $file->created : NULL,
  );

  // In some cases this fieldset will be provided by file_entity.
  // @see file_entity_edit().
  if (!isset($form['user'])) {

    // File user information for administrators.
    $form['user'] = array(
      '#type' => 'fieldset',
      '#access' => user_access('administer files'),
      '#title' => t('User information'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#group' => 'additional_settings',
      '#attributes' => array(
        'class' => array(
          'file-form-user',
        ),
      ),
      '#attached' => array(
        'js' => array(
          drupal_get_path('module', 'file_entity') . '/file_entity.js',
          array(
            'type' => 'setting',
            'data' => array(
              'anonymous' => variable_get('anonymous', t('Anonymous')),
            ),
          ),
        ),
      ),
      '#weight' => 90,
    );
    $form['user']['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Associated with'),
      '#maxlength' => 60,
      '#autocomplete_path' => 'user/autocomplete',
      '#default_value' => !empty($file->uid) ? user_load($file->uid)->name : '',
      '#weight' => -1,
      '#description' => t('Leave blank for %anonymous.', array(
        '%anonymous' => variable_get('anonymous', t('Anonymous')),
      )),
    );
  }
  $form['user']['date'] = array(
    '#type' => 'textfield',
    '#title' => t('Posted on'),
    '#maxlength' => 25,
    '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array(
      '%time' => !empty($file->date) ? date_format(date_create($file->date), 'Y-m-d H:i:s O') : format_date($file->created, 'custom', 'Y-m-d H:i:s O'),
      '%timezone' => !empty($file->date) ? date_format(date_create($file->date), 'O') : format_date($file->created, 'custom', 'O'),
    )),
    '#default_value' => !empty($file->date) ? $file->date : '',
  );

  // File options for administrators.
  $form['options'] = array(
    '#type' => 'fieldset',
    '#access' => user_access('administer files'),
    '#title' => t('Workflow'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#group' => 'additional_settings',
    '#attributes' => array(
      'class' => array(
        'file-entity-form-options',
      ),
    ),
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'file_admin') . '/file_admin.js',
      ),
    ),
    '#weight' => 95,
  );
  $form['options']['published'] = array(
    '#type' => 'checkbox',
    '#title' => t('Published'),
    '#default_value' => $file->published,
  );
  $form['options']['promote'] = array(
    '#type' => 'checkbox',
    '#title' => t('Promoted'),
    '#default_value' => $file->promote,
  );
  $form['options']['sticky'] = array(
    '#type' => 'checkbox',
    '#title' => t('Sticky at the top of lists'),
    '#default_value' => $file->sticky,
  );
  if (isset($form['actions']['submit']['#validate'])) {
    $form['actions']['submit']['#validate'][] = 'file_admin_file_entity_edit_validate';
  }
  else {
    $form['#validate'][] = 'file_admin_file_entity_edit_validate';
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @see file_entity_edit()
 */
function file_admin_form_file_entity_edit_alter(&$form, &$form_state) {
  $file = file_load($form['fid']['#value']);
  if ($help = variable_get('file_admin_help_' . $form['#bundle'], '')) {
    $form['#prefix'] = (isset($form['#prefix']) ? $form['#prefix'] : '') . '<p>' . filter_xss_admin($help) . '</p>';
  }
  _file_admin_add_file_admin_fieldset($form, $form_state, $file);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @see file_entity_add_upload()
 */
function file_admin_form_file_entity_add_upload_alter(&$form, &$form_state, $form_id) {
  if (isset($form['#step']) && $form['#step'] == 4) {

    // Default to the settings for the 'image' file type.
    $file_admin_image_options = variable_get('file_admin_file_options_image', array(
      'published' => 'published',
    ));
    $file = new stdClass();
    $file->created = time();
    foreach (array(
      'published',
      'promote',
      'sticky',
    ) as $property) {
      $file->{$property} = !empty($file_admin_image_options[$property]);
    }
    _file_admin_add_file_admin_fieldset($form, $form_state, $file);
  }
}

/**
 * Validate handler for file_entity_edit form.
 *
 * Prepare additional fields' data for saving to the file entity. Actual saving
 * is handled by file_entity_edit_submit().
 */
function file_admin_file_entity_edit_validate($form, &$form_state) {

  // Validate the "posted on" field.
  if (!empty($form_state['values']['date']) && strtotime($form_state['values']['date']) === FALSE) {
    form_set_error('date', t('You have to specify a valid date.'));
  }
  $created = !empty($form_state['values']['date']) ? strtotime($form_state['values']['date']) : time();
  form_set_value($form['created'], $created, $form_state);
}

/**
 * Implement hook_file_operations().
 */
function file_admin_file_operations() {
  $operations = array(
    'publish' => array(
      'label' => t('Publish'),
      'callback' => 'file_admin_mass_update',
      'callback arguments' => array(
        'updates' => array(
          'published' => FILE_PUBLISHED,
        ),
      ),
    ),
    'unpublish' => array(
      'label' => t('Unpublish'),
      'callback' => 'file_admin_mass_update',
      'callback arguments' => array(
        'updates' => array(
          'published' => FILE_NOT_PUBLISHED,
        ),
      ),
    ),
    'promote' => array(
      'label' => t('Promote selected files'),
      'callback' => 'file_admin_mass_update',
      'callback arguments' => array(
        'updates' => array(
          'published' => FILE_PUBLISHED,
          'promote' => FILE_PROMOTED,
        ),
      ),
    ),
    'demote' => array(
      'label' => t('Demote selected files'),
      'callback' => 'file_admin_mass_update',
      'callback arguments' => array(
        'updates' => array(
          'promote' => FILE_NOT_PROMOTED,
        ),
      ),
    ),
  );
  return $operations;
}

/**
 * Implements hook_action_info().
 */
function file_admin_action_info() {
  return array(
    'file_admin_publish_action' => array(
      'type' => 'file',
      'label' => t('Publish file'),
      'configurable' => FALSE,
      'triggers' => array(
        'entity_presave',
      ),
    ),
    'file_admin_unpublish_action' => array(
      'type' => 'file',
      'label' => t('Unpublish file'),
      'configurable' => FALSE,
      'triggers' => array(
        'entity_presave',
      ),
    ),
  );
}

/**
 * Sets the published field of a file to 1 (published).
 *
 * @param file_entity $file_entity
 *   A file entity object.
 * @param array $context
 *   (optional) Array of additional information about what triggered the action.
 *   Not used for this action.
 *
 * @ingroup actions
 */
function file_admin_publish_action($file_entity, $context = array()) {
  file_admin_mass_update(array(
    'fid' => $file_entity->fid,
  ), array(
    'published' => FILE_PUBLISHED,
  ));
  if (!empty($file_entity->title)) {
    $title = $file_entity->title;
  }
  else {
    $title = $file_entity->filename;
  }
  watchdog('action', 'Set @type %title to published.', array(
    '@type' => file_get_type($file_entity),
    '%title' => $title,
  ));
}

/**
 * Sets the published field of a file to 0 (unpublished).
 *
 * @param file_entity $file_entity
 *   A file entity object.
 * @param array $context
 *   (optional) Array of additional information about what triggered the action.
 *   Not used for this action.
 *
 * @ingroup actions
 */
function file_admin_unpublish_action($file_entity, $context = array()) {
  file_admin_mass_update(array(
    'fid' => $file_entity->fid,
  ), array(
    'published' => FILE_NOT_PUBLISHED,
  ));
  if (!empty($file_entity->title)) {
    $title = $file_entity->title;
  }
  else {
    $title = $file_entity->filename;
  }
  watchdog('action', 'Set @type %title to unpublished.', array(
    '@type' => file_get_type($file_entity),
    '%title' => $title,
  ));
}

/**
 * Make mass update of files, changing all files in the $files array
 * to update them with the field values in $updates.
 *
 * IMPORTANT NOTE: This function is intended to work when called
 * from a form submit handler. Calling it outside of the form submission
 * process may not work correctly.
 *
 * @param array $files
 *   Array of file fids to update.
 * @param array $updates
 *   Array of key/value pairs with file field names and the
 *   value to update that field to.
 */
function file_admin_mass_update($files, $updates) {

  // We use batch processing to prevent timeout when updating a large number
  // of files.
  if (count($files) > 10) {
    $batch = array(
      'operations' => array(
        array(
          '_file_mass_update_batch_process',
          array(
            $files,
            $updates,
          ),
        ),
      ),
      'finished' => '_file_mass_update_batch_finished',
      'title' => t('Processing'),
      // We use a single multi-pass operation, so the default
      // 'Remaining x of y operations' message will be confusing here.
      'progress_message' => '',
      'error_message' => t('The update has encountered an error.'),
      // The operations do not live in the .module file, so we need to
      // tell the batch engine which file to load before calling them.
      'file' => drupal_get_path('module', 'file_admin') . '/file.admin.inc',
    );
    batch_set($batch);
  }
  else {
    foreach ($files as $fid) {
      _file_mass_update_helper($fid, $updates);
    }
    drupal_set_message(t('The update has been performed.'));
  }
}

/**
 * File Mass Update - helper function.
 */
function _file_mass_update_helper($fid, $updates) {
  $file = file_load($fid, NULL, TRUE);

  // For efficiency manually save the original file before applying any changes.
  $file->original = clone $file;
  foreach ($updates as $name => $value) {
    $file->{$name} = $value;
  }
  file_save($file);
  return $file;
}

/**
 * File Mass Update Batch operation
 */
function _file_mass_update_batch_process($files, $updates, &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = count($files);
    $context['sandbox']['files'] = $files;
  }

  // Process files by groups of 5.
  $count = min(5, count($context['sandbox']['files']));
  for ($i = 1; $i <= $count; $i++) {

    // For each fid, load the file, reset the values, and save it.
    $fid = array_shift($context['sandbox']['files']);
    $file = _file_mass_update_helper($fid, $updates);

    // Store result for post-processing in the finished callback.
    $context['results'][] = l($file->title, 'file/' . $file->fid);

    // Update our progress information.
    $context['sandbox']['progress']++;
  }

  // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

/**
 * File Mass Update Batch 'finished' callback.
 */
function _file_mass_update_batch_finished($success, $results, $operations) {
  if ($success) {
    drupal_set_message(t('The update has been performed.'));
  }
  else {
    drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
    $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:');
    $message .= theme('item_list', array(
      'items' => $results,
    ));
    drupal_set_message($message);
  }
}

Functions

Namesort descending Description
file_admin_action_info Implements hook_action_info().
file_admin_entity_presave Implements hook_entity_presave().
file_admin_file_entity_access Implements hook_file_entity_access().
file_admin_file_entity_edit_validate Validate handler for file_entity_edit form.
file_admin_file_entity_file_type_form_submit Submit handler for file_entity_file_type_form.
file_admin_file_operations Implement hook_file_operations().
file_admin_form_file_entity_add_upload_alter Implements hook_form_FORM_ID_alter().
file_admin_form_file_entity_admin_file_alter Implements hook_form_FORM_ID_alter() for file_entity_admin_file.
file_admin_form_file_entity_edit_alter Implements hook_form_FORM_ID_alter().
file_admin_form_file_entity_file_type_form_alter Implements hook_form_FORM_ID_alter() for file_entity_edit.
file_admin_mass_update Make mass update of files, changing all files in the $files array to update them with the field values in $updates.
file_admin_permission Implements hook_permission().
file_admin_publish_action Sets the published field of a file to 1 (published).
file_admin_unpublish_action Sets the published field of a file to 0 (unpublished).
file_admin_views_api Implements hook_views_api().
_file_admin_add_file_admin_fieldset Helper to build the Workflow fieldset.
_file_mass_update_batch_finished File Mass Update Batch 'finished' callback.
_file_mass_update_batch_process File Mass Update Batch operation
_file_mass_update_helper File Mass Update - helper function.

Constants

Namesort descending Description
FILE_NOT_PROMOTED File is not promoted.
FILE_NOT_PUBLISHED File is not published.
FILE_NOT_STICKY File is not sticky at top of a list.
FILE_PROMOTED File is promoted.
FILE_PUBLISHED File is published.
FILE_STICKY File is sticky at top of a list.