class Attach in FileField Sources 8
A FileField source plugin to allow use of files within a server directory.
Plugin annotation
@FilefieldSource(
id = "attach",
name = @Translation("File attach from server directory"),
label = @Translation("File attach"),
description = @Translation("Select a file from a directory on the server."),
weight = 3
)
Hierarchy
- class \Drupal\filefield_sources\Plugin\FilefieldSource\Attach implements FilefieldSourceInterface
Expanded class hierarchy of Attach
6 string references to 'Attach'
- Attach::process in src/
Plugin/ FilefieldSource/ Attach.php - Process callback for file field source plugin.
- AttachSourceTest::assertCanAttachFile in tests/
src/ Functional/ AttachSourceTest.php - Check to see if can attach file.
- AttachSourceTest::assertCanNotAttachFile in tests/
src/ Functional/ AttachSourceTest.php - Check to see if can attach file.
- AttachSourceTest::fileCanBeUploadAndDeleted in tests/
src/ Functional/ AttachSourceTest.php - AttachSourceTest::testCopyFileFromAbsolutePath in tests/
src/ Functional/ AttachSourceTest.php - Tests copy file from absolute path.
File
- src/
Plugin/ FilefieldSource/ Attach.php, line 25
Namespace
Drupal\filefield_sources\Plugin\FilefieldSourceView source
class Attach implements FilefieldSourceInterface {
/**
* {@inheritdoc}
*/
public static function value(array &$element, &$input, FormStateInterface $form_state) {
if (!empty($input['filefield_attach']['filename'])) {
$instance = \Drupal::entityTypeManager()
->getStorage('field_config')
->load($element['#entity_type'] . '.' . $element['#bundle'] . '.' . $element['#field_name']);
$filepath = $input['filefield_attach']['filename'];
// Check that the destination is writable.
$directory = $element['#upload_location'];
$mode = Settings::get('file_chmod_directory', FileSystem::CHMOD_DIRECTORY);
// This first chmod check is for other systems such as S3, which don't
// work with file_prepare_directory().
if (!\Drupal::service('file_system')
->chmod($directory, $mode) && !\Drupal::service('file_system')
->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY)) {
\Drupal::logger('filefield_sources')
->log(E_NOTICE, 'File %file could not be copied, because the destination directory %destination is not configured correctly.', [
'%file' => $filepath,
'%destination' => \Drupal::service('file_system')
->realpath($directory),
]);
\Drupal::messenger()
->addError(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', [
'%file' => $filepath,
]), 'error');
return;
}
// Clean up the file name extensions and transliterate.
$original_filepath = $filepath;
$new_filepath = filefield_sources_clean_filename($filepath, $instance
->getSetting('file_extensions'));
rename($filepath, $new_filepath);
$filepath = $new_filepath;
// Run all the normal validations, minus file size restrictions.
$validators = $element['#upload_validators'];
if (isset($validators['file_validate_size'])) {
unset($validators['file_validate_size']);
}
// Save the file to the new location.
if ($file = filefield_sources_save_file($filepath, $validators, $directory)) {
if (!in_array($file
->id(), $input['fids'])) {
$input['fids'][] = $file
->id();
}
// Delete the original file if "moving" the file instead of copying.
if ($element['#filefield_sources_settings']['source_attach']['attach_mode'] !== FILEFIELD_SOURCE_ATTACH_MODE_COPY) {
@unlink($filepath);
}
}
// Restore the original file name if the file still exists.
if (file_exists($filepath) && $filepath != $original_filepath) {
rename($filepath, $original_filepath);
}
$input['filefield_attach']['filename'] = '';
}
}
/**
* {@inheritdoc}
*/
public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
$settings = $element['#filefield_sources_settings']['source_attach'];
$field_name = $element['#field_name'];
$instance = \Drupal::entityTypeManager()
->getStorage('field_config')
->load($element['#entity_type'] . '.' . $element['#bundle'] . '.' . $field_name);
$element['filefield_attach'] = [
'#weight' => 100.5,
'#theme' => 'filefield_sources_element',
'#source_id' => 'attach',
// Required for proper theming.
'#filefield_source' => TRUE,
];
$path = static::getDirectory($settings);
$options = static::getAttachOptions($path, $instance
->getSetting('file_extensions'));
// If we have built this element before, append the list of options that we
// had previously. This allows files to be deleted after copying them and
// still be considered a valid option during the validation and submit.
$triggering_element = $form_state
->getTriggeringElement();
$property = [
'filefield_sources',
$field_name,
'attach_options',
];
if (!isset($triggering_element) && $form_state
->has($property)) {
$attach_options = $form_state
->get($property);
$options = $options + $attach_options;
}
else {
$form_state
->set([
'filefield_sources',
$field_name,
'attach_options',
], $options);
}
$description = t('This method may be used to attach files that exceed the file size limit. Files may be attached from the %directory directory on the server, usually uploaded through FTP.', [
'%directory' => realpath($path),
]);
// Error messages.
if ($options === FALSE || empty($settings['path'])) {
$attach_message = t('A file attach directory could not be located.');
$attach_description = t('Please check your settings for the %field field.', [
'%field' => $instance
->getLabel(),
]);
}
elseif (!count($options)) {
$attach_message = t('There currently are no files to attach.');
$attach_description = $description;
}
if (isset($attach_message)) {
$element['filefield_attach']['attach_message'] = [
'#markup' => $attach_message,
];
$element['filefield_attach']['#description'] = $attach_description;
}
else {
$validators = $element['#upload_validators'];
if (isset($validators['file_validate_size'])) {
unset($validators['file_validate_size']);
}
$description .= '<br />' . filefield_sources_element_validation_help($validators);
$element['filefield_attach']['filename'] = [
'#type' => 'select',
'#options' => $options,
];
$element['filefield_attach']['#description'] = $description;
}
$class = '\\Drupal\\file\\Element\\ManagedFile';
$ajax_settings = [
'callback' => [
$class,
'uploadAjaxCallback',
],
'options' => [
'query' => [
'element_parents' => implode('/', $element['#array_parents']),
],
],
'wrapper' => $element['upload_button']['#ajax']['wrapper'],
'effect' => 'fade',
];
$element['filefield_attach']['attach'] = [
'#name' => implode('_', $element['#parents']) . '_attach',
'#type' => 'submit',
'#value' => t('Attach'),
'#validate' => [],
'#submit' => [
'filefield_sources_field_submit',
],
'#limit_validation_errors' => [
$element['#parents'],
],
'#ajax' => $ajax_settings,
];
return $element;
}
/**
* Theme the output of the attach element.
*/
public static function element($variables) {
$element = $variables['element'];
if (isset($element['attach_message'])) {
$output = $element['attach_message']['#markup'];
}
elseif (isset($element['filename'])) {
// Get rendered options.
$options = form_select_options($element['filename']);
$option_output = '';
foreach ($options as $key => $value) {
$option_output .= '<option value="' . $value["value"] . '">' . $value["label"] . '</option>';
}
// Get rendered select.
$size = !empty($element['filename']['#size']) ? ' size="' . $element['filename']['#size'] . '"' : '';
$element['filename']['#attributes']['class'][] = 'form-select';
$multiple = !empty($element['#multiple']);
$output = '<select name="' . $element['filename']['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . new Attribute($element['filename']['#attributes']) . ' id="' . $element['filename']['#id'] . '" ' . $size . '>' . $option_output . '</select>';
}
$output .= \Drupal::service('renderer')
->render($element['attach']);
$element['#children'] = $output;
$element['#theme_wrappers'] = [
'form_element',
];
return '<div class="filefield-source filefield-source-attach clear-block">' . \Drupal::service('renderer')
->render($element) . '</div>';
}
/**
* Get directory from settings.
*
* @param array $settings
* Attach source's settings.
* @param object $account
* User to replace token.
*
* @return string
* Path that contains files to attach.
*/
protected static function getDirectory(array $settings, $account = NULL) {
$account = isset($account) ? $account : \Drupal::currentUser();
$path = $settings['path'];
$absolute = !empty($settings['absolute']);
// Replace user level tokens.
// Node level tokens require a lot of complexity like temporary storage
// locations when values don't exist. See the filefield_paths module.
if (\Drupal::moduleHandler()
->moduleExists('token')) {
$token = \Drupal::token();
$path = $token
->replace($path, [
'user' => $account,
]);
}
return $absolute ? $path : \Drupal::config('system.file')
->get('default_scheme') . '://' . $path;
}
/**
* Get attach options.
*
* @param string $path
* Path to scan files.
* @param string $extensions
* Path to scan files.
*
* @return array
* List of options.
*/
protected static function getAttachOptions($path, $extensions = FALSE) {
if (!\Drupal::service('file_system')
->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY)) {
\Drupal::messenger()
->addError(t('Specified file attach path %path must exist or be writable.', [
'%path' => $path,
]), 'error');
return FALSE;
}
$options = [];
$pattern = !empty($extensions) ? '/\\.(' . strtr($extensions, ' ', '|') . ')$/' : '/.*/';
$files = \Drupal::service('file_system')
->scanDirectory($path, $pattern);
if (count($files)) {
$options = [
'' => t('-- Select file --'),
];
foreach ($files as $file) {
$options[$file->uri] = str_replace($path . '/', '', $file->uri);
}
natcasesort($options);
}
return $options;
}
/**
* Implements hook_filefield_source_settings().
*/
public static function settings(WidgetInterface $plugin) {
$settings = $plugin
->getThirdPartySetting('filefield_sources', 'filefield_sources', [
'source_attach' => [
'path' => FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH,
'absolute' => FILEFIELD_SOURCE_ATTACH_RELATIVE,
'attach_mode' => FILEFIELD_SOURCE_ATTACH_MODE_MOVE,
],
]);
$return['source_attach'] = [
'#title' => t('File attach settings'),
'#type' => 'details',
'#description' => t('File attach allows for selecting a file from a directory on the server, commonly used in combination with FTP.') . ' <strong>' . t('This file source will ignore file size checking when used.') . '</strong>',
'#element_validate' => [
[
get_called_class(),
'filePathValidate',
],
],
'#weight' => 3,
];
$return['source_attach']['path'] = [
'#type' => 'textfield',
'#title' => t('File attach path'),
'#default_value' => $settings['source_attach']['path'],
'#size' => 60,
'#maxlength' => 128,
'#description' => t('The directory within the <em>File attach location</em> that will contain attachable files.'),
];
if (\Drupal::moduleHandler()
->moduleExists('token')) {
$return['source_attach']['tokens'] = [
'#theme' => 'token_tree',
'#token_types' => [
'user',
],
];
}
$return['source_attach']['absolute'] = [
'#type' => 'radios',
'#title' => t('File attach location'),
'#options' => [
FILEFIELD_SOURCE_ATTACH_RELATIVE => t('Within the files directory'),
FILEFIELD_SOURCE_ATTACH_ABSOLUTE => t('Absolute server path'),
],
'#default_value' => $settings['source_attach']['absolute'],
'#description' => t('The <em>File attach path</em> may be with the files directory (%file_directory) or from the root of your server. If an absolute path is used and it does not start with a "/" your path will be relative to your site directory: %realpath.', [
'%file_directory' => \Drupal::service('file_system')
->realpath(\Drupal::config('system.file')
->get('default_scheme') . '://'),
'%realpath' => realpath('./'),
]),
];
$return['source_attach']['attach_mode'] = [
'#type' => 'radios',
'#title' => t('Attach method'),
'#options' => [
FILEFIELD_SOURCE_ATTACH_MODE_MOVE => t('Move the file directly to the final location'),
FILEFIELD_SOURCE_ATTACH_MODE_COPY => t('Leave a copy of the file in the attach directory'),
],
'#default_value' => isset($settings['source_attach']['attach_mode']) ? $settings['source_attach']['attach_mode'] : 'move',
];
return $return;
}
/**
* Validate file path.
*
* @param array $element
* Form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state.
* @param array $complete_form
* Complete form.
*/
public static function filePathValidate(array &$element, FormStateInterface $form_state, array &$complete_form) {
$parents = $element['#parents'];
array_pop($parents);
$input_exists = FALSE;
// Get input of the whole parent element.
$input = NestedArray::getValue($form_state
->getValues(), $parents, $input_exists);
if ($input_exists) {
// Only validate if this source is enabled.
if (!$input['sources']['attach']) {
return;
}
// Strip slashes from the end of the file path.
$filepath = rtrim($element['path']['#value'], '\\/');
$form_state
->setValueForElement($element['path'], $filepath);
$filepath = static::getDirectory($input['source_attach']);
// Check that the directory exists and is writable.
if (!\Drupal::service('file_system')
->prepareDirectory($filepath, FileSystemInterface::CREATE_DIRECTORY)) {
$form_state
->setError($element['path'], t('Specified file attach path %path must exist or be writable.', [
'%path' => $filepath,
]));
}
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Attach:: |
public static | function | Theme the output of the attach element. | |
Attach:: |
public static | function | Validate file path. | |
Attach:: |
protected static | function | Get attach options. | |
Attach:: |
protected static | function | Get directory from settings. | |
Attach:: |
public static | function |
Process callback for file field source plugin. Overrides FilefieldSourceInterface:: |
|
Attach:: |
public static | function | Implements hook_filefield_source_settings(). | |
Attach:: |
public static | function |
Value callback for file field source plugin. Overrides FilefieldSourceInterface:: |