Further modules included.
[yaffs-website] / web / modules / contrib / dropzonejs / src / DropzoneJsUploadSave.php
1 <?php
2
3 namespace Drupal\dropzonejs;
4
5 use Drupal\Component\Render\PlainTextOutput;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
9 use Drupal\Core\Render\RendererInterface;
10 use Drupal\Core\Session\AccountProxyInterface;
11 use Drupal\Core\StringTranslation\StringTranslationTrait;
12 use Drupal\Core\Utility\Token;
13 use Drupal\file\FileInterface;
14 use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
15 use Drupal\Core\File\FileSystemInterface;
16
17 /**
18  * A service that saves files uploaded by the dropzonejs element as files.
19  *
20  * Most of this file mimics or directly copies what core does. For more
21  * information and comments see file_save_upload().
22  */
23 class DropzoneJsUploadSave implements DropzoneJsUploadSaveInterface {
24
25   use StringTranslationTrait;
26
27   /**
28    * Entity manager service.
29    *
30    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
31    */
32   protected $entityTypeManager;
33
34   /**
35    * Mime type guesser service.
36    *
37    * @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
38    */
39   protected $mimeTypeGuesser;
40
41   /**
42    * The file system service.
43    *
44    * @var \Drupal\Core\File\FileSystemInterface
45    */
46   protected $fileSystem;
47
48   /**
49    * The logger service.
50    *
51    * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
52    */
53   protected $logger;
54
55   /**
56    * The renderer service.
57    *
58    * @var \Drupal\Core\Render\RendererInterface
59    */
60   protected $renderer;
61
62   /**
63    * Config factory service.
64    *
65    * @var \Drupal\Core\Config\ConfigFactoryInterface
66    */
67   protected $configFactory;
68
69   /**
70    * The token service.
71    *
72    * @var \Drupal\Core\Utility\Token
73    */
74   protected $token;
75
76   /**
77    * Construct the DropzoneUploadSave object.
78    *
79    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
80    *   Entity type manager service.
81    * @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mimetype_guesser
82    *   The mime type guesser service.
83    * @param \Drupal\Core\File\FileSystemInterface $file_system
84    *   The file system service.
85    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
86    *   The logger factory service.
87    * @param \Drupal\Core\Render\RendererInterface $renderer
88    *   The renderer service.
89    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
90    *   Config factory service.
91    * @param \Drupal\Core\Utility\Token $token
92    *   The token service.
93    */
94   public function __construct(EntityTypeManagerInterface $entity_type_manager, MimeTypeGuesserInterface $mimetype_guesser, FileSystemInterface $file_system, LoggerChannelFactoryInterface $logger_factory, RendererInterface $renderer, ConfigFactoryInterface $config_factory, Token $token) {
95     $this->entityTypeManager = $entity_type_manager;
96     $this->mimeTypeGuesser = $mimetype_guesser;
97     $this->fileSystem = $file_system;
98     $this->logger = $logger_factory->get('dropzonejs');
99     $this->renderer = $renderer;
100     $this->configFactory = $config_factory;
101     $this->token = $token;
102   }
103
104   /**
105    * {@inheritdoc}
106    */
107   public function createFile($uri, $destination, $extensions, AccountProxyInterface $user, array $validators = []) {
108     // Create the file entity.
109     $uri = file_stream_wrapper_uri_normalize($uri);
110     $file_info = new \SplFileInfo($uri);
111
112     /** @var \Drupal\file\FileInterface $file */
113     $file = $this->entityTypeManager->getStorage('file')->create([
114       'uid' => $user->id(),
115       'status' => 0,
116       'filename' => $file_info->getFilename(),
117       'uri' => $uri,
118       'filesize' => $file_info->getSize(),
119       'filemime' => $this->mimeTypeGuesser->guess($uri),
120     ]);
121
122     // Replace tokens. As the tokens might contain HTML we convert it to plain
123     // text.
124     $destination = PlainTextOutput::renderFromHtml($this->token->replace($destination));
125
126     // Handle potentialy dangerous extensions.
127     $renamed = $this->renameExecutableExtensions($file);
128     // The .txt extension may not be in the allowed list of extensions. We have
129     // to add it here or else the file upload will fail.
130     if ($renamed && !empty($extensions)) {
131       $extensions .= ' txt';
132       drupal_set_message($this->t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $file->getFilename()]));
133     }
134
135     // Validate the file.
136     $errors = $this->validateFile($file, $extensions, $validators);
137     if (!empty($errors)) {
138       $message = [
139         'error' => [
140           '#markup' => $this->t('The specified file %name could not be uploaded.', ['%name' => $file->getFilename()]),
141         ],
142         'item_list' => [
143           '#theme' => 'item_list',
144           '#items' => $errors,
145         ],
146       ];
147       drupal_set_message($this->renderer->renderPlain($message), 'error');
148       return FALSE;
149     }
150
151     // Prepare destination.
152     if (!$this->prepareDestination($file, $destination)) {
153       drupal_set_message($this->t('The file could not be uploaded because the destination %destination is invalid.', ['%destination' => $destination]), 'error');
154       return FALSE;
155     }
156
157     // Move uploaded files from PHP's upload_tmp_dir to destination.
158     $move_result = file_unmanaged_move($uri, $file->getFileUri());
159     if (!$move_result) {
160       drupal_set_message($this->t('File upload error. Could not move uploaded file.'), 'error');
161       $this->logger->notice('Upload error. Could not move uploaded file %file to destination %destination.', ['%file' => $file->getFilename(), '%destination' => $file->getFileUri()]);
162       return FALSE;
163     }
164
165     // Set the permissions on the new file.
166     $this->fileSystem->chmod($file->getFileUri());
167
168     return $file;
169   }
170
171   /**
172    * {@inheritdoc}
173    */
174   public function validateFile(FileInterface $file, $extensions, array $additional_validators = []) {
175     $validators = $additional_validators;
176
177     if (!empty($extensions)) {
178       $validators['file_validate_extensions'] = [$extensions];
179     }
180     $validators['file_validate_name_length'] = [];
181
182     // Call the validation functions specified by this function's caller.
183     return file_validate($file, $validators);
184   }
185
186   /**
187    * Rename potentially executable files.
188    *
189    * @param \Drupal\file\FileInterface $file
190    *   The file entity object.
191    *
192    * @return bool
193    *   Whether the file was renamed or not.
194    */
195   protected function renameExecutableExtensions(FileInterface $file) {
196     if (!$this->configFactory->get('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
197       $file->setMimeType('text/plain');
198       // The destination filename will also later be used to create the URI.
199       $file->setFilename($file->getFilename() . '.txt');
200       return TRUE;
201     }
202     return FALSE;
203   }
204
205   /**
206    * Validate and set destination the destination URI.
207    *
208    * @param \Drupal\file\FileInterface $file
209    *   The file entity object.
210    * @param string $destination
211    *   A string containing the URI that the file should be copied to. This must
212    *   be a stream wrapper URI.
213    *
214    * @return bool
215    *   True if the destination was sucesfully validated and set, otherwise
216    *   false.
217    */
218   protected function prepareDestination(FileInterface $file, $destination) {
219     // Assert that the destination contains a valid stream.
220     $destination_scheme = $this->fileSystem->uriScheme($destination);
221     if (!$this->fileSystem->validScheme($destination_scheme)) {
222       return FALSE;
223     }
224
225     // Prepare the destination dir.
226     if (!file_exists($destination)) {
227       $this->fileSystem->mkdir($destination, NULL, TRUE);
228     }
229
230     // A file URI may already have a trailing slash or look like "public://".
231     if (substr($destination, -1) != '/') {
232       $destination .= '/';
233     }
234     $destination = file_destination($destination . $file->getFilename(), FILE_EXISTS_RENAME);
235     $file->setFileUri($destination);
236     return TRUE;
237   }
238
239 }