You are here

filefield_sources.module in FileField Sources 8

Same filename and directory in other branches
  1. 6 filefield_sources.module
  2. 7 filefield_sources.module

Extend FileField to allow files from multiple sources.

File

filefield_sources.module
View source
<?php

/**
 * @file
 * Extend FileField to allow files from multiple sources.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Field\WidgetInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Render\Element;
use Drupal\imce\Imce;
use Drupal\file\Entity\File;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\File\FileSystemInterface;
const FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH = 'file_attach';
const FILEFIELD_SOURCE_ATTACH_RELATIVE = 0;
const FILEFIELD_SOURCE_ATTACH_ABSOLUTE = 1;
const FILEFIELD_SOURCE_ATTACH_MODE_MOVE = 'move';
const FILEFIELD_SOURCE_ATTACH_MODE_COPY = 'copy';
const FILEFIELD_SOURCE_REFERENCE_HINT_TEXT = 'example.png [fid:123]';
const FILEFIELD_SOURCE_REMOTE_HINT_TEXT = 'http://example.com/files/file.png';
const FILEFIELD_SOURCE_REFERENCE_MATCH_STARTS_WITH = '0';
const FILEFIELD_SOURCE_REFERENCE_MATCH_CONTAINS = '1';
const FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_NO = '0';
const FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_YES = '1';

/**
 * Implements hook_element_info_alter().
 */
function filefield_sources_element_info_alter(&$type) {
  if (isset($type['managed_file'])) {
    $type['managed_file']['#process'][] = 'filefield_sources_field_process';
    $type['managed_file']['#pre_render'][] = '\\Drupal\\filefield_sources\\FilefieldSourcesPreRenderCallback::preRender';
    $type['managed_file']['#element_validate'][] = 'filefield_sources_field_validate';
    $type['managed_file']['#file_value_callbacks'][] = 'filefield_sources_field_value';
  }
}

/**
 * Implements hook_theme().
 */
function filefield_sources_theme() {
  $theme = [];
  $theme['filefield_sources_element'] = [
    'render element' => 'element',
  ];
  $theme['filefield_sources_list'] = [
    'variables' => [
      'id' => NULL,
      'sources' => NULL,
    ],
  ];
  return $theme;
}

/**
 * Implements hook_field_widget_third_party_settings_form().
 *
 * Add file field sources settings form to supported field widget forms.
 *
 * @see \Drupal\field_ui\FormDisplayOverview
 */
function filefield_sources_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, $form, FormStateInterface $form_state) {
  $element = [];
  if (in_array($plugin
    ->getPluginId(), \Drupal::moduleHandler()
    ->invokeAll('filefield_sources_widgets'))) {
    $element = filefield_sources_form($plugin, $form_state);
  }
  return $element;
}

/**
 * Implements hook_field_widget_settings_summary_alter().
 *
 * Add file field sources information to the field widget settings summary.
 *
 * @see \Drupal\field_ui\FormDisplayOverview
 */
function filefield_sources_field_widget_settings_summary_alter(&$summary, $context) {
  $plugin = $context['widget'];
  if (in_array($plugin
    ->getPluginId(), \Drupal::moduleHandler()
    ->invokeAll('filefield_sources_widgets'))) {
    $settings = $plugin
      ->getThirdPartySetting('filefield_sources', 'filefield_sources');
    $enabled_sources = _filefield_sources_enabled($settings);
    $summary[] = t('File field sources:') . ' ' . implode(', ', array_keys($enabled_sources));
  }
}

/**
 * Implements hook_field_widget_form_alter().
 *
 * Add file field sources widget's settings to element.
 */
function filefield_sources_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
  $plugin = $context['widget'];
  if (in_array($plugin
    ->getPluginId(), \Drupal::moduleHandler()
    ->invokeAll('filefield_sources_widgets'))) {
    $element['#filefield_sources_settings'] = $plugin
      ->getThirdPartySetting('filefield_sources', 'filefield_sources');

    // Bundle is missing in element.
    $items = $context['items'];
    $element['#bundle'] = $items
      ->getEntity()
      ->bundle();
  }
}

/**
 * Implements hook_filefield_sources_widgets().
 *
 * This returns a list of widgets that are compatible with FileField Sources.
 */
function filefield_sources_filefield_sources_widgets() {
  return [
    'file_generic',
    'image_image',
  ];
}

/**
 * Configuration form for editing FileField Sources settings for a widget.
 */
