1 file.module file_managed_file_process($element, &$form_state, $form)

Render API callback: Expands the managed_file element type.

Expands the file type to include Upload and Remove buttons, as well as support for a default value.

This function is assigned as a #process callback in file_element_info().

File

core/modules/file/file.module, line 1313
Defines a "managed_file" Form API field and a "file" field for Field module.

Code

function file_managed_file_process($element, &$form_state, $form) {
  // This parent list is used as a prefix on the name attribute of sub-elements.
  $parents_prefix = implode('_', $element['#parents']);

  // Whether FID is a single file ID or multiple, convert to an array of file
  // IDs for consistent operation within this process function.
  $fids = !empty($element['#value']['fid']) ? (array) $element['#value']['fid'] : array();

  // Set some default element properties.
  $element['#progress_indicator'] = empty($element['#progress_indicator']) ? 'none' : $element['#progress_indicator'];
  $element['#files'] = !empty($fids) ? file_load_multiple($fids) : FALSE;
  $element['#tree'] = TRUE;

  // Token here is not generated with backdrop_token() as it does not need to
  // be session-based.
  $path = 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'];
  $token = backdrop_hmac_base64($path, backdrop_get_private_key() . backdrop_get_hash_salt());
  $path .= '/' . $token;

  $ajax_settings = array(
    'path' => $path,
    'wrapper' => $element['#id'] . '-ajax-wrapper',
    'effect' => 'none',
    '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' => $parents_prefix . '_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,
    '#attributes' => array('class' => array('file-upload-button')),
  );

  // 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;
  $element['remove_button'] = array(
    '#name' => $parents_prefix . '_remove_button',
    '#type' => 'submit',
    '#value' => $element['#multiple'] ? t('Remove selected') : t('Remove'),
    '#validate' => array(),
    '#submit' => array('file_managed_file_submit'),
    '#limit_validation_errors' => array($element['#parents']),
    '#ajax' => $ajax_settings,
    '#weight' => 1,
    '#attributes' => array('class' => array('file-remove-button')),
  );

  // Add a button that will be used to select existing files if enabled.
  $element['browse_button'] = array(
    '#name' => implode('_', $element['#parents']) . '_browse_button',
    '#type' => 'submit',
    '#value' => t('Select existing file'),
    '#validate' => array(),
    '#submit' => array(),
    '#limit_validation_errors' => array($element['#parents']),
    '#ajax' => array(
      'callback' => 'file_managed_file_browser_open',
      'progress' => array(
        'type' => 'throbber',
        'message' => NULL,
      ),
    ),
    '#weight' => -5,
    '#attributes' => array('class' => array('file-browse-button')),
    '#prefix' => '<span class="file-browse-text js-show"> ' . t('or') . ' ',
    '#suffix' => '</span>',
    '#access' => !empty($element['#browser_view']),
    // Attaching the assets from the Filter module makes sure that the image
    // browser works, even if no field on that form has a filter set.
    '#attached' => array(
      'library' => array(
        array('filter', 'filter'),
      ),
    ),
  );

  // If multiple upload, FID is a list of IDs concatenated by a comma. If
  // single-upload, it is a single ID.
  $element['fid'] = array(
    '#type' => 'hidden',
    '#value' => $element['#multiple'] ? implode(',', $fids) : reset($fids),
  );

  // Legacy server-side progress bar support. If not available, client-side
  // upload progress will be used instead. Client-side support does not require
  // a hidden field nor does it do AJAX requests to check on the progress,
  // so no [#ajax][progress][path] is necessary.
  // @todo: Remove in Backdrop 2.0.
  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.
    if ($implementation !== 'client') {
      $element['upload_button']['#ajax']['progress']['path'] = 'file/progress/' . $upload_progress_key;
    }
  }

  // The file upload field itself.
  $element['upload'] = array(
    '#name' => 'files[' . $parents_prefix . ']',
    '#type' => 'file',
    '#title' => t('Choose a file'),
    '#title_display' => 'invisible',
    '#size' => $element['#size'],
    '#multiple' => $element['#multiple'],
    '#theme_wrappers' => array(),
    '#weight' => -10,
  );

  // Indicate that $element['#title'] should be used as the HTML label for the
  // file upload field.
  $element['#label_for'] = backdrop_html_id('edit-' . implode('-', array_merge($element['#parents'], array('upload'))));

  if (!empty($fids) && $element['#files']) {
    foreach ($element['#files'] as $delta => $file) {
      if ($element['#multiple']) {
        $element['file_' . $delta]['selected'] = array(
          '#type' => 'checkbox',
          '#title' => theme('file_link', array('file' => $file)) . ' ',
        );
      }
      else {
        $element['file_' . $delta]['filename'] = array(
          '#type' => 'markup',
          '#markup' => theme('file_link', array(
            'file' => $file,
            'attributes' => array('class' => array('file-preview-link')),
          )) . ' ',
          '#weight' => -10,
        );
      }

      // Anonymous users who have uploaded a temporary file need a
      // non-session-based token added so file_managed_file_value() can check
      // that they have permission to use this file on subsequent submissions of
      // the same form (for example, after an Ajax upload or form validation
      // error).
      if (!$GLOBALS['user']->uid && $file->status != FILE_STATUS_PERMANENT) {
        $element['file_' . $delta]['fid_token'] = array(
          '#type' => 'hidden',
          '#value' => backdrop_hmac_base64('file-' . $file->fid, backdrop_get_private_key() . backdrop_get_hash_salt()),
        );
      }
    }
  }

  // Enable auto-uploading unless requested otherwise.
  if (!isset($element['#auto_upload']) || $element['##auto_upload']) {
    $element['upload']['#attributes']['data-file-auto-upload'] = '1';
  }

  // Add client-side validation of file extensions.
  if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
    $extension_array = array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0]));
    $extension_string = implode(',', $extension_array);
    $element['upload']['#attributes']['data-file-extensions'] = $extension_string;

    // Add support for mobile camera uploads if jpg is a valid extension.
    // Browser support for HTML5 extension validation is still weak at this
    // point, add more extensive extension support in the future.
    if (in_array('jpg', $extension_array)) {
      $element['upload']['#attributes']['accept'] = '*/*,capture=camera';
    }
  }

  // Prefix and suffix used for Ajax replacement.
  $element['#prefix'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
  $element['#suffix'] = '</div>';

  return $element;
}