Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • Button
  • DateBox
  • Image
  • RecordSelector
  • StringBox
  • TagCloud
  • TextBox
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * A widget that can generate an image which is scaled to the screen resolution of the
  5:  * user, and can be made secured to prevent hot-linking from remote sites.  Note that by
  6:  * default, a jpg file will be returned (the source file can be jpg, png, or gif).
  7:  *
  8:  * @package alpha::view::widgets
  9:  * @since 1.0
 10:  * @author John Collins <dev@alphaframework.org>
 11:  * @version $Id: Image.inc 1669 2013-09-12 20:38:17Z alphadevx $
 12:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 13:  * @copyright Copyright (c) 2013, John Collins (founder of Alpha Framework).
 14:  * All rights reserved.
 15:  *
 16:  * <pre>
 17:  * Redistribution and use in source and binary forms, with or
 18:  * without modification, are permitted provided that the
 19:  * following conditions are met:
 20:  *
 21:  * * Redistributions of source code must retain the above
 22:  *   copyright notice, this list of conditions and the
 23:  *   following disclaimer.
 24:  * * Redistributions in binary form must reproduce the above
 25:  *   copyright notice, this list of conditions and the
 26:  *   following disclaimer in the documentation and/or other
 27:  *   materials provided with the distribution.
 28:  * * Neither the name of the Alpha Framework nor the names
 29:  *   of its contributors may be used to endorse or promote
 30:  *   products derived from this software without specific
 31:  *   prior written permission.
 32:  *
 33:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 34:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 35:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 36:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 37:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 38:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 39:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 40:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 41:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 42:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 43:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 44:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 45:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 46:  * </pre>
 47:  *
 48:  */
 49: class Image {
 50:     /**
 51:      * The title of the image for alt text (optional).
 52:      *
 53:      * @var string
 54:      * @since 1.0
 55:      */
 56:     private $title;
 57: 
 58:     /**
 59:      * The absolute path to the source image.
 60:      *
 61:      * @var string
 62:      * @since 1.0
 63:      */
 64:     private $source;
 65: 
 66:     /**
 67:      * The width of the image (can differ from the source file when scale=true).
 68:      *
 69:      * @var Integer
 70:      * @since 1.0
 71:      */
 72:     private $width;
 73: 
 74:     /**
 75:      * The height of the image (can differ from the source file when scale=true).
 76:      *
 77:      * @var Integer
 78:      * @since 1.0
 79:      */
 80:     private $height;
 81: 
 82:     /**
 83:      * The file type of the source image (gif, jpg, or png supported)
 84:      *
 85:      * @var Enum
 86:      * @since 1.0
 87:      */
 88:     private $sourceType;
 89: 
 90:     /**
 91:      * The quality of the jpg image generated (0.00 to 1.00, 0.75 by default).
 92:      *
 93:      * @var Double
 94:      * @since 1.0
 95:      */
 96:     private $quality;
 97: 
 98:     /**
 99:      * Flag to determine if the image will scale to match the target resolution (false
100:      * by default).
101:      *
102:      * @var Boolean
103:      * @since 1.0
104:      */
105:     private $scale;
106: 
107:     /**
108:      * Flag to determine if the link to the image will change every 24hrs, making hot-linking
109:      * to the image difficult (false by default).
110:      *
111:      * @var Boolean
112:      * @since 1.0
113:      */
114:     private $secure;
115: 
116:     /**
117:      * The auto-generated name of the cache file for the image.
118:      *
119:      * @var string
120:      * @since 1.0
121:      */
122:     private $filename;
123: 
124:     /**
125:      * Trace logger
126:      *
127:      * @var Logger
128:      * @since 1.0
129:      */
130:     private static $logger = null;
131: 
132:     /**
133:      * The constructor.
134:      *
135:      * @param $source
136:      * @param $width
137:      * @param $height
138:      * @param $sourceType
139:      * @param $quality
140:      * @param $scale
141:      * @throws IllegalArguementException
142:      * @since 1.0
143:      */
144:     public function __construct($source, $width, $height, $sourceType, $quality=0.75, $scale=false, $secure=false) {
145:         self::$logger = new Logger('Image');
146:         self::$logger->debug('>>__construct(source=['.$source.'], width=['.$width.'], height=['.$height.'], sourceType=['.$sourceType.'], quality=['.$quality.'], scale=['.$scale.'], secure=['.$secure.'])');
147: 
148:         global $config;
149: 
150:         if(file_exists($source))
151:             $this->source = $source;
152:         else
153:             throw new IllegalArguementException('The source file for the Image widget ['.$source.'] cannot be found!');
154: 
155:         $this->sourceType = new Enum();
156:         $this->sourceType->setOptions(array('jpg','png','gif'));
157:         $this->sourceType->setValue($sourceType);
158: 
159:         if($quality < 0.0 || $quality > 1.0)
160:             throw new IllegalArguementException('The quality setting of ['.$quality.'] is outside of the allowable range of 0.0 to 1.0');
161: 
162:         $this->quality = new Double($quality);
163:         $this->scale = new Boolean($scale);
164:         $this->secure = new Boolean($secure);
165: 
166:         $this->width = new Integer($width);
167:         $this->height = new Integer($height);
168: 
169:         $this->setFilename();
170: 
171:         self::$logger->debug('<<__construct');
172:     }
173: 
174:     /**
175:      * Renders the HTML <img> tag to the ViewImage controller, with all of the correct params to render the source
176:      * image in the desired resolution.
177:      *
178:      * @param $altText Set this value to render alternate text as part of the HTML link (defaults to no alternate text)
179:      * @return string
180:      * @since 1.0
181:      */
182:     public function renderHTMLLink($altText='') {
183:         global $config;
184: 
185:         if($this->secure->getBooleanValue()) {
186:             $params = AlphaController::generateSecurityFields();
187:             return '<img src="'.FrontController::generateSecureURL('act=ViewImage&s='.$this->source.'&w='.$this->width->getValue().'&h='.$this->height->getValue().'&t='.$this->sourceType->getValue().'&q='.$this->quality->getValue().'&sc='.$this->scale->getValue().'&se='.$this->secure->getValue().'&var1='.urlencode($params[0]).'&var2='.urlencode($params[1])).'"'.(empty($altText) ? '' : ' alt="'.$altText.'"').'/>';
188:         }else{
189:             return '<img src="'.FrontController::generateSecureURL('act=ViewImage&s='.$this->source.'&w='.$this->width->getValue().'&h='.$this->height->getValue().'&t='.$this->sourceType->getValue().'&q='.$this->quality->getValue().'&sc='.$this->scale->getValue().'&se='.$this->secure->getValue()).'"'.(empty($altText) ? '' : ' alt="'.$altText.'"').'/>';
190:         }
191:     }
192: 
193:     /**
194:      * Setter for the filename, which also creates a sub-directory under /cache for images when required
195:      *
196:      * @since 1.0
197:      * @throws AlphaException
198:      */
199:     private function setFilename() {
200:         global $config;
201: 
202:         if(!strpos($this->source, 'attachments/article_')) {
203:             // checking to see if we will write a jpg or png to the cache
204:             if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png'))
205:                 $this->filename = $config->get('app.file.store.dir').'cache/images/'.basename($this->source, ".".$this->sourceType->getValue()).'_'.$this->width->getValue().'x'.$this->height->getValue().'.png';
206:             else
207:                 $this->filename = $config->get('app.file.store.dir').'cache/images/'.basename($this->source, ".".$this->sourceType->getValue()).'_'.$this->width->getValue().'x'.$this->height->getValue().'.jpg';
208:         }else{
209:             // make a cache dir for the article
210:             $cacheDir = $config->get('app.file.store.dir').'cache/images/article_'.substr($this->source, strpos($this->source, 'attachments/article_')+20, 11);
211:             if(!file_exists($cacheDir)) {
212:                 $success = mkdir($cacheDir);
213: 
214:                 if (!$success) {
215:                     throw new AlphaException('Unable to create the folder '.$cacheDir.' for the cache image, source file is '.$this->source);
216:                 }
217: 
218:                 // ...and set write permissions on the folder
219:                 $success = chmod($cacheDir, 0777);
220: 
221:                 if (!$success) {
222:                     throw new AlphaException('Unable to set write permissions on the folder ['.$cacheDir.'].');
223:                 }
224:             }
225: 
226:             // now set the filename to include the new cache directory
227:             if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png'))
228:                 $this->filename = $cacheDir.'/'.basename($this->source, ".".$this->sourceType->getValue()).'_'.$this->width->getValue().'x'.$this->height->getValue().'.png';
229:             else
230:                 $this->filename = $cacheDir.'/'.basename($this->source, ".".$this->sourceType->getValue()).'_'.$this->width->getValue().'x'.$this->height->getValue().'.jpg';
231:         }
232: 
233:         self::$logger->debug('Image filename is ['.$this->filename.']');
234:     }
235: 
236:     /**
237:      * Gets the auto-generated filename for the image in the /cache directory
238:      *
239:      * @since 1.0
240:      */
241:     public function getFilename() {
242:         return $this->filename;
243:     }
244: 
245:     /**
246:      * Renders the actual binary image using GD library calls.
247:      *
248:      *  @since 1.0
249:      */
250:     public function renderImage() {
251:         global $config;
252: 
253:         /*
254:          * Handle secure image validation (if enabled in config).
255:          * Not required for the view_article_pdf.php controller.
256:          */
257:         if($config->get('cms.images.widget.secure') && basename($_SERVER["PHP_SELF"]) != 'ViewArticlePDF.php') {
258:             // will need to decode the secure vars on the query string first as they will be encoded in the link to this controller
259:             $_REQUEST['var1'] = urldecode($_REQUEST['var1']);
260:             $_REQUEST['var2'] = urldecode($_REQUEST['var2']);
261:             $valid = AlphaController::checkSecurityFields();
262: 
263:             // if not valid, just return a blank black image of the same dimensions
264:             if(!$valid) {
265:                 $im  = imagecreatetruecolor($this->width->getValue(), $this->height->getValue());
266:                 $bgc = imagecolorallocate($im, 0, 0, 0);
267:                 imagefilledrectangle($im, 0, 0, $this->width->getValue(), $this->height->getValue(), $bgc);
268: 
269:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
270:                     header("Content-Type: image/png");
271:                     imagepng($im);
272:                 }else{
273:                     header("Content-Type: image/jpeg");
274:                     imagejpeg($im);
275:                 }
276: 
277:                 imagedestroy($im);
278: 
279:                 self::$logger->warn('The client ['.$_SERVER['HTTP_USER_AGENT'].'] was blocked from accessing the file ['.$this->filename.'] due to bad security tokens being provided');
280:             }
281:         }
282: 
283:         // if scaled, we need to compute the target image size
284:         if($this->scale->getBooleanValue() && isset($_COOKIE['screenSize'])) {
285:             $originalScreenResolution = explode('x', $config->get('sysCMSImagesWidgetScreenResolution'));
286:             $originalScreenX = $originalScreenResolution[0];
287:             $originalScreenY = $originalScreenResolution[1];
288: 
289:             $targetScreenResolution = explode('x', $_COOKIE['screenSize']);
290:             $targetScreenX = $targetScreenResolution[0];
291:             $targetScreenY = $targetScreenResolution[1];
292: 
293:             // calculate the new units we will scale by
294:             $xu = $targetScreenX/$originalScreenX;
295:             $yu = $targetScreenY/$originalScreenY;
296: 
297:             $this->width = new Integer(intval($this->width->getValue()*$xu));
298:             $this->height = new Integer(intval($this->height->getValue()*$yu));
299: 
300:             // need to update the cache filename as the dimensions have changed
301:             $this->setFilename();
302:         }
303: 
304:         // check the image cache first before we proceed
305:         if ($this->checkCache()) {
306:             $this->loadCache();
307:         }else{
308:             // now get the old image
309:             switch ($this->sourceType->getValue()) {
310:                 case "gif":
311:                     $old_image = imagecreatefromgif($this->source);
312:                 break;
313:                 case "jpg":
314:                     $old_image = imagecreatefromjpeg($this->source);
315:                 break;
316:                 case "png":
317:                     $old_image = imagecreatefrompng($this->source);
318:                 break;
319:             }
320: 
321:             if (!$old_image) {
322:                 $im  = imagecreatetruecolor($this->width->getValue(), $this->height->getValue());
323:                 $bgc = imagecolorallocate($im, 255, 255, 255);
324:                 $tc  = imagecolorallocate($im, 0, 0, 0);
325:                 imagefilledrectangle($im, 0, 0, $this->width->getValue(), $this->height->getValue(), $bgc);
326: 
327:                 imagestring($im, 1, 5, 5, "Error loading $this->source", $tc);
328:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
329:                     header("Content-Type: image/png");
330:                     imagepng($im);
331:                 }else{
332:                     header("Content-Type: image/jpeg");
333:                     imagejpeg($im);
334:                 }
335:                 imagedestroy($im);
336:             }else{
337:                 // the dimensions of the source image
338:                 $oldWidth = imagesx($old_image);
339:                 $oldHeight = imagesy($old_image);
340: 
341:                 // now create the new image
342:                 $new_image = imagecreatetruecolor($this->width->getValue(), $this->height->getValue());
343: 
344:                 // set a transparent background for PNGs
345:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
346:                     // Turn off transparency blending (temporarily)
347:                     imagealphablending($new_image, false);
348: 
349:                     // Create a new transparent color for image
350:                     $color = imagecolorallocatealpha($new_image, 255, 0, 0, 0);
351: 
352:                     // Completely fill the background of the new image with allocated color.
353:                     imagefill($new_image, 0, 0, $color);
354: 
355:                     // Restore transparency blending
356:                     imagesavealpha($new_image, true);
357:                 }
358:                 // copy the old image to the new image (in memory, not the file!)
359:                 imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $this->width->getValue(), $this->height->getValue(), $oldWidth, $oldHeight); 
360: 
361:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
362:                     header("Content-Type: image/png");
363:                     imagepng($new_image);
364:                 }else{
365:                     header("Content-Type: image/jpeg");
366:                     imagejpeg($new_image, null, 100*$this->quality->getValue());
367:                 }
368: 
369:                 $this->cache($new_image);
370:                 imagedestroy($old_image);
371:                 imagedestroy($new_image);
372:             }
373:         }
374:     }
375: 
376:     /**
377:      * Caches the image to the cache directory
378:      *
379:      * @param image $image the binary GD image stream to save
380:      * @since 1.0
381:      */
382:     private function cache($image) {
383:         global $config;
384: 
385:         if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png'))
386:             imagepng($image, $this->filename);
387:         else
388:             imagejpeg($image, $this->filename, 100*$this->quality->getValue());
389:     }
390: 
391:     /**
392:      * Used to check the image cache for the image jpeg cache file.
393:      *
394:      * @return bool
395:      * @since 1.0
396:      */
397:     private function checkCache() {
398:         return file_exists($this->filename);
399:     }
400: 
401:     /**
402:      * Method to load the content of the image cache file to the standard output stream (the browser)
403:      *
404:      * @since 1.0
405:      */
406:     private function loadCache() {
407:         readfile($this->filename);
408:     }
409: 
410:     /**
411:      * Converts a URL for an image to a relative file system path for the image, assuming it is
412:      * hosted on the same server as the application.
413:      *
414:      * @param string $imgURL
415:      * @return string the path of the image
416:      * @since 1.0
417:      */
418:     public static function convertImageURLToPath($imgURL) {
419:         global $config;
420: 
421:         $imgPath = str_replace($config->get('app.url'), '', $imgURL);
422: 
423:         return $imgPath;
424:     }
425: }
426: 
427: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0