3 namespace Drupal\image\Controller;
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;
19 * Returns responses for our image routes.
21 class QuickEditImageController extends ControllerBase {
24 * Stores The Quick Edit tempstore.
26 * @var \Drupal\user\PrivateTempStore
33 * @var \Drupal\Core\Render\RendererInterface
40 * @var \Drupal\Core\Image\ImageFactory
42 protected $imageFactory;
45 * Constructs a new QuickEditImageController.
47 * @param \Drupal\Core\Render\RendererInterface $renderer
49 * @param \Drupal\Core\Image\ImageFactory $image_factory
51 * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
52 * The tempstore factory.
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');
63 public static function create(ContainerInterface $container) {
65 $container->get('renderer'),
66 $container->get('image.factory'),
67 $container->get('user.private_tempstore')
72 * Returns JSON representing the new file upload, or validation errors.
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.
83 * @return \Symfony\Component\HttpFoundation\JsonResponse
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();
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']];
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' => '']);
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 */
107 $image = $this->imageFactory->get($file->getFileUri());
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);
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);
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);
131 // Save the Entity to tempstore.
132 $this->tempStore->set($entity->uuid(), $entity);
135 'fid' => $file->id(),
136 'html' => $this->renderer->renderRoot($output),
138 return new JsonResponse($data);
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.')]);
149 * Returns JSON representing an image field's metadata.
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.
160 * @return \Drupal\Core\Cache\CacheableJsonResponse
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();
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'],
174 $response = new CacheableJsonResponse($info);
175 $response->addCacheableDependency($entity);
180 * Returns JSON representing the current state of the field.
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.
189 * @return \Drupal\image\Plugin\Field\FieldType\ImageItem
190 * The field for this request.
192 * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
193 * Throws an exception if the request is invalid.
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.');
201 // Check that this field exists.
202 /** @var \Drupal\Core\Field\FieldItemListInterface $field_list */
203 $field_list = $entity->getTranslation($langcode)->get($field_name);
205 throw new BadRequestHttpException('Requested Field does not exist.');
208 // If the list is empty, append an empty item to use.
209 if ($field_list->isEmpty()) {
210 $field = $field_list->appendItem();
212 // Otherwise, use the first item.
214 $field = $entity->getTranslation($langcode)->get($field_name)->first();
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".');