Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • AdjustThumbnailOptions
  • AdjustThumbnailVersion
  • CacheManager
  • GetOperation
  • Hooks
  • Module
  • PopThumbnailVersion
  • Thumbnail
  • Version
  • Versions

Exceptions

  • VersionNotDefined

Constants

  • CACHE_VERSIONS
  1 <?php
  2 
  3 /*
  4  * This file is part of the Icybee package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace ICanBoogie\Modules\Thumbnailer;
 13 
 14 use ICanBoogie\Exception;
 15 use ICanBoogie\FileCache;
 16 use ICanBoogie\HTTP\HTTPError;
 17 use ICanBoogie\HTTP\NotFound;
 18 use ICanBoogie\HTTP\Request;
 19 use ICanBoogie\I18n;
 20 use ICanBoogie\Image;
 21 use ICanBoogie\Operation;
 22 
 23 /**
 24  * @property Modules\Thumbnailer $module
 25  * @property string $repository Path to the thumbnails repository.
 26  * @property FileCache $cache Thumbnails cache manager.
 27  */
 28 class GetOperation extends Operation
 29 {
 30     const VERSION = '2.1';
 31 
 32     static public $background;
 33 
 34     /**
 35      * Tries to create a {@link Version} instance from the request.
 36      *
 37      * @param Request $request
 38      *
 39      * @return \ICanBoogie\Modules\Thumbnailer\Version
 40      */
 41     protected function resolve_version(Request $request)
 42     {
 43         global $core;
 44 
 45         $version_name = $request['version'] ?: $request['v'];
 46 
 47         if ($version_name)
 48         {
 49             $version = $core->thumbnailer_versions[$version_name];
 50         }
 51         else
 52         {
 53             $version = Version::from_uri($request->uri);
 54 
 55             if (!$version)
 56             {
 57                 $version = new Version($request->params);
 58             }
 59         }
 60 
 61         Image::assert_sizes($version->method, $version->width, $version->height); // TODO-20140423: $version->validate($errors)
 62 
 63         return $version;
 64     }
 65 
 66     /**
 67      * Returns the location of the thumbnail on the server, relative to the document root.
 68      *
 69      * The thumbnail is created using the parameters supplied, if it is not already available in
 70      * the cache.
 71      *
 72      * @param array $params
 73      * @throws HTTPError
 74      */
 75     public function get()
 76     {
 77         $version = clone $this->resolve_version($this->request);
 78 
 79         #
 80         # We check if the source file exists
 81         #
 82 
 83         $src = $version->src;
 84         $path = $version->path;
 85 
 86         if (!$src)
 87         {
 88             throw new NotFound('Missing thumbnail source.');
 89         }
 90 
 91         $src = $path . $src;
 92         $location = \ICanBoogie\DOCUMENT_ROOT . DIRECTORY_SEPARATOR . $src;
 93 
 94         if (!is_file($location))
 95         {
 96             $default = $version->default;
 97 
 98             #
 99             # use the provided default file is defined
100             #
101 
102             if (!$default)
103             {
104                 throw new NotFound(I18n\t('Thumbnail source not found: %src', array('%src' => $src)));
105             }
106 
107             $src = $path . $default;
108             $location = \ICanBoogie\DOCUMENT_ROOT . DIRECTORY_SEPARATOR . $src;
109 
110             if (!is_file($location))
111             {
112                 throw new NotFound(I18n\t('Thumbnail source (default) not found: %src', array('%src' => $src)));
113             }
114         }
115 
116         if (!$version->format)
117         {
118             $info = getimagesize($location);
119             $version->format = substr($info['mime'], 6);
120         }
121 
122         if ($version->format == 'jpeg' && $version->background == 'transparent')
123         {
124             $version->background = 'white';
125         }
126 
127         #
128         # We create a unique key for the thumbnail, using the image information
129         # and the options used to create the thumbnail.
130         #
131 
132         $key = filemtime($location) . '#' . filesize($location) . '#' . json_encode($version->to_array());
133         $key = sha1($key) . '.' . $version->format;
134 
135         #
136         # Use the cache object to get the file
137         #
138 
139         $cache = new CacheManager;
140 
141         return $cache->retrieve($key, array($this, 'get_construct'), array($location, $version));
142     }
143 
144     /**
145      * Constructor for the cache entry.
146      *
147      * @param FileCache $cache The cache object.
148      * @param string $destination The file to create.
149      * @param array $userdata An array with the path of the original image and the options to use
150      * to create the thumbnail.
151      * @throws Exception
152      */
153     public function get_construct(FileCache $cache, $destination, $userdata)
154     {
155         list($path, $version) = $userdata;
156 
157         $callback = null;
158 
159         if ($version->background != 'transparent')
160         {
161             self::$background = self::decode_background($version->background);
162 
163             $callback = __CLASS__ . '::fill_callback';
164         }
165 
166         $image = Image::load($path, $info);
167 
168         if (!$image)
169         {
170             throw new Exception('Unable to load image from file %path', array('%path' => $path));
171         }
172 
173         #
174         # resize image
175         #
176 
177         $w = $version->width;
178         $h = $version->height;
179 
180         list($ow, $oh) = $info;
181 
182         $method = $version->method;
183 
184         if ($version->no_upscale)
185         {
186             if ($method == Image::RESIZE_SURFACE)
187             {
188                 if ($w * $h > $ow * $oh)
189                 {
190                     $w = $ow;
191                     $h = $oh;
192                 }
193             }
194             else
195             {
196                 if ($w > $ow)
197                 {
198                     $w = $ow;
199                 }
200 
201                 if ($h > $oh)
202                 {
203                     $h = $ow;
204                 }
205             }
206         }
207 
208         $image = Image::resize($image, $w, $h, $method, $callback);
209 
210         if (!$image)
211         {
212             throw new Exception
213             (
214                 'Unable to resize image for file %path with version: !version', array
215                 (
216                     '%path' => $path,
217                     '!version' => $version
218                 )
219             );
220         }
221 
222         #
223         # apply filters
224         #
225 
226         $filter = $version->filter;
227 
228         if ($filter)
229         {
230             $this->apply_filter($image, $filter);
231         }
232 
233         #
234         # apply the overlay
235         #
236 
237         if ($version->overlay)
238         {
239             $overlay_file = \ICanBoogie\DOCUMENT_ROOT . $version->overlay;
240 
241             list($o_w, $o_h) = getimagesize($overlay_file);
242 
243             $overlay_source = imagecreatefrompng($overlay_file);
244 
245             imagecopyresampled($image, $overlay_source, 0, 0, 0, 0, $w, $h, $o_w, $o_h);
246         }
247 
248         #
249         # interlace
250         #
251 
252         if (!$version->no_interlace)
253         {
254             imageinterlace($image, true);
255         }
256 
257         #
258         # choose export format
259         #
260 
261         $format = $version->format;
262 
263         static $functions = array
264         (
265             'gif' => 'imagegif',
266             'jpeg' => 'imagejpeg',
267             'png' => 'imagepng'
268         );
269 
270         $function = $functions[$format];
271         $args = array($image, $destination);
272 
273         if ($format == 'jpeg')
274         {
275             #
276             # add quality option for the 'jpeg' format
277             #
278 
279             $args[] = $version->quality;
280         }
281         else if ($format == 'png' && !$callback)
282         {
283             #
284             # If there is no background callback defined, the image is defined as transparent in
285             # order to obtain a transparent thumbnail when the resulting image is centered.
286             #
287 
288             imagealphablending($image, false);
289             imagesavealpha($image, true);
290         }
291 
292         $rc = call_user_func_array($function, $args);
293 
294         imagedestroy($image);
295 
296         if (!$rc)
297         {
298             throw new Exception('Unable to save thumbnail');
299         }
300 
301         return $destination;
302     }
303 
304     protected function apply_filter($image, $filter)
305     {
306         if ($filter != 'grayscale')
307         {
308             return;
309         }
310 
311         imagefilter($image, IMG_FILTER_GRAYSCALE);
312     }
313 
314     protected function validate(\ICanBoogie\Errors $errors)
315     {
316         return true;
317     }
318 
319     /**
320      * Operation interface to the {@link get()} method.
321      *
322      * The function uses the {@link get()} method to obtain the location of the image version.
323      * A HTTP redirection is made to the location of the image.
324      *
325      * A HTTPException exception with code 404 is thrown if the function fails to obtain the
326      * location of the image version.
327      *
328      * @throws HTTPError
329      */
330     protected function process()
331     {
332         $this->rescue_uri();
333 
334         $path = $this->get();
335 
336         if (!$path)
337         {
338             throw new HTTPError(\ICanBoogie\format('Unable to create thumbnail for: %src', array('%src' => $this->request['src'])), 404);
339         }
340 
341         $request = $this->request;
342         $response = $this->response;
343 
344         $server_location = \ICanBoogie\DOCUMENT_ROOT . $path;
345         $stat = stat($server_location);
346         $etag = md5($path);
347 
348         #
349         # The expiration date is set to seven days.
350         #
351 
352         session_cache_limiter('public');
353 
354         $response->cache_control->cacheable = 'public';
355         $response->etag = $etag;
356         $response->expires = '+1 week';
357         $response->headers['X-Generated-By'] = 'Icybee/Thumbnailer';
358 
359         if ($request->cache_control->cacheable != 'no-cache')
360         {
361             $if_none_match = $request->headers['If-None-Match'];
362             $if_modified_since = $request->headers['If-Modified-Since'];
363 
364             if ($if_modified_since && $if_modified_since->timestamp >= $stat['mtime']
365             && $if_none_match && trim($if_none_match) == $etag)
366             {
367                 $response->status = 304;
368 
369                 #
370                 # WARNING: do *not* send any data after that
371                 #
372 
373                 return true;
374             }
375         }
376 
377         $pos = strrpos($path, '.');
378         $type = substr($path, $pos + 1);
379 
380         $response->last_modified = $stat['mtime'];
381         $response->content_type = "image/$type";
382         $response->content_length = $stat['size'];
383 
384         return function() use ($server_location)
385         {
386             $fh = fopen($server_location, 'rb');
387 
388             fpassthru($fh);
389 
390             fclose($fh);
391         };
392     }
393 
394     static private function decode_background($background)
395     {
396         $parts = explode(',', $background);
397 
398         $parts[0] = Image::decode_color($parts[0]);
399 
400         if (count($parts) == 1)
401         {
402             return array($parts[0], null, 0);
403         }
404 
405         $parts[1] = Image::decode_color($parts[1]);
406 
407         return $parts;
408     }
409 
410     static public function fill_callback($image, $w, $h)
411     {
412         #
413         # We create Image::drawGrid() arguments from the dimensions of the image
414         # and the values passed using the 'background' parameter.
415         #
416 
417         $args = (array) self::$background;
418 
419         array_unshift($args, $image, 0, 0, $w - 1, $h - 1);
420 
421         call_user_func_array('ICanBoogie\Image::draw_grid', $args);
422     }
423 
424     /**
425      * Under some strange circumstances, IE6 uses URL with encoded entities. This function tries
426      * to rescue the bullied URIs.
427      *
428      * The decoded parameters are set in the operation's params property.
429      */
430     private function rescue_uri()
431     {
432         $query = $this->request->query_string;
433 
434         if (strpos($query, '&amp;') === false)
435         {
436             return;
437         }
438 
439         $query = html_entity_decode($query);
440 
441         $rc = parse_str($query, $this->request->params);
442     }
443 }
Autodoc API documentation generated by ApiGen 2.8.0