filefield_paths.module in File (Field) Paths 8
Same filename and directory in other branches
Contains core functions for the File (Field) Paths module.
File
filefield_paths.moduleView source
<?php
/**
* @file
* Contains core functions for the File (Field) Paths module.
*/
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
use Symfony\Component\HttpFoundation\Response;
// @TODO - Turn this into a plugin.
require_once __DIR__ . '/filefield_paths.inc';
/**
* Implements hook_entity_base_field_info().
*/
function filefield_paths_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type
->id() == 'file') {
$fields['origname'] = BaseFieldDefinition::create('string')
->setLabel(t('Original filename'))
->setDescription(t('Original name of the file with no path components.'));
}
return $fields;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function filefield_paths_form_field_config_edit_form_alter(array &$form, FormStateInterface $form_state) {
/** @var Drupal\field\Entity\FieldConfig $field */
$field = $form_state
->getFormObject()
->getEntity();
$class = $field
->getClass();
if (class_exists($class) && new $class($field
->getItemDefinition()) instanceof FileFieldItemList) {
$entity_info = \Drupal::entityTypeManager()
->getDefinition($field
->getTargetEntityTypeId());
$settings = $field
->getThirdPartySettings('filefield_paths');
$form['settings']['filefield_paths'] = [
'#type' => 'container',
'#tree' => TRUE,
'#weight' => 2,
'#parents' => [
'third_party_settings',
'filefield_paths',
],
];
$form['settings']['filefield_paths']['enabled'] = [
'#type' => 'checkbox',
'#title' => t('Enable File (Field) Paths?'),
'#default_value' => isset($settings['enabled']) ? $settings['enabled'] : TRUE,
'#description' => t('File (Field) Paths provides advanced file path and naming options.'),
];
// Hide standard File directory field.
$form['settings']['file_directory']['#states'] = [
'visible' => [
':input[name="third_party_settings[filefield_paths][enabled]"]' => [
'checked' => FALSE,
],
],
];
// File (Field) Paths details element.
$form['settings']['filefield_paths']['details'] = [
'#type' => 'details',
'#title' => t('File (Field) Path settings'),
'#weight' => 3,
'#tree' => TRUE,
'#states' => [
'visible' => [
':input[name="third_party_settings[filefield_paths][enabled]"]' => [
'checked' => TRUE,
],
],
],
'#parents' => [
'third_party_settings',
'filefield_paths',
],
];
// Additional File (Field) Paths widget fields.
$settings_fields = \Drupal::moduleHandler()
->invokeAll('filefield_paths_field_settings', [
$form,
]);
foreach ($settings_fields as $name => $settings_field) {
// Attach widget fields.
$form['settings']['filefield_paths']['details'][$name] = [
'#type' => 'container',
];
// Attach widget field form elements.
if (isset($settings_field['form']) && is_array($settings_field['form'])) {
foreach (array_keys($settings_field['form']) as $delta => $key) {
$form['settings']['filefield_paths']['details'][$name][$key] = $settings_field['form'][$key];
if (\Drupal::moduleHandler()
->moduleExists('token')) {
$form['settings']['filefield_paths']['details'][$name][$key]['#element_validate'][] = 'token_element_validate';
$form['settings']['filefield_paths']['details'][$name][$key]['#token_types'] = [
'date',
'file',
];
$token_type = \Drupal::service('token.entity_mapper')
->getTokenTypeForEntityType($entity_info
->id());
if (!empty($token_type)) {
$form['settings']['filefield_paths']['details'][$name][$key]['#token_types'][] = $token_type;
}
}
// Fetch stored value from instance.
if (isset($settings[$name][$key])) {
$form['settings']['filefield_paths']['details'][$name][$key]['#default_value'] = $settings[$name][$key];
}
}
// Field options.
$form['settings']['filefield_paths']['details'][$name]['options'] = [
'#type' => 'details',
'#title' => t('@title options', [
'@title' => $settings_field['title'],
]),
'#weight' => 1,
'#attributes' => [
'class' => [
"{$name} cleanup",
],
],
];
// Cleanup slashes (/).
$form['settings']['filefield_paths']['details'][$name]['options']['slashes'] = [
'#type' => 'checkbox',
'#title' => t('Remove slashes (/) from tokens'),
'#default_value' => isset($settings[$name]['options']['slashes']) ? $settings[$name]['options']['slashes'] : FALSE,
'#description' => t('If checked, any slashes (/) in tokens will be removed from %title.', [
'%title' => $settings_field['title'],
]),
];
// Cleanup field with Pathauto module.
$form['settings']['filefield_paths']['details'][$name]['options']['pathauto'] = [
'#type' => 'checkbox',
'#title' => t('Cleanup using Pathauto'),
'#default_value' => isset($settings[$name]['options']['pathauto']) && \Drupal::moduleHandler()
->moduleExists('pathauto') ? $settings[$name]['options']['pathauto'] : FALSE,
'#description' => t('Cleanup %title using Pathauto.', [
'%title' => $settings_field['title'],
]),
'#disabled' => TRUE,
];
if (\Drupal::moduleHandler()
->moduleExists('pathauto')) {
unset($form['settings']['filefield_paths']['details'][$name]['options']['pathauto']['#disabled']);
$form['settings']['filefield_paths']['details'][$name]['options']['pathauto']['#description'] = t('Cleanup %title using <a href="@pathauto">Pathauto settings</a>.', [
'%title' => $settings_field['title'],
'@pathauto' => Url::fromRoute('pathauto.settings.form')
->toString(),
]);
}
// Transliterate field.
$form['settings']['filefield_paths']['details'][$name]['options']['transliterate'] = [
'#type' => 'checkbox',
'#title' => t('Transliterate'),
'#default_value' => isset($settings[$name]['options']['transliterate']) ? $settings[$name]['options']['transliterate'] : 0,
'#description' => t('Provides one-way string transliteration (romanization) and cleans the %title during upload by replacing unwanted characters.', [
'%title' => $settings_field['title'],
]),
];
// Replacement patterns for field.
if (\Drupal::moduleHandler()
->moduleExists('token')) {
$form['settings']['filefield_paths']['details']['token_tree'] = [
'#theme' => 'token_tree_link',
'#token_types' => [
'file',
],
'#weight' => 10,
];
if (!empty($token_type)) {
$form['settings']['filefield_paths']['details']['token_tree']['#token_types'][] = $token_type;
}
}
// Redirect.
$form['settings']['filefield_paths']['details']['redirect'] = [
'#type' => 'checkbox',
'#title' => t('Create Redirect'),
'#description' => t('Create a redirect to the new location when a previously uploaded file is moved.'),
'#default_value' => isset($settings['redirect']) ? $settings['redirect'] : FALSE,
'#weight' => 11,
];
if (!\Drupal::moduleHandler()
->moduleExists('redirect')) {
$form['settings']['filefield_paths']['details']['redirect']['#disabled'] = TRUE;
$form['settings']['filefield_paths']['details']['redirect']['#description'] .= '<br />' . t('Requires the <a href="https://drupal.org/project/redirect" target="_blank">Redirect</a> module.');
}
// Retroactive updates.
$form['settings']['filefield_paths']['details']['retroactive_update'] = [
'#type' => 'checkbox',
'#title' => t('Retroactive update'),
'#description' => t('Move and rename previously uploaded files.') . '<div>' . t('<strong class="warning">Warning:</strong> This feature should only be used on developmental servers or with extreme caution.') . '</div>',
'#weight' => 12,
];
// Active updating.
$form['settings']['filefield_paths']['details']['active_updating'] = [
'#type' => 'checkbox',
'#title' => t('Active updating'),
'#default_value' => isset($settings['active_updating']) ? $settings['active_updating'] : FALSE,
'#description' => t('Actively move and rename previously uploaded files as required.') . '<div>' . t('<strong class="warning">Warning:</strong> This feature should only be used on developmental servers or with extreme caution.') . '</div>',
'#weight' => 13,
];
}
}
$form['actions']['submit']['#submit'][] = 'filefield_paths_form_submit';
}
}
/**
* Implements hook_form_alter().
*/
function filefield_paths_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
// Force all File (Field) Paths uploads to go to the temporary file system
// prior to being processed.
if (isset($element['#type']) && $element['#type'] == 'managed_file') {
$settings = $context['items']
->getFieldDefinition()
->getThirdPartySettings('filefield_paths');
if (isset($settings['enabled']) && $settings['enabled']) {
$element['#upload_location'] = \Drupal::config('filefield_paths.settings')
->get('temp_location');
}
}
}
/**
* Submit callback for File (Field) Paths settings form.
*
* @param array $form
* TODO.
* @param FormStateInterface $form_state
* TODO.
*/
function filefield_paths_form_submit($form, FormStateInterface $form_state) {
$settings = $form_state
->getValue('third_party_settings')['filefield_paths'];
// Retroactive updates.
if ($settings['enabled'] && $settings['retroactive_update']) {
if (filefield_paths_batch_update($form_state
->getFormObject()
->getEntity())) {
$response = batch_process($form_state
->getRedirect());
if ($response instanceof Response) {
$response
->send();
}
}
}
}
/**
* Set batch process to update File (Field) Paths.
*
* @param FieldConfig $field_config
* TODO.
*
* @return bool
* TODO.
*/
function filefield_paths_batch_update(FieldConfig $field_config) {
$entity_info = \Drupal::entityTypeManager()
->getDefinition($field_config
->getTargetEntityTypeId());
$query = \Drupal::entityQuery($field_config
->getTargetEntityTypeId());
$result = $query
->condition($entity_info
->getKey('bundle'), $field_config
->getTargetBundle())
->condition("{$field_config->getName()}.target_id", '', '<>')
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT')
->execute();
// If there are no results, do not set a batch as there is nothing to process.
if (empty($result)) {
return FALSE;
}
// Create batch.
$batch = [
'title' => t('Updating File (Field) Paths'),
'operations' => [
[
'_filefield_paths_batch_update_process',
[
$result,
$field_config,
],
],
],
];
batch_set($batch);
return TRUE;
}
/**
* Batch callback for File (Field) Paths retroactive updates.
*
* @param array $objects
* TODO.
* @param FieldConfig $field_config
* TODO.
* @param array $context
* TODO.
*/
function _filefield_paths_batch_update_process($objects, FieldConfig $field_config, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = count($objects);
$context['sandbox']['objects'] = $objects;
}
/** @var Drupal\Core\Entity\ContentEntityStorageBase $entity_storage */
$entity_storage = \Drupal::entityTypeManager()
->getStorage($field_config
->getTargetEntityTypeId());
// Process nodes by groups of 5.
$count = min(5, count($context['sandbox']['objects']));
for ($i = 1; $i <= $count; $i++) {
// For each oid, load the object, update the files and save it.
$oid = array_shift($context['sandbox']['objects']);
$entity = $entity_storage
->load($oid);
// Enable active updating if it isn't already enabled.
$active_updating = $field_config
->getThirdPartySetting('filefield_paths', 'active_updating');
if (!$active_updating) {
$field_config
->setThirdPartySetting('filefield_paths', 'active_updating', TRUE);
$field_config
->save();
}
$entity->original = $entity;
filefield_paths_entity_update($entity);
// Restore active updating to it's previous state if necessary.
if (!$active_updating) {
$field_config
->setThirdPartySetting('filefield_paths', 'active_updating', $active_updating);
$field_config
->save();
}
// 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'];
}
}
/**
* Implements hook_entity_insert().
*/
function filefield_paths_entity_insert(EntityInterface $entity) {
filefield_paths_entity_update($entity);
}
/**
* Implements hook_entity_update().
*/
function filefield_paths_entity_update(EntityInterface $entity) {
if ($entity instanceof ContentEntityInterface) {
foreach ($entity
->getFields() as $field) {
if ($field instanceof FileFieldItemList) {
/** @var FieldConfig $definition */
$definition = $field
->getFieldDefinition();
// Ignore base fields.
if ($definition instanceof ThirdPartySettingsInterface) {
$settings = $definition
->getThirdPartySettings('filefield_paths');
if (isset($settings['enabled']) && $settings['enabled']) {
// Invoke hook_filefield_paths_process_file().
foreach (\Drupal::moduleHandler()
->getImplementations('filefield_paths_process_file') as $module) {
if (function_exists($function = "{$module}_filefield_paths_process_file")) {
$function($entity, $field, $settings);
}
}
}
}
}
}
}
}
/**
* Implements hook_file_presave().
*/
function filefield_paths_file_presave($file) {
// Store original filename in the database.
if ($file->origname
->isEmpty() && !$file->filename
->isEmpty()) {
$file->origname = $file->filename;
}
}
/**
* Creates a redirect for a moved File field.
*
* @param string $source
* The source file URL.
* @param string $path
* The moved file URL.
* @param \Drupal\Core\Language\Language $language
* The language of the source file.
*
* @deprecated in filefield_paths:1.0.0 and will be removed before
* filefield_paths:2.0.0. Use
* \Drupal\filefield_paths\RedirectInterface::createRedirect() instead.
*/
function _filefield_paths_create_redirect($source, $path, Language $language) {
@trigger_error('_filefield_paths_create_redirect() is deprecated in filefield_paths:1.0.0 and will be removed before filefield_paths:2.0.0. Use \\Drupal\\filefield_paths\\RedirectInterface::createRedirect() instead.', E_USER_DEPRECATED);
\Drupal::service('filefield_paths.redirect')
->createRedirect($source, $path, $language);
}
/**
* Process and cleanup strings.
*
* @param string $value
* Todo.
* @param string $data
* Todo.
* @param array $settings
* Todo.
*
* @return string
* Todo.
*/
function filefield_paths_process_string($value, $data, array $settings = []) {
$transliterate = $settings['transliterate'];
$pathauto = \Drupal::moduleHandler()
->moduleExists('pathauto') && isset($settings['pathauto']) && $settings['pathauto'] == TRUE;
$remove_slashes = !empty($settings['slashes']);
// If '/' is to be removed from tokens, token replacement need to happen after
// splitting the paths to subdirs, otherwise tokens containing '/' will be
// part of the final path.
if (!$remove_slashes) {
$value = \Drupal::service('token')
->replace($value, $data, [
'clear' => TRUE,
]);
}
$paths = explode('/', $value);
foreach ($paths as $i => &$path) {
if ($remove_slashes) {
$path = \Drupal::service('token')
->replace($path, $data, [
'clear' => TRUE,
]);
}
if ($pathauto == TRUE) {
if ('file_name' == $settings['context'] && count($paths) == $i + 1) {
$pathinfo = pathinfo($path);
$basename = \Drupal::service('file_system')
->basename($path);
$extension = preg_match('/\\.[^.]+$/', $basename, $matches) ? $matches[0] : NULL;
$pathinfo['filename'] = !is_null($extension) ? mb_substr($basename, 0, mb_strlen($basename) - mb_strlen($extension)) : $basename;
if ($remove_slashes) {
$path = '';
if (!empty($pathinfo['dirname']) && $pathinfo['dirname'] !== '.') {
$path .= $pathinfo['dirname'] . '/';
}
$path .= $pathinfo['filename'];
$path = \Drupal::service('pathauto.alias_cleaner')
->cleanstring($path);
if (!empty($pathinfo['extension'])) {
$path .= '.' . \Drupal::service('pathauto.alias_cleaner')
->cleanstring($pathinfo['extension']);
}
$path = str_replace('/', '', $path);
}
else {
$path = str_replace($pathinfo['filename'], \Drupal::service('pathauto.alias_cleaner')
->cleanstring($pathinfo['filename']), $path);
}
}
else {
$path = \Drupal::service('pathauto.alias_cleaner')
->cleanstring($path);
}
}
elseif ($remove_slashes) {
$path = str_replace('/', '', $path);
}
// Transliterate string.
if ($transliterate == TRUE) {
$path = \Drupal::service('transliteration')
->transliterate($path);
}
}
$value = implode('/', $paths);
// Ensure that there are no double-slash sequences due to empty token values.
$value = preg_replace('/\\/+/', '/', $value);
return $value;
}
Functions
Name | Description |
---|---|
filefield_paths_batch_update | Set batch process to update File (Field) Paths. |
filefield_paths_entity_base_field_info | Implements hook_entity_base_field_info(). |
filefield_paths_entity_insert | Implements hook_entity_insert(). |
filefield_paths_entity_update | Implements hook_entity_update(). |
filefield_paths_field_widget_form_alter | Implements hook_form_alter(). |
filefield_paths_file_presave | Implements hook_file_presave(). |
filefield_paths_form_field_config_edit_form_alter | Implements hook_form_FORM_ID_alter(). |
filefield_paths_form_submit | Submit callback for File (Field) Paths settings form. |
filefield_paths_process_string | Process and cleanup strings. |
_filefield_paths_batch_update_process | Batch callback for File (Field) Paths retroactive updates. |
_filefield_paths_create_redirect Deprecated | Creates a redirect for a moved File field. |