aa49bc7d3c39268143b79186c6e7d6dd176deef6
[yaffs-website] / src / Controller / QuickEditImageController.php
1 <?php
2
3 namespace Drupal\image\Controller;
4
5 use Drupal\Core\Cache\CacheableJsonResponse;
6 use Drupal\Core\Controller\ControllerBase;
7 use Drupal\Core\Entity\ContentEntityInterface;
8 use Drupal\Core\Entity\EntityInterface;
9 use Drupal\Core\Image\ImageFactory;
10 use Drupal\Core\Render\Element\StatusMessages;
11 use Drupal\Core\Render\RendererInterface;
12 use Drupal\image\Plugin\Field\FieldType\ImageItem;
13 use Drupal\user\PrivateTempStoreFactory;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15 use Symfony\Component\HttpFoundation\JsonResponse;
16 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
17
18 /**
19  * Returns responses for our image routes.
20  */
21 class QuickEditImageController extends ControllerBase {
22
23   /**
24    * Stores The Quick Edit tempstore.
25    *
26    * @var \Drupal\user\PrivateTempStore
27    */
28   protected $tempStore;
29
30   /**
31    * The renderer.
32    *
33    * @var \Drupal\Core\Render\RendererInterface
34    */
35   protected $renderer;
36
37   /**
38    * The image factory.
39    *
40    * @var \Drupal\Core\Image\ImageFactory
41    */
42   protected $imageFactory;
43
44   /**
45    * Constructs a new QuickEditImageController.
46    *
47    * @param \Drupal\Core\Render\RendererInterface $renderer
48    *   The renderer.
49    * @param \Drupal\Core\Image\ImageFactory $image_factory
50    *   The image factory.
51    * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
52    *   The tempstore factory.
53    */
54   public function __construct(RendererInterface $renderer, ImageFactory $image_factory, PrivateTempStoreFactory $temp_store_factory) {
55     $this->renderer = $renderer;
56     $this->imageFactory = $image_factory;
57     $this->tempStore = $temp_store_factory->get('quickedit');
58   }
59
60   /**
61    * {@inheritdoc}
62    */
63   public static function create(ContainerInterface $container) {
64     return new static(
65       $container->get('renderer'),
66       $container->get('image.factory'),
67       $container->get('user.private_tempstore')
68     );
69   }
70
71   /**
72    * Returns JSON representing the new file upload, or validation errors.
73    *
74    * @param \Drupal\Core\Entity\EntityInterface $entity
75    *   The entity of which an image field is being rendered.
76    * @param string $field_name
77    *   The name of the (image) field that is being rendered
78    * @param string $langcode
79    *   The language code of the field that is being rendered.
80    * @param string $view_mode_id
81    *   The view mode of the field that is being rendered.
82    *
83    * @return \Symfony\Component\HttpFoundation\JsonResponse
84    *   The JSON response.
85    */
86   public function upload(EntityInterface $entity, $field_name, $langcode, $view_mode_id) {
87     $field = $this->getField($entity, $field_name, $langcode);
88     $field_validators = $field->getUploadValidators();
89     $field_settings = $field->getFieldDefinition()->getSettings();
90     $destination = $field->getUploadLocation();
91
92     // Add upload resolution validation.
93     if ($field_settings['max_resolution'] || $field_settings['min_resolution']) {
94       $field_validators['file_validate_image_resolution'] = [$field_settings['max_resolution'], $field_settings['min_resolution']];
95     }
96
97     // Create the destination directory if it does not already exist.
98     if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
99       return new JsonResponse(['main_error' => $this->t('The destination directory could not be created.'), 'errors' => '']);
100     }
101
102     // Attempt to save the image given the field's constraints.
103     $result = file_save_upload('image', $field_validators, $destination);
104     if (is_array($result) && $result[0]) {
105       /** @var \Drupal\file\Entity\File $file */
106       $file = $result[0];
107       $image = $this->imageFactory->get($file->getFileUri());
108
109       // Set the value in the Entity to the new file.
110       /** @var \Drupal\file\Plugin\Field\FieldType\FileFieldItemList $field_list */
111       $value = $entity->$field_name->getValue();
112       $value[0]['target_id'] = $file->id();
113       $value[0]['width'] = $image->getWidth();
114       $value[0]['height'] = $image->getHeight();
115       $entity->$field_name->setValue($value);
116
117       // Render the new image using the correct formatter settings.
118       $entity_view_mode_ids = array_keys($this->entityManager()->getViewModes($entity->getEntityTypeId()));
119       if (in_array($view_mode_id, $entity_view_mode_ids, TRUE)) {
120         $output = $entity->$field_name->view($view_mode_id);
121       }
122       else {
123         // Each part of a custom (non-Entity Display) view mode ID is separated
124         // by a dash; the first part must be the module name.
125         $mode_id_parts = explode('-', $view_mode_id, 2);
126         $module = reset($mode_id_parts);
127         $args = [$entity, $field_name, $view_mode_id, $langcode];
128         $output = $this->moduleHandler()->invoke($module, 'quickedit_render_field', $args);
129       }
130
131       // Save the Entity to tempstore.
132       $this->tempStore->set($entity->uuid(), $entity);
133
134       $data = [
135         'fid' => $file->id(),
136         'html' => $this->renderer->renderRoot($output),
137       ];
138       return new JsonResponse($data);
139     }
140     else {
141       // Return a JSON object containing the errors from Drupal and our
142       // "main_error", which is displayed inside the dropzone area.
143       $messages = StatusMessages::renderMessages('error');
144       return new JsonResponse(['errors' => $this->renderer->render($messages), 'main_error' => $this->t('The image failed validation.')]);
145     }
146   }
147
148   /**
149    * Returns JSON representing an image field's metadata.
150    *
151    * @param \Drupal\Core\Entity\EntityInterface $entity
152    *   The entity of which an image field is being rendered.
153    * @param string $field_name
154    *   The name of the (image) field that is being rendered
155    * @param string $langcode
156    *   The language code of the field that is being rendered.
157    * @param string $view_mode_id
158    *   The view mode of the field that is being rendered.
159    *
160    * @return \Drupal\Core\Cache\CacheableJsonResponse
161    *   The JSON response.
162    */
163   public function getInfo(EntityInterface $entity, $field_name, $langcode, $view_mode_id) {
164     $field = $this->getField($entity, $field_name, $langcode);
165     $settings = $field->getFieldDefinition()->getSettings();
166     $info = [
167       'alt' => $field->alt,
168       'title' => $field->title,
169       'alt_field' => $settings['alt_field'],
170       'title_field' => $settings['title_field'],
171       'alt_field_required' => $settings['alt_field_required'],
172       'title_field_required' => $settings['title_field_required'],
173     ];
174     $response = new CacheableJsonResponse($info);
175     $response->addCacheableDependency($entity);
176     return $response;
177   }
178
179   /**
180    * Returns JSON representing the current state of the field.
181    *
182    * @param \Drupal\Core\Entity\EntityInterface $entity
183    *   The entity of which an image field is being rendered.
184    * @param string $field_name
185    *   The name of the (image) field that is being rendered
186    * @param string $langcode
187    *   The language code of the field that is being rendered.
188    *
189    * @return \Drupal\image\Plugin\Field\FieldType\ImageItem
190    *   The field for this request.
191    *
192    * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
193    *   Throws an exception if the request is invalid.
194    */
195   protected function getField(EntityInterface $entity, $field_name, $langcode) {
196     // Ensure that this is a valid Entity.
197     if (!($entity instanceof ContentEntityInterface)) {
198       throw new BadRequestHttpException('Requested Entity is not a Content Entity.');
199     }
200
201     // Check that this field exists.
202     /** @var \Drupal\Core\Field\FieldItemListInterface $field_list */
203     $field_list = $entity->getTranslation($langcode)->get($field_name);
204     if (!$field_list) {
205       throw new BadRequestHttpException('Requested Field does not exist.');
206     }
207
208     // If the list is empty, append an empty item to use.
209     if ($field_list->isEmpty()) {
210       $field = $field_list->appendItem();
211     }
212     // Otherwise, use the first item.
213     else {
214       $field = $entity->getTranslation($langcode)->get($field_name)->first();
215     }
216
217     // Ensure that the field is the type we expect.
218     if (!($field instanceof ImageItem)) {
219       throw new BadRequestHttpException('Requested Field is not of type "image".');
220     }
221
222     return $field;
223   }
224
225 }