class PlUploadFile in Plupload integration 2.0.x
Same name and namespace in other branches
- 8 src/Element/PlUploadFile.php \Drupal\plupload\Element\PlUploadFile
Provides a PLUpload widget for uploading and saving files.
Plugin annotation
@FormElement("plupload");
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\plupload\Element\PlUploadFile
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of PlUploadFile
See also
ManagedFile::getInfo()
1 #type use of PlUploadFile
- PluploadTestForm::buildForm in plupload_test/
src/ PluploadTestForm.php - Form constructor.
File
- src/
Element/ PlUploadFile.php, line 17
Namespace
Drupal\plupload\ElementView source
class PlUploadFile extends FormElement {
/**
* {@inheritdoc}
*
* Note: based on plupload_element_info().
*/
public function getInfo() {
$class = get_class($this);
return [
'#input' => TRUE,
'#attributes' => [
'class' => [
'plupload-element',
],
],
'#theme_wrappers' => [
'form_element',
],
'#theme' => 'container',
'#attached' => [
'library' => [
'plupload/plupload',
],
],
'#process' => [
[
$class,
'processPlUploadFile',
],
],
'#element_validate' => [
[
$class,
'validatePlUploadFile',
],
],
'#pre_render' => [
[
$class,
'preRenderPlUploadFile',
],
],
];
}
/**
* {@inheritdoc}
*
* @see ManagedFile::valueCallback
* @see file_managed_file_save_upload()
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
$id = $element['#id'];
// If a unique identifier added with '--', we need to exclude it.
if (preg_match('/(.*)(--[0-9A-Za-z]+)$/', $id, $reg)) {
$id = $reg[1];
}
// Seems cleaner to use something like this, but it's empty.
// $request_files = \Drupal::request()->files;
$input = $form_state
->getUserInput();
$files = [];
foreach ($input as $key => $value) {
if (preg_match('/' . $id . '(--([0-9A-Za-z_-]+))?_([0-9]+)_(tmpname|name|status)/', $key, $reg)) {
$i = $reg[3];
$key = $reg[4];
// Only add the keys we expect.
if (!in_array($key, [
'tmpname',
'name',
'status',
])) {
continue;
}
// Munge the submitted file names for security.
//
// Similar munging is normally done by file_save_upload(), but submit
// handlers for forms containing plupload elements can't use
// file_save_upload(), for reasons discussed in plupload_test_submit().
// So we have to do this for them.
//
// Note that we do the munging here in the value callback function
// (rather than during form validation or elsewhere) because we want to
// actually modify the submitted values rather than reject them
// outright; file names that require munging can be innocent and do
// not necessarily indicate an attempted exploit. Actual validation of
// the file names is performed later, in plupload_element_validate().
if (in_array($key, [
'tmpname',
'name',
])) {
// Find the whitelist of extensions to use when munging. If there are
// none, we'll be adding default ones in plupload_element_process(),
// so use those here.
if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
$extensions = $element['#upload_validators']['file_validate_extensions'][0];
}
else {
$validators = _plupload_default_upload_validators();
$extensions = $validators['file_validate_extensions'][0];
}
$value = file_munge_filename($value, $extensions, FALSE);
// To prevent directory traversal issues, make sure the file name does
// not contain any directory components in it. (This more properly
// belongs in the form validation step, but it's simpler to do here so
// that we don't have to deal with the temporary file names during
// form validation and can just focus on the final file name.)
//
// This step is necessary since this module allows a large amount of
// flexibility in where its files are placed (for example, they could
// be intended for public://subdirectory rather than public://, and we
// don't want an attacker to be able to get them back into the top
// level of public:// in that case).
$value = rtrim(\Drupal::service('file_system')
->basename($value), '.');
// Based on the same feture from file_save_upload().
if (!\Drupal::config('system.file')
->get('allow_insecure_uploads') && preg_match('/\\.(php|pl|py|cgi|asp|js)(\\.|$)/i', $value) && substr($value, -4) != '.txt') {
$value .= '.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)) {
$element['#upload_validators']['file_validate_extensions'][0] .= ' txt';
\Drupal::messenger()
->addMessage(t('For security reasons, your upload has been renamed to %filename.', [
'%filename' => $value,
]));
}
}
}
// The temporary file name has to be processed further so it matches
// what was used when the file was written; see
// plupload_handle_uploads().
if ($key == 'tmpname') {
$value = _plupload_fix_temporary_filename($value);
// We also define an extra key 'tmppath' which is useful so that
// submit handlers do not need to know which directory plupload
// stored the temporary files in before trying to copy them.
$files[$i]['tmppath'] = \Drupal::config('plupload.settings')
->get('temporary_uri') . $value;
}
elseif ($key == 'name') {
$value = \Drupal::service('transliteration')
->transliterate($value);
}
// Store the final value in the array we will return.
$files[$i][$key] = $value;
}
}
return $files;
}
/**
* 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.
*
* Note: based on plupload_element_process().
*/
public static function processPlUploadFile(&$element, FormStateInterface $form_state, &$complete_form) {
// Start session if not there yet. We need session if we want security
// tokens to work properly.
$session_manager = \Drupal::service('session_manager');
if (!$session_manager
->isStarted()) {
$session_manager
->start();
}
if (!isset($element['#upload_validators'])) {
$element['#upload_validators'] = [];
}
$element['#upload_validators'] += _plupload_default_upload_validators();
return $element;
}
/**
* Render API callback: Hides display of the upload or remove controls.
*
* Upload controls are hidden when a file is already uploaded. Remove controls
* are hidden when there is no file attached. Controls are hidden here instead
* of in \Drupal\file\Element\ManagedFile::processManagedFile(), because
* #access for these buttons depends on the managed_file element's #value. See
* the documentation of \Drupal\Core\Form\FormBuilderInterface::doBuildForm()
* for more detailed information about the relationship between #process,
* #value, and #access.
*
* Because #access is set here, it affects display only and does not prevent
* JavaScript or other untrusted code from submitting the form as though
* access were enabled. The form processing functions for these elements
* should not assume that the buttons can't be "clicked" just because they are
* not displayed.
*
* @see \Drupal\file\Element\ManagedFile::processManagedFile()
* @see \Drupal\Core\Form\FormBuilderInterface::doBuildForm()
*
* Note: based on plupload_element_pre_render().
*/
public static function preRenderPlUploadFile($element) {
$settings = isset($element['#plupload_settings']) ? $element['#plupload_settings'] : [];
// Set upload URL.
if (empty($settings['url'])) {
$settings['url'] = Url::fromRoute('plupload.upload', [], [
'query' => [
'token' => \Drupal::csrfToken()
->get('plupload-handle-uploads'),
],
])
->toString();
}
// The Plupload library supports client-side validation of file extension,
// so pass along the information for it to do that. However, as with all
// client- side validation, this is a UI enhancement only, and not a
// replacement for server-side validation.
if (empty($settings['filters']) && isset($element['#upload_validators']['file_validate_extensions'][0])) {
$settings['filters'][] = [
// @todo Some runtimes (e.g., flash) require a non-empty title for each
// filter, but I don't know what this title is used for. Seems a shame
// to hard-code it, but what's a good way to avoid that?
'title' => t('Allowed files'),
'extensions' => str_replace(' ', ',', $element['#upload_validators']['file_validate_extensions'][0]),
];
}
// Check for autoupload and autosubmit settings and add appropriate
// callback.
if (!empty($element['#autoupload'])) {
$settings['init']['FilesAdded'] = 'Drupal.plupload.filesAddedCallback';
if (!empty($element['#autosubmit'])) {
$settings['init']['UploadComplete'] = 'Drupal.plupload.uploadCompleteCallback';
}
}
// Add a specific submit element that we want to click if one is specified.
if (!empty($element['#submit_element'])) {
$settings['submit_element'] = $element['#submit_element'];
}
// Check if there are event callbacks & append them to current ones, if any.
if (!empty($element['#event_callbacks'])) {
// array_merge() only accepts parameters of type array.
if (!isset($settings['init'])) {
$settings['init'] = [];
}
$settings['init'] = array_merge($settings['init'], $element['#event_callbacks']);
}
if (empty($element['#description'])) {
$element['#description'] = '';
}
$element['#description'] = [
'#theme' => 'file_upload_help',
'#description' => $element['#description'],
'#upload_validators' => $element['#upload_validators'],
];
// Global settings.
$library_discovery = \Drupal::service('library.discovery');
$library = $library_discovery
->getLibraryByName('plupload', 'plupload');
$element['#attached']['drupalSettings']['plupload'] = [
'_default' => $library['settings']['plupload']['_default'],
$element['#id'] => $settings,
];
return $element;
}
/**
* Render API callback: Validates the managed_file element.
*
* Note: based on plupload_element_validate().
*/
public static function validatePlUploadFile(&$element, FormStateInterface $form_state, &$complete_form) {
foreach ($element['#value'] as $file_info) {
// Here we create a $file object for a file that doesn't exist yet,
// because saving the file to its destination is done in a submit handler.
// Using tmp path will give validators access to the actual file on disk
// and filesize information. We manually modify filename and mime to allow
// extension checks.
$destination = \Drupal::config('system.file')
->get('default_scheme') . '://' . $file_info['name'];
$file_uri = \Drupal::service('stream_wrapper_manager')
->normalizeUri($destination);
$file = File::create([
'uri' => $file_info['tmppath'],
'uid' => \Drupal::currentUser()
->id(),
'status' => FILE_STATUS_PERMANENT,
'filename' => \Drupal::service('file_system')
->basename($destination),
'filemime' => \Drupal::service('file.mime_type.guesser')
->guess($destination),
]);
foreach (file_validate($file, $element['#upload_validators']) as $error_message) {
$message = t('The specified file %name could not be uploaded.', [
'%name' => $file
->getFilename(),
]);
$concatenated_message = $message . ' ' . $error_message;
$form_state
->setError($element, $concatenated_message);
}
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 2 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
MessengerTrait:: |
protected | property | The messenger. | 27 |
MessengerTrait:: |
public | function | Gets the messenger. | 27 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
2 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 98 |
PlUploadFile:: |
public | function |
Note: based on plupload_element_info(). Overrides ElementInterface:: |
|
PlUploadFile:: |
public static | function | Render API callback: Hides display of the upload or remove controls. | |
PlUploadFile:: |
public static | function | Render API callback: Expands the managed_file element type. | |
PlUploadFile:: |
public static | function | Render API callback: Validates the managed_file element. | |
PlUploadFile:: |
public static | function |
Overrides FormElement:: |
|
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | 1 |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |