PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/web/core/modules/media_library/src/Form/FileUploadForm.php

https://gitlab.com/andecode/theme-spark
PHP | 392 lines | 192 code | 39 blank | 161 comment | 16 complexity | 49ef406fe47da225c011e63a7f248812 MD5 | raw file
  1. <?php
  2. namespace Drupal\media_library\Form;
  3. use Drupal\Core\Entity\EntityStorageInterface;
  4. use Drupal\Core\Entity\EntityTypeManagerInterface;
  5. use Drupal\Core\Field\FieldStorageDefinitionInterface;
  6. use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
  7. use Drupal\Core\File\Exception\FileWriteException;
  8. use Drupal\Core\File\FileSystemInterface;
  9. use Drupal\Core\Form\FormBuilderInterface;
  10. use Drupal\Core\Form\FormStateInterface;
  11. use Drupal\Core\Render\ElementInfoManagerInterface;
  12. use Drupal\Core\Render\RendererInterface;
  13. use Drupal\Core\Url;
  14. use Drupal\file\FileRepositoryInterface;
  15. use Drupal\file\FileInterface;
  16. use Drupal\file\FileUsage\FileUsageInterface;
  17. use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
  18. use Drupal\file\Plugin\Field\FieldType\FileItem;
  19. use Drupal\media\MediaInterface;
  20. use Drupal\media\MediaTypeInterface;
  21. use Drupal\media_library\MediaLibraryUiBuilder;
  22. use Drupal\media_library\OpenerResolverInterface;
  23. use Symfony\Component\DependencyInjection\ContainerInterface;
  24. /**
  25. * Creates a form to create media entities from uploaded files.
  26. *
  27. * @internal
  28. * Form classes are internal.
  29. */
  30. class FileUploadForm extends AddFormBase {
  31. /**
  32. * The element info manager.
  33. *
  34. * @var \Drupal\Core\Render\ElementInfoManagerInterface
  35. */
  36. protected $elementInfo;
  37. /**
  38. * The renderer service.
  39. *
  40. * @var \Drupal\Core\Render\ElementInfoManagerInterface
  41. */
  42. protected $renderer;
  43. /**
  44. * The file system service.
  45. *
  46. * @var \Drupal\Core\File\FileSystemInterface
  47. */
  48. protected $fileSystem;
  49. /**
  50. * The file usage service.
  51. *
  52. * @var \Drupal\file\FileUsage\FileUsageInterface
  53. */
  54. protected $fileUsage;
  55. /**
  56. * The file repository service.
  57. *
  58. * @var \Drupal\file\FileRepositoryInterface
  59. */
  60. protected $fileRepository;
  61. /**
  62. * Constructs a new FileUploadForm.
  63. *
  64. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  65. * The entity type manager.
  66. * @param \Drupal\media_library\MediaLibraryUiBuilder $library_ui_builder
  67. * The media library UI builder.
  68. * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
  69. * The element info manager.
  70. * @param \Drupal\Core\Render\RendererInterface $renderer
  71. * The renderer service.
  72. * @param \Drupal\Core\File\FileSystemInterface $file_system
  73. * The file system service.
  74. * @param \Drupal\media_library\OpenerResolverInterface $opener_resolver
  75. * The opener resolver.
  76. * @param \Drupal\file\FileUsage\FileUsageInterface $file_usage
  77. * The file usage service.
  78. * @param \Drupal\file\FileRepositoryInterface|null $file_repository
  79. * The file repository service.
  80. */
  81. public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer, FileSystemInterface $file_system, OpenerResolverInterface $opener_resolver, FileUsageInterface $file_usage, FileRepositoryInterface $file_repository = NULL) {
  82. parent::__construct($entity_type_manager, $library_ui_builder, $opener_resolver);
  83. $this->elementInfo = $element_info;
  84. $this->renderer = $renderer;
  85. $this->fileSystem = $file_system;
  86. $this->fileUsage = $file_usage;
  87. if (!$file_repository) {
  88. @trigger_error('Calling ' . __METHOD__ . ' without the $file_repository argument is deprecated in drupal:9.3.0 and will be required in drupal:10.0.0. See https://www.drupal.org/node/3223520', E_USER_DEPRECATED);
  89. $file_repository = \Drupal::service('file.repository');
  90. }
  91. $this->fileRepository = $file_repository;
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public static function create(ContainerInterface $container) {
  97. return new static(
  98. $container->get('entity_type.manager'),
  99. $container->get('media_library.ui_builder'),
  100. $container->get('element_info'),
  101. $container->get('renderer'),
  102. $container->get('file_system'),
  103. $container->get('media_library.opener_resolver'),
  104. $container->get('file.usage'),
  105. $container->get('file.repository')
  106. );
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function getFormId() {
  112. return $this->getBaseFormId() . '_upload';
  113. }
  114. /**
  115. * {@inheritdoc}
  116. */
  117. protected function getMediaType(FormStateInterface $form_state) {
  118. if ($this->mediaType) {
  119. return $this->mediaType;
  120. }
  121. $media_type = parent::getMediaType($form_state);
  122. // The file upload form only supports media types which use a file field as
  123. // a source field.
  124. $field_definition = $media_type->getSource()->getSourceFieldDefinition($media_type);
  125. if (!is_a($field_definition->getClass(), FileFieldItemList::class, TRUE)) {
  126. throw new \InvalidArgumentException('Can only add media types which use a file field as a source field.');
  127. }
  128. return $media_type;
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. protected function buildInputElement(array $form, FormStateInterface $form_state) {
  134. // Create a file item to get the upload validators.
  135. $media_type = $this->getMediaType($form_state);
  136. $item = $this->createFileItem($media_type);
  137. /** @var \Drupal\media_library\MediaLibraryState $state */
  138. $state = $this->getMediaLibraryState($form_state);
  139. if (!$state->hasSlotsAvailable()) {
  140. return $form;
  141. }
  142. $slots = $state->getAvailableSlots();
  143. // Add a container to group the input elements for styling purposes.
  144. $form['container'] = [
  145. '#type' => 'container',
  146. ];
  147. $process = (array) $this->elementInfo->getInfoProperty('managed_file', '#process', []);
  148. $form['container']['upload'] = [
  149. '#type' => 'managed_file',
  150. '#title' => $this->formatPlural($slots, 'Add file', 'Add files'),
  151. // @todo Move validation in https://www.drupal.org/node/2988215
  152. '#process' => array_merge(['::validateUploadElement'], $process, ['::processUploadElement']),
  153. '#upload_validators' => $item->getUploadValidators(),
  154. '#multiple' => $slots > 1 || $slots === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
  155. '#cardinality' => $slots,
  156. '#remaining_slots' => $slots,
  157. ];
  158. $file_upload_help = [
  159. '#theme' => 'file_upload_help',
  160. '#upload_validators' => $form['container']['upload']['#upload_validators'],
  161. '#cardinality' => $slots,
  162. ];
  163. // The file upload help needs to be rendered since the description does not
  164. // accept render arrays. The FileWidget::formElement() method adds the file
  165. // upload help in the same way, so any theming improvements made to file
  166. // fields would also be applied to this upload field.
  167. // @see \Drupal\file\Plugin\Field\FieldWidget\FileWidget::formElement()
  168. $form['container']['upload']['#description'] = $this->renderer->renderPlain($file_upload_help);
  169. return $form;
  170. }
  171. /**
  172. * Validates the upload element.
  173. *
  174. * @param array $element
  175. * The upload element.
  176. * @param \Drupal\Core\Form\FormStateInterface $form_state
  177. * The form state.
  178. *
  179. * @return array
  180. * The processed upload element.
  181. */
  182. public function validateUploadElement(array $element, FormStateInterface $form_state) {
  183. if ($form_state::hasAnyErrors()) {
  184. // When an error occurs during uploading files, remove all files so the
  185. // user can re-upload the files.
  186. $element['#value'] = [];
  187. }
  188. $values = $form_state->getValue('upload', []);
  189. if (count($values['fids']) > $element['#cardinality'] && $element['#cardinality'] !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
  190. $form_state->setError($element, $this->t('A maximum of @count files can be uploaded.', [
  191. '@count' => $element['#cardinality'],
  192. ]));
  193. $form_state->setValue('upload', []);
  194. $element['#value'] = [];
  195. }
  196. return $element;
  197. }
  198. /**
  199. * Processes an upload (managed_file) element.
  200. *
  201. * @param array $element
  202. * The upload element.
  203. * @param \Drupal\Core\Form\FormStateInterface $form_state
  204. * The form state.
  205. *
  206. * @return array
  207. * The processed upload element.
  208. */
  209. public function processUploadElement(array $element, FormStateInterface $form_state) {
  210. $element['upload_button']['#submit'] = ['::uploadButtonSubmit'];
  211. // Limit the validation errors to make sure
  212. // FormValidator::handleErrorsWithLimitedValidation doesn't remove the
  213. // current selection from the form state.
  214. // @see Drupal\Core\Form\FormValidator::handleErrorsWithLimitedValidation()
  215. $element['upload_button']['#limit_validation_errors'] = [
  216. ['upload'],
  217. ['current_selection'],
  218. ];
  219. $element['upload_button']['#ajax'] = [
  220. 'callback' => '::updateFormCallback',
  221. 'wrapper' => 'media-library-wrapper',
  222. // Add a fixed URL to post the form since AJAX forms are automatically
  223. // posted to <current> instead of $form['#action'].
  224. // @todo Remove when https://www.drupal.org/project/drupal/issues/2504115
  225. // is fixed.
  226. 'url' => Url::fromRoute('media_library.ui'),
  227. 'options' => [
  228. 'query' => $this->getMediaLibraryState($form_state)->all() + [
  229. FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
  230. ],
  231. ],
  232. ];
  233. return $element;
  234. }
  235. /**
  236. * {@inheritdoc}
  237. */
  238. protected function buildEntityFormElement(MediaInterface $media, array $form, FormStateInterface $form_state, $delta) {
  239. $element = parent::buildEntityFormElement($media, $form, $form_state, $delta);
  240. $source_field = $this->getSourceFieldName($media->bundle->entity);
  241. if (isset($element['fields'][$source_field])) {
  242. $element['fields'][$source_field]['widget'][0]['#process'][] = [static::class, 'hideExtraSourceFieldComponents'];
  243. }
  244. return $element;
  245. }
  246. /**
  247. * Processes an image or file source field element.
  248. *
  249. * @param array $element
  250. * The entity form source field element.
  251. * @param \Drupal\Core\Form\FormStateInterface $form_state
  252. * The current form state.
  253. * @param $form
  254. * The complete form.
  255. *
  256. * @return array
  257. * The processed form element.
  258. */
  259. public static function hideExtraSourceFieldComponents($element, FormStateInterface $form_state, $form) {
  260. // Remove original button added by ManagedFile::processManagedFile().
  261. if (!empty($element['remove_button'])) {
  262. $element['remove_button']['#access'] = FALSE;
  263. }
  264. // Remove preview added by ImageWidget::process().
  265. if (!empty($element['preview'])) {
  266. $element['preview']['#access'] = FALSE;
  267. }
  268. $element['#title_display'] = 'none';
  269. $element['#description_display'] = 'none';
  270. // Remove the filename display.
  271. foreach ($element['#files'] as $file) {
  272. $element['file_' . $file->id()]['filename']['#access'] = FALSE;
  273. }
  274. return $element;
  275. }
  276. /**
  277. * Submit handler for the upload button, inside the managed_file element.
  278. *
  279. * @param array $form
  280. * The form render array.
  281. * @param \Drupal\Core\Form\FormStateInterface $form_state
  282. * The form state.
  283. */
  284. public function uploadButtonSubmit(array $form, FormStateInterface $form_state) {
  285. $files = $this->entityTypeManager
  286. ->getStorage('file')
  287. ->loadMultiple($form_state->getValue('upload', []));
  288. $this->processInputValues($files, $form, $form_state);
  289. }
  290. /**
  291. * {@inheritdoc}
  292. */
  293. protected function createMediaFromValue(MediaTypeInterface $media_type, EntityStorageInterface $media_storage, $source_field_name, $file) {
  294. if (!($file instanceof FileInterface)) {
  295. throw new \InvalidArgumentException('Cannot create a media item without a file entity.');
  296. }
  297. // Create a file item to get the upload location.
  298. $item = $this->createFileItem($media_type);
  299. $upload_location = $item->getUploadLocation();
  300. if (!$this->fileSystem->prepareDirectory($upload_location, FileSystemInterface::CREATE_DIRECTORY)) {
  301. throw new FileWriteException("The destination directory '$upload_location' is not writable");
  302. }
  303. $file = $this->fileRepository->move($file, $upload_location);
  304. if (!$file) {
  305. throw new \RuntimeException("Unable to move file to '$upload_location'");
  306. }
  307. return parent::createMediaFromValue($media_type, $media_storage, $source_field_name, $file);
  308. }
  309. /**
  310. * Create a file field item.
  311. *
  312. * @param \Drupal\media\MediaTypeInterface $media_type
  313. * The media type of the media item.
  314. *
  315. * @return \Drupal\file\Plugin\Field\FieldType\FileItem
  316. * A created file item.
  317. */
  318. protected function createFileItem(MediaTypeInterface $media_type) {
  319. $field_definition = $media_type->getSource()->getSourceFieldDefinition($media_type);
  320. $data_definition = FieldItemDataDefinition::create($field_definition);
  321. return new FileItem($data_definition);
  322. }
  323. /**
  324. * {@inheritdoc}
  325. */
  326. protected function prepareMediaEntityForSave(MediaInterface $media) {
  327. /** @var \Drupal\file\FileInterface $file */
  328. $file = $media->get($this->getSourceFieldName($media->bundle->entity))->entity;
  329. $file->setPermanent();
  330. $file->save();
  331. }
  332. /**
  333. * Submit handler for the remove button.
  334. *
  335. * @param array $form
  336. * The form render array.
  337. * @param \Drupal\Core\Form\FormStateInterface $form_state
  338. * The form state.
  339. */
  340. public function removeButtonSubmit(array $form, FormStateInterface $form_state) {
  341. // Retrieve the delta of the media item from the parents of the remove
  342. // button.
  343. $triggering_element = $form_state->getTriggeringElement();
  344. $delta = array_slice($triggering_element['#array_parents'], -2, 1)[0];
  345. /** @var \Drupal\media\MediaInterface $removed_media */
  346. $removed_media = $form_state->get(['media', $delta]);
  347. $file = $removed_media->get($this->getSourceFieldName($removed_media->bundle->entity))->entity;
  348. if ($file instanceof FileInterface && empty($this->fileUsage->listUsage($file))) {
  349. $file->delete();
  350. }
  351. parent::removeButtonSubmit($form, $form_state);
  352. }
  353. }