function filefield_sources_form($plugin, FormStateInterface $form_state) {
  $settings = $plugin
    ->getThirdPartySetting('filefield_sources', 'filefield_sources');

  // Backward compatibility: auto-enable 'upload'.
  $enabled = _filefield_sources_enabled($settings);
  $form['filefield_sources'] = [
    '#type' => 'details',
    '#title' => t('File sources'),
    '#weight' => 20,
  ];
  $sources = filefield_sources_list();
  $form['filefield_sources']['sources'] = [
    '#type' => 'checkboxes',
    '#title' => t('Enabled sources'),
    '#options' => $sources,
    '#default_value' => $enabled,
    '#description' => t('Select the available locations from which this widget may select files.'),
  ];
  $params = [
    $plugin,
  ];
  $form['filefield_sources'] = array_merge($form['filefield_sources'], filefield_sources_invoke_all('settings', $params));
  return $form;
}

/**
 * A #process callback to extend the filefield_widget element type.
 *
 * Add the central JavaScript and CSS files that allow switching between
 * different sources. Third-party modules can also add to the list of sources
 * by implementing hook_filefield_sources_info().
 */
function filefield_sources_field_process(&$element, FormStateInterface $form_state, &$complete_form) {
  static $js_added;

  // Check if we are processing file field sources.
  if (!isset($element['#filefield_sources_settings'])) {
    return $element;
  }

  // Do all processing as needed by each source.
  $sources = filefield_sources_info();
  $settings = $element['#filefield_sources_settings'];
  $enabled_sources = _filefield_sources_enabled($settings);
  $context = [
    'enabled_sources' => &$enabled_sources,
    'element' => $element,
    'form_state' => $form_state,
  ];

  // Allow other modules to alter the sources.
  \Drupal::moduleHandler()
    ->alter('filefield_sources_sources', $sources, $context);
  foreach ($sources as $source_name => $source) {
    if (empty($enabled_sources[$source_name])) {
      unset($sources[$source_name]);
    }
    elseif (isset($source['class'])) {
      $callback = [
        $source['class'],
        'process',
      ];
      if (is_callable($callback)) {
        $element = call_user_func_array($callback, [
          &$element,
          $form_state,
          &$complete_form,
        ]);
      }
    }
  }
  $element['#filefield_sources'] = $sources;

  // Exit out if not adding any sources.
  if (empty($sources)) {
    return $element;
  }

  // Hide default 'upload' type?
  if (!isset($enabled_sources['upload'])) {
    foreach ([
      'upload_button',
      'upload',
    ] as $field) {
      if (isset($element[$field])) {
        $element[$field]['#access'] = FALSE;
      }
    }
  }

  // Add class to upload button.
  $element['upload_button']['#attributes']['class'][] = 'upload-button';
  $element['#attached']['library'][] = 'filefield_sources/drupal.filefield_sources';

  // Check the element for hint text that might need to be added.
  foreach (Element::children($element) as $key) {
    if (isset($element[$key]['#filefield_sources_hint_text']) && !isset($js_added[$key])) {
      $type = str_replace('filefield_', '', $key);
      $element['#attached']['drupalSettings']['fileFieldSources'][$type] = [
        'hintText' => $element[$key]['#filefield_sources_hint_text'],
      ];
      $js_added[$key] = TRUE;
    }
  }

  // Adjust the Ajax settings so that on upload and remove of any individual
  // file, the entire group of file fields is updated together.
  // Clone of Drupal\file\Plugin\Field\FieldWidget\FileWidget::process().
  if ($element['#cardinality'] != 1) {
    $parents = array_slice($element['#array_parents'], 0, -1);
    $new_options = [
      'query' => [
        'element_parents' => implode('/', $parents),
      ],
    ];
    $field_element = NestedArray::getValue($complete_form, $parents);
    $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
    foreach (Element::children($element) as $key) {
      foreach (Element::children($element[$key]) as $subkey) {
        if (isset($element[$key][$subkey]['#ajax'])) {
          $element[$key][$subkey]['#ajax']['options'] = $new_options;
          $element[$key][$subkey]['#ajax']['wrapper'] = $new_wrapper;
          $element[$key][$subkey]['#limit_validation_errors'] = [
            array_slice($element['#array_parents'], 0, -2),
          ];
        }
      }
    }
    unset($element['#prefix'], $element['#suffix']);
  }

  // Add the list of sources to the element for toggling between sources.
  if (empty($element['fids']['#value'])) {
    if (count($enabled_sources) > 1) {
      $element['filefield_sources_list'] = [
        '#theme' => 'filefield_sources_list',
        '#id' => $element['#id'],
        '#sources' => $sources,
        '#weight' => -20,
      ];
    }
  }
  return $element;
}

