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 1568 2012-08-05 19:59:16Z alphadevx $
 12:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 13:  * @copyright Copyright (c) 2012, 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='.$params[0].'&var2='.$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:             $valid = AlphaController::checkSecurityFields();            
259:             
260:             // if not valid, just return a blank black image of the same dimensions
261:             if(!$valid) {
262:                 $im  = imagecreatetruecolor($this->width->getValue(), $this->height->getValue()); /* Create a blank image */ 
263:                 $bgc = imagecolorallocate($im, 0, 0, 0);                
264:                 imagefilledrectangle($im, 0, 0, $this->width->getValue(), $this->height->getValue(), $bgc); 
265:                 
266:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
267:                     header("Content-Type: image/png");
268:                     imagepng($im);
269:                 }else{
270:                     header("Content-Type: image/jpeg");
271:                     imagejpeg($im);
272:                 }
273:                 
274:                 imagedestroy($im);
275:                 
276:                 self::$logger->warn('The client ['.$_SERVER['HTTP_USER_AGENT'].'] was blocked from accessing the file ['.$this->filename.'] due to bad security tokens being provided');
277:             }
278:         }
279:         
280:         // if scaled, we need to compute the target image size
281:         if($this->scale->getBooleanValue() && isset($_COOKIE['screenSize'])) {
282:             $originalScreenResolution = explode('x', $config->get('sysCMSImagesWidgetScreenResolution'));
283:             $originalScreenX = $originalScreenResolution[0];
284:             $originalScreenY = $originalScreenResolution[1];
285:                 
286:             $targetScreenResolution = explode('x', $_COOKIE['screenSize']);
287:             $targetScreenX = $targetScreenResolution[0];
288:             $targetScreenY = $targetScreenResolution[1];
289:                 
290:             // calculate the new units we will scale by 
291:             $xu = $targetScreenX/$originalScreenX;
292:             $yu = $targetScreenY/$originalScreenY;
293:                 
294:             $this->width = new Integer(intval($this->width->getValue()*$xu));
295:             $this->height = new Integer(intval($this->height->getValue()*$yu));
296:             
297:             // need to update the cache filename as the dimensions have changed
298:             $this->setFilename();
299:         }
300:         
301:         // check the image cache first before we proceed
302:         if ($this->checkCache()) {
303:             $this->loadCache();         
304:         }else{
305:             // now get the old image
306:             switch ($this->sourceType->getValue()) {
307:                 case "gif":
308:                     $old_image = imagecreatefromgif($this->source);
309:                 break;
310:                 case "jpg":
311:                     $old_image = imagecreatefromjpeg($this->source);
312:                 break;
313:                 case "png":
314:                     $old_image = imagecreatefrompng($this->source);
315:                 break;
316:             }
317:             
318:             if (!$old_image) { /* See if it failed */ 
319:                 $im  = imagecreatetruecolor($this->width->getValue(), $this->height->getValue()); /* Create a blank image */ 
320:                 $bgc = imagecolorallocate($im, 255, 255, 255); 
321:                 $tc  = imagecolorallocate($im, 0, 0, 0); 
322:                 imagefilledrectangle($im, 0, 0, $this->width->getValue(), $this->height->getValue(), $bgc); 
323:                 
324:                 imagestring($im, 1, 5, 5, "Error loading $this->source", $tc);
325:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
326:                     header("Content-Type: image/png");
327:                     imagepng($im);
328:                 }else{
329:                     header("Content-Type: image/jpeg");
330:                     imagejpeg($im);
331:                 }
332:                 imagedestroy($im);
333:             }else{
334:                 // the dimensions of the source image
335:                 $oldWidth = imagesx($old_image);
336:                 $oldHeight = imagesy($old_image);
337:             
338:                 // now create the new image
339:                 $new_image = imagecreatetruecolor($this->width->getValue(), $this->height->getValue());
340:             
341:                 // set a transparent background for PNGs
342:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
343:                     // Turn off transparency blending (temporarily)
344:                     imagealphablending($new_image, false);
345:             
346:                     // Create a new transparent color for image
347:                     $color = imagecolorallocatealpha($new_image, 255, 0, 0, 0);
348:             
349:                     // Completely fill the background of the new image with allocated color.
350:                     imagefill($new_image, 0, 0, $color);
351:             
352:                     // Restore transparency blending
353:                     imagesavealpha($new_image, true);
354:                 }
355:                 // copy the old image to the new image (in memory, not the file!)
356:                 imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $this->width->getValue(), $this->height->getValue(), $oldWidth, $oldHeight); 
357:                 
358:                 if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png')) {
359:                     header("Content-Type: image/png");
360:                     imagepng($new_image, basename($this->filename));
361:                 }else{
362:                     header("Content-Type: image/jpeg");
363:                     imagejpeg($new_image, basename($this->filename), 100*$this->quality->getValue());
364:                 }
365:                 
366:                 $this->cache($new_image);
367:                 imagedestroy($old_image);
368:                 imagedestroy($new_image);
369:             }
370:         }
371:     }
372:     
373:     /**
374:      * Caches the image to the cache directory
375:      * 
376:      * @param image $image the binary GD image stream to save
377:      * @since 1.0
378:      */
379:     private function cache($image) {
380:         global $config;
381:         
382:         if($this->sourceType->getValue() == 'png' && $config->get('cms.images.perserve.png'))
383:             imagepng($image, $this->filename);
384:         else
385:             imagejpeg($image, $this->filename, 100*$this->quality->getValue());
386:     }
387:     
388:     /**
389:      * Used to check the image cache for the image jpeg cache file.
390:      *   
391:      * @return bool
392:      * @since 1.0
393:      */
394:     private function checkCache() {
395:         return file_exists($this->filename);
396:     }
397:     
398:     /**
399:      * Method to load the content of the image cache file to the standard output stream (the browser)
400:      * 
401:      * @since 1.0
402:      */
403:     private function loadCache() {      
404:         readfile($this->filename);      
405:     }
406:     
407:     /**
408:      * Converts a URL for an image to a relative file system path for the image, assuming it is
409:      * hosted on the same server as the application.
410:      * 
411:      * @param string $imgURL
412:      * @return string the path of the image
413:      * @since 1.0
414:      */
415:     public static function convertImageURLToPath($imgURL) {
416:         global $config;
417:         
418:         $imgPath = str_replace($config->get('app.url'), '', $imgURL);
419: 
420:         return $imgPath;
421:     }
422: }
423: 
424: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0