annotate core/modules/image/src/Controller/QuickEditImageController.php @ 19:fa3358dc1485 tip

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