/**
 * An #element_validate function to run source validations.
 */
function filefield_sources_field_validate(&$element, FormStateInterface $form_state, &$complete_form) {

  // Do all processing as needed by each source.
  $sources = filefield_sources_info();
  foreach ($sources as $source) {
    if (!isset($source['class'])) {
      continue;
    }
    $callback = [
      $source['class'],
      'validate',
    ];
    if (is_callable($callback)) {
      call_user_func_array($callback, [
        $element,
        $form_state,
        $complete_form,
      ]);
    }
  }
}

/**
 * Form submission handler for all FileField Source buttons.
 *
 * Clone of \Drupal\file\Plugin\Field\FieldWidget\FileWidget::submit(), with
 * a few changes:
 *   - Submit button is one level down compare to 'Upload' source's submit
 *     button.
 *   - Replace static in static::getWidgetState and static::setWidgetState by
 *     WidgetBase.
 *   - Rebuild the form after all.
 */
function filefield_sources_field_submit(&$form, FormStateInterface $form_state) {

  // During the form rebuild, formElement() will create field item widget
  // elements using re-indexed deltas, so clear out FormState::$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.
  $button = $form_state
    ->getTriggeringElement();
  $parents = array_slice($button['#parents'], 0, -3);
  NestedArray::setValue($form_state
    ->getUserInput(), $parents, NULL);

  // Go one level up in the form, to the widgets container.
  $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
  $field_name = $element['#field_name'];
  $parents = $element['#field_parents'];
  $submitted_values = NestedArray::getValue($form_state
    ->getValues(), array_slice($button['#parents'], 0, -3));
  foreach ($submitted_values as $delta => $submitted_value) {
    if (empty($submitted_value['fids'])) {
      unset($submitted_values[$delta]);
    }
  }

  // If there are more files uploaded via the same widget, we have to separate
  // them, as we display each file in it's own widget.
  $new_values = [];
  foreach ($submitted_values as $delta => $submitted_value) {
    if (is_array($submitted_value['fids'])) {
      foreach ($submitted_value['fids'] as $fid) {
        $new_value = $submitted_value;
        $new_value['fids'] = [
          $fid,
        ];
        $new_values[] = $new_value;
      }
    }
    else {
      $new_value = $submitted_value;
    }
  }

  // Re-index deltas after removing empty items.
  $submitted_values = array_values($new_values);

  // Update form_state values.
  NestedArray::setValue($form_state
    ->getValues(), array_slice($button['#parents'], 0, -3), $submitted_values);

  // Update items.
  $field_state = WidgetBase::getWidgetState($parents, $field_name, $form_state);
  $field_state['items'] = $submitted_values;
  WidgetBase::setWidgetState($parents, $field_name, $form_state, $field_state);

  // We need to rebuild the form, so that uploaded file can be displayed.
  $form_state
    ->setRebuild();
}

/**
 * A #filefield_value_callback to run source value callbacks.
 */
function filefield_sources_field_value(&$element, &$input, FormStateInterface $form_state) {

  // Do all processing as needed by each source.
  $sources = filefield_sources_info();
  foreach ($sources as $source) {
    if (isset($source['class'])) {
      $callback = [
        $source['class'],
        'value',
      ];
      if (is_callable($callback)) {
        call_user_func_array($callback, [
          &$element,
          &$input,
          $form_state,
        ]);
      }
    }
  }
}

/**
 * Call all FileField Source hooks stored in the available include files.
 */
function filefield_sources_invoke_all($method, &$params) {
  $return = [];
  foreach (\Drupal::service('filefield_sources')
    ->getDefinitions() as $definition) {
    if (!isset($definition['class'])) {
      continue;
    }

    // Get routes defined by each plugin.
    $callback = [
      $definition['class'],
      $method,
    ];
    if (is_callable($callback)) {
      $result = call_user_func_array($callback, $params);
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result);
      }
      elseif (isset($result)) {
        $return[] = $result;
      }
    }
  }
  return $return;
}

/**
 * Load hook_filefield_sources_info() data from all modules.
 */
function filefield_sources_info($include_default = TRUE) {
  $info = \Drupal::service('filefield_sources')
    ->getDefinitions();
  if (isset($info['imce']) && !Imce::access()) {
    unset($info['imce']);
  }
  if ($include_default) {
    $info['upload'] = [
      'name' => t('Upload (default)'),
      'label' => t('Upload'),
      'description' => t('Upload a file from your computer.'),
      'weight' => -10,
    ];
  }
  uasort($info, '_filefield_sources_sort');
  return $info;
}

