Mercurial > hg > isophonics-drupal-site
comparison core/modules/image/src/Controller/ImageStyleDownloadController.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | c2387f117808 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\image\Controller; | |
4 | |
5 use Drupal\Component\Utility\Crypt; | |
6 use Drupal\Core\Image\ImageFactory; | |
7 use Drupal\Core\Lock\LockBackendInterface; | |
8 use Drupal\image\ImageStyleInterface; | |
9 use Drupal\system\FileDownloadController; | |
10 use Symfony\Component\DependencyInjection\ContainerInterface; | |
11 use Symfony\Component\HttpFoundation\BinaryFileResponse; | |
12 use Symfony\Component\HttpFoundation\Request; | |
13 use Symfony\Component\HttpFoundation\Response; | |
14 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
15 use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; | |
16 | |
17 /** | |
18 * Defines a controller to serve image styles. | |
19 */ | |
20 class ImageStyleDownloadController extends FileDownloadController { | |
21 | |
22 /** | |
23 * The lock backend. | |
24 * | |
25 * @var \Drupal\Core\Lock\LockBackendInterface | |
26 */ | |
27 protected $lock; | |
28 | |
29 /** | |
30 * The image factory. | |
31 * | |
32 * @var \Drupal\Core\Image\ImageFactory | |
33 */ | |
34 protected $imageFactory; | |
35 | |
36 /** | |
37 * A logger instance. | |
38 * | |
39 * @var \Psr\Log\LoggerInterface | |
40 */ | |
41 protected $logger; | |
42 | |
43 /** | |
44 * Constructs a ImageStyleDownloadController object. | |
45 * | |
46 * @param \Drupal\Core\Lock\LockBackendInterface $lock | |
47 * The lock backend. | |
48 * @param \Drupal\Core\Image\ImageFactory $image_factory | |
49 * The image factory. | |
50 */ | |
51 public function __construct(LockBackendInterface $lock, ImageFactory $image_factory) { | |
52 $this->lock = $lock; | |
53 $this->imageFactory = $image_factory; | |
54 $this->logger = $this->getLogger('image'); | |
55 } | |
56 | |
57 /** | |
58 * {@inheritdoc} | |
59 */ | |
60 public static function create(ContainerInterface $container) { | |
61 return new static( | |
62 $container->get('lock'), | |
63 $container->get('image.factory') | |
64 ); | |
65 } | |
66 | |
67 /** | |
68 * Generates a derivative, given a style and image path. | |
69 * | |
70 * After generating an image, transfer it to the requesting agent. | |
71 * | |
72 * @param \Symfony\Component\HttpFoundation\Request $request | |
73 * The request object. | |
74 * @param string $scheme | |
75 * The file scheme, defaults to 'private'. | |
76 * @param \Drupal\image\ImageStyleInterface $image_style | |
77 * The image style to deliver. | |
78 * | |
79 * @return \Symfony\Component\HttpFoundation\BinaryFileResponse|\Symfony\Component\HttpFoundation\Response | |
80 * The transferred file as response or some error response. | |
81 * | |
82 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | |
83 * Thrown when the user does not have access to the file. | |
84 * @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException | |
85 * Thrown when the file is still being generated. | |
86 */ | |
87 public function deliver(Request $request, $scheme, ImageStyleInterface $image_style) { | |
88 $target = $request->query->get('file'); | |
89 $image_uri = $scheme . '://' . $target; | |
90 | |
91 // Check that the style is defined, the scheme is valid, and the image | |
92 // derivative token is valid. Sites which require image derivatives to be | |
93 // generated without a token can set the | |
94 // 'image.settings:allow_insecure_derivatives' configuration to TRUE to | |
95 // bypass the latter check, but this will increase the site's vulnerability | |
96 // to denial-of-service attacks. To prevent this variable from leaving the | |
97 // site vulnerable to the most serious attacks, a token is always required | |
98 // when a derivative of a style is requested. | |
99 // The $target variable for a derivative of a style has | |
100 // styles/<style_name>/... as structure, so we check if the $target variable | |
101 // starts with styles/. | |
102 $valid = !empty($image_style) && file_stream_wrapper_valid_scheme($scheme); | |
103 if (!$this->config('image.settings')->get('allow_insecure_derivatives') || strpos(ltrim($target, '\/'), 'styles/') === 0) { | |
104 $valid &= $request->query->get(IMAGE_DERIVATIVE_TOKEN) === $image_style->getPathToken($image_uri); | |
105 } | |
106 if (!$valid) { | |
107 throw new AccessDeniedHttpException(); | |
108 } | |
109 | |
110 $derivative_uri = $image_style->buildUri($image_uri); | |
111 $headers = []; | |
112 | |
113 // If using the private scheme, let other modules provide headers and | |
114 // control access to the file. | |
115 if ($scheme == 'private') { | |
116 $headers = $this->moduleHandler()->invokeAll('file_download', [$image_uri]); | |
117 if (in_array(-1, $headers) || empty($headers)) { | |
118 throw new AccessDeniedHttpException(); | |
119 } | |
120 } | |
121 | |
122 // Don't try to generate file if source is missing. | |
123 if (!file_exists($image_uri)) { | |
124 // If the image style converted the extension, it has been added to the | |
125 // original file, resulting in filenames like image.png.jpeg. So to find | |
126 // the actual source image, we remove the extension and check if that | |
127 // image exists. | |
128 $path_info = pathinfo($image_uri); | |
129 $converted_image_uri = $path_info['dirname'] . DIRECTORY_SEPARATOR . $path_info['filename']; | |
130 if (!file_exists($converted_image_uri)) { | |
131 $this->logger->notice('Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', ['%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri]); | |
132 return new Response($this->t('Error generating image, missing source file.'), 404); | |
133 } | |
134 else { | |
135 // The converted file does exist, use it as the source. | |
136 $image_uri = $converted_image_uri; | |
137 } | |
138 } | |
139 | |
140 // Don't start generating the image if the derivative already exists or if | |
141 // generation is in progress in another thread. | |
142 if (!file_exists($derivative_uri)) { | |
143 $lock_name = 'image_style_deliver:' . $image_style->id() . ':' . Crypt::hashBase64($image_uri); | |
144 $lock_acquired = $this->lock->acquire($lock_name); | |
145 if (!$lock_acquired) { | |
146 // Tell client to retry again in 3 seconds. Currently no browsers are | |
147 // known to support Retry-After. | |
148 throw new ServiceUnavailableHttpException(3, $this->t('Image generation in progress. Try again shortly.')); | |
149 } | |
150 } | |
151 | |
152 // Try to generate the image, unless another thread just did it while we | |
153 // were acquiring the lock. | |
154 $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); | |
155 | |
156 if (!empty($lock_acquired)) { | |
157 $this->lock->release($lock_name); | |
158 } | |
159 | |
160 if ($success) { | |
161 $image = $this->imageFactory->get($derivative_uri); | |
162 $uri = $image->getSource(); | |
163 $headers += [ | |
164 'Content-Type' => $image->getMimeType(), | |
165 'Content-Length' => $image->getFileSize(), | |
166 ]; | |
167 // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond() | |
168 // sets response as not cacheable if the Cache-Control header is not | |
169 // already modified. We pass in FALSE for non-private schemes for the | |
170 // $public parameter to make sure we don't change the headers. | |
171 return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private'); | |
172 } | |
173 else { | |
174 $this->logger->notice('Unable to generate the derived image located at %path.', ['%path' => $derivative_uri]); | |
175 return new Response($this->t('Error generating image.'), 500); | |
176 } | |
177 } | |
178 | |
179 } |