/**
 * Create a list of FileField Sources by name, suitable for a select list.
 */
function filefield_sources_list($include_default = TRUE) {
  $info = filefield_sources_info($include_default);
  $list = [];
  foreach ($info as $key => $source) {
    $list[$key] = $source['name'];
  }
  return $list;
}

/**
 * Save a file into the database after validating it.
 *
 * This function is identical to the core function file_save_upload() except
 * that it accepts an input file path instead of an input file source name.
 *
 * @see file_save_upload()
 */
function filefield_sources_save_file($filepath, $validators = [], $destination = FALSE, $replace = FileSystemInterface::EXISTS_RENAME) {
  $user = \Drupal::currentUser();

  // Begin building file object.
  $file = File::create([
    'uri' => $filepath,
    'uid' => $user
      ->id(),
    'status' => FileSystemInterface::EXISTS_RENAME,
  ]);
  $file
    ->setFilename(trim(basename($filepath), '.'));
  $file
    ->setMimeType(\Drupal::service('file.mime_type.guesser')
    ->guess($file
    ->getFilename()));
  $file
    ->setSize(filesize($filepath));
  $extensions = '';
  if (isset($validators['file_validate_extensions'])) {
    if (isset($validators['file_validate_extensions'][0])) {

      // Build the list of non-munged extensions if the caller provided them.
      $extensions = $validators['file_validate_extensions'][0];
    }
    else {

      // If 'file_validate_extensions' is set and the list is empty then the
      // caller wants to allow any extension. In this case we have to remove the
      // validator or else it will reject all extensions.
      unset($validators['file_validate_extensions']);
    }
  }
  else {

    // No validator was provided, so add one using the default list.
    // Build a default non-munged safe list for file_munge_filename().
    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
    $validators['file_validate_extensions'] = [];
    $validators['file_validate_extensions'][0] = $extensions;
  }
  if (!empty($extensions)) {

    // Munge the filename to protect against possible malicious extension hiding
    // within an unknown file type (ie: filename.html.foo).
    $file
      ->setFilename(file_munge_filename($file
      ->getFilename(), $extensions));
  }

  // Rename potentially executable files, to help prevent exploits (i.e. will
  // rename filename.php.foo and filename.php to filename.php.foo.txt and
  // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
  // evaluates to TRUE.
  if (!\Drupal::config('system.file')
    ->get('allow_insecure_uploads') && preg_match('/\\.(php|pl|py|cgi|asp|js)(\\.|$)/i', $file
    ->getFilename()) && substr($file
    ->getFilename(), -4) != '.txt') {
    $file
      ->setMimeType('text/plain');
    $file
      ->setFileUri($file
      ->getFileUri() . '.txt');
    $file
      ->setFilename($file
      ->getFilename() . '.txt');

    // The .txt extension may not be in the allowed list of extensions. We have
    // to add it here or else the file upload will fail.
    if (!empty($extensions)) {
      $validators['file_validate_extensions'][0] .= ' txt';
      \Drupal::messenger()
        ->addStatus(t('For security reasons, your upload has been renamed to %filename.', [
        '%filename' => $file
          ->getFilename(),
      ]));
    }
  }

  // If the destination is not provided, use the temporary directory.
  if (empty($destination)) {
    $destination = 'temporary://';
  }

  // Assert that the destination contains a valid stream.
  $destination_scheme = \Drupal::service('stream_wrapper_manager')
    ->getScheme($destination);
  if (!$destination_scheme || !\Drupal::service('stream_wrapper_manager')
    ->isValidScheme($destination_scheme)) {
    \Drupal::messenger()
      ->addError(t('The file could not be uploaded, because the destination %destination is invalid.', [
      '%destination' => $destination,
    ]), 'error');
    return FALSE;
  }

  // A URI may already have a trailing slash or look like "public://".
  if (substr($destination, -1) != '/') {
    $destination .= '/';
  }

  // Ensure the destination is writable.
  \Drupal::service('file_system')
    ->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY);

  // Check if this is actually the same file being "attached" to a file record.
  // If so, it acts as a file replace, except no file is actually moved.
  $reuse_file = $destination . $file
    ->getFilename() === $file
    ->getFileUri();
  if ($reuse_file) {
    $replace = FileSystemInterface::EXISTS_REPLACE;
  }
  $file->destination = \Drupal::service('file_system')
    ->getDestinationFilename($destination . $file
    ->getFilename(), $replace);

  // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
  // there's an existing file so we need to bail.
  if ($file->destination === FALSE) {
    \Drupal::messenger()
      ->addError(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', [
      '%source' => $file
        ->getFilename(),
      '%directory' => $destination,
    ]), 'error');
    return FALSE;
  }

  // Add in our check of the the file name length.
  $validators['file_validate_name_length'] = [];

  // Call the validation functions specified by this function's caller.
  $errors = file_validate($file, $validators);

  // Check for errors.
  if (!empty($errors)) {
    $message = t('The specified file %name could not be uploaded.', [
      '%name' => $file
        ->getFilename(),
    ]);
    if (count($errors) > 1) {
      $message .= theme('item_list', [
        'items' => $errors,
      ]);
    }
    else {
      $message .= ' ' . array_pop($errors);
    }
    \Drupal::messenger()
      ->addError($message, 'error');
    return FALSE;
  }

  // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
  // directory. This overcomes open_basedir restrictions for future file
  // operations.
  $file
    ->setFileUri($file->destination);
  if (!$reuse_file && !\Drupal::service('file_system')
    ->copy($filepath, $file
    ->getFileUri(), $replace)) {
    \Drupal::messenger()
      ->addError(t('File upload error. Could not move uploaded file.'), 'error');
    \Drupal::logger('filefield_sources')
      ->log(E_NOTICE, 'Upload error. Could not move uploaded file %file to destination %destination.', [
      '%file' => $file
        ->getFilename(),
      '%destination' => $file
        ->getFileUri(),
    ]);
    return FALSE;
  }

  // Set the permissions on the new file.
  \Drupal::service('file_system')
    ->chmod($file
    ->getFileUri());

  // If we are replacing an existing file re-use its database record.
  if ($replace == FileSystemInterface::EXISTS_REPLACE) {
    $existing_files = File::loadMultiple([], [
      'uri' => $file
        ->getFileUri(),
    ]);
    if (count($existing_files)) {
      $existing = reset($existing_files);
      $file
        ->setOriginalId($existing
        ->id());
    }
  }

  // If we made it this far it's safe to record this file in the database.
  $file
    ->save();
  return $file;
}

/**
 * Clean up the file name, munging extensions and transliterating.
 *
 * @param string $filepath
 *   A string containing a file name or full path. Only the file name will
 *   actually be modified.
 *
 * @return string
 *   A file path with a cleaned-up file name.
 */
function filefield_sources_clean_filename($filepath, $extensions) {
  $filename = basename($filepath);
  $language = \Drupal::languageManager()
    ->getCurrentLanguage()
    ->getId();
  $filename = \Drupal::transliteration()
    ->transliterate($filename, $language);

  // Because this transfer mechanism does not use file_save_upload(), we need
  // to manually munge the filename to prevent dangerous extensions.
  // See file_save_upload().
  if (empty($extensions)) {
    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
  }
  $filename = file_munge_filename($filename, $extensions);
  $directory = \Drupal::service('file_system')
    ->dirname($filepath);
  return ($directory != '.' ? $directory . '/' : '') . $filename;
}

/**
 * Theme the display of the source element.
 */
function template_preprocess_filefield_sources_element(&$variables) {
  $element = $variables['element'];
  $source_id = $element['#source_id'];
  $method = isset($element['#method']) ? $element['#method'] : 'element';
  $extra_variables = isset($element['#variables']) ? $element['#variables'] : [];
  $sources = filefield_sources_info();
  if (isset($sources[$source_id]['class'])) {
    $callback = [
      $sources[$source_id]['class'],
      $method,
    ];
    if (is_callable($callback)) {
      $variables = array_merge($variables, $extra_variables);
      $variables['rendered_element'] = call_user_func_array($callback, [
        $variables,
      ]);
    }
  }
  return '';
}

/**
 * Validate a file based on the $element['#upload_validators'] property.
 */
function filefield_sources_element_validate($element, $file, FormStateInterface $form_state) {
  $validators = $element['#upload_validators'];
  $errors = [];

  // Since this frequently is used to reference existing files, check that
  // they exist first in addition to the normal validations.
  if (!file_exists($file
    ->getFileUri())) {
    $errors[] = t('The file does not exist.');
  }
  else {
    foreach ($validators as $function => $args) {

      // Add the $file variable to the list of arguments and pass it by
      // reference (required for PHP 5.3 and higher).
      array_unshift($args, NULL);
      $args[0] =& $file;
      $errors = array_merge($errors, call_user_func_array($function, $args));
    }
  }

  // Check for validation errors.
  if (!empty($errors)) {
    $message = t('The selected file %name could not be referenced.', [
      '%name' => $file->filename,
    ]);
    if (count($errors) > 1) {
      $message .= '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
    }
    else {
      $message .= ' ' . array_pop($errors);
    }
    $form_state
      ->setError($element, $message);
    return 0;
  }
  return 1;
}

/**
 * Generate help text based on the $element['#upload_validators'] property.
 */
function filefield_sources_element_validation_help($validators) {
  $desc = [];
  foreach ($validators as $callback => $arguments) {
    $help_func = $callback . '_help';
    if (function_exists($help_func)) {
      $desc[] = call_user_func_array($help_func, $arguments);
    }
  }
  return empty($desc) ? '' : implode('<br />', $desc);
}

/**
 * Custom sort function for ordering sources.
 */
function _filefield_sources_sort($a, $b) {
  $a = (array) $a + [
    'weight' => 0,
    'label' => '',
  ];
  $b = (array) $b + [
    'weight' => 0,
    'label' => '',
  ];
  return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : strnatcasecmp($a['label'], $b['label']));
}

/**
 * Helper to return enabled sources for a field.
 *
 * This provides backward compatibility for 'upload' type.
 *
 * @see http://drupal.org/node/932994
 */
function _filefield_sources_enabled($settings) {
  if (!isset($settings['sources']['upload'])) {
    $settings['sources']['upload'] = 'upload';
  }
  $enabled = array_keys(array_filter($settings['sources']));
  asort($enabled);
  return array_combine($enabled, $enabled);
}

// @todo Remove once https://www.drupal.org/node/1808132 is finished.
if (!function_exists('module_get_weight')) {

  /**
   * Gets weight of a particular module.
   *
   * @param string $module
   *   The name of the module (without the .module extension).
   *
   * @return int
   *   The configured weight of the module.
   *
   * @throws InvalidArgumentException
   *   Thrown in case the given module is not installed in the system.
   */
  function module_get_weight($module) {
    $weight = \Drupal::config('core.extension')
      ->get("module.{$module}");
    if ($weight !== NULL) {
      return (int) $weight;
    }
    $weight = \Drupal::config('core.extension')
      ->get("disabled.module.{$module}");
    if ($weight !== NULL) {
      return (int) $weight;
    }
    throw new InvalidArgumentException(new FormattableMarkup('The module %module is not installed.', [
      '%module' => $module,
    ]));
  }
}

/**
 * Check for CURL extension enabled.
 */
function filefield_sources_curl_enabled() {
  return function_exists('curl_version');
}

Functions

Namesort descending Description
filefield_sources_clean_filename Clean up the file name, munging extensions and transliterating.
filefield_sources_curl_enabled Check for CURL extension enabled.
filefield_sources_element_info_alter Implements hook_element_info_alter().
filefield_sources_element_validate Validate a file based on the $element['#upload_validators'] property.
filefield_sources_element_validation_help Generate help text based on the $element['#upload_validators'] property.
filefield_sources_field_process A #process callback to extend the filefield_widget element type.
filefield_sources_field_submit Form submission handler for all FileField Source buttons.
filefield_sources_field_validate An #element_validate function to run source validations.
filefield_sources_field_value A #filefield_value_callback to run source value callbacks.
filefield_sources_field_widget_form_alter Implements hook_field_widget_form_alter().
filefield_sources_field_widget_settings_summary_alter Implements hook_field_widget_settings_summary_alter().
filefield_sources_field_widget_third_party_settings_form Implements hook_field_widget_third_party_settings_form().
filefield_sources_filefield_sources_widgets Implements hook_filefield_sources_widgets().
filefield_sources_form Configuration form for editing FileField Sources settings for a widget.
filefield_sources_info Load hook_filefield_sources_info() data from all modules.
filefield_sources_invoke_all Call all FileField Source hooks stored in the available include files.
filefield_sources_list Create a list of FileField Sources by name, suitable for a select list.
filefield_sources_save_file Save a file into the database after validating it.
filefield_sources_theme Implements hook_theme().
template_preprocess_filefield_sources_element Theme the display of the source element.
_filefield_sources_enabled Helper to return enabled sources for a field.
_filefield_sources_sort Custom sort function for ordering sources.

Constants