root / trunk / plugins / ImageManager / Classes / ImageEditor.php @ 709

Revision 709, 11.2 kB (checked in by ray, 6 years ago)

Ticket #928 ImageManager fails if (another) Files.php exists in include path

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
Line 
1<?php
2/**
3 * Image Editor. Editing tools, crop, rotate, scale and save.
4 * @author $Author$
5 * @version $Id$
6 * @package ImageManager
7 */
8
9require_once('../ImageManager/Classes/Transform.php');
10
11/**
12 * Handles the basic image editing capbabilities.
13 * @author $Author$
14 * @version $Id$
15 * @package ImageManager
16 * @subpackage Editor
17 */
18class ImageEditor
19{
20    /**
21     * ImageManager instance.
22     */
23    var $manager;
24
25    /**
26     * user based on IP address
27     */
28    var $_uid;
29
30    /**
31     * tmp file storage time.
32     */
33    var $lapse_time =900; //15 mins
34
35    var $filesaved = 0;
36
37    /**
38     * Create a new ImageEditor instance. Editing requires a
39     * tmp file, which is saved in the current directory where the
40     * image is edited. The tmp file is assigned by md5 hash of the
41     * user IP address. This hashed is used as an ID for cleaning up
42     * the tmp files. In addition, any tmp files older than the
43     * the specified period will be deleted.
44     * @param ImageManager $manager the image manager, we need this
45     * for some file and path handling functions.
46     */
47    function ImageEditor($manager)
48    {
49        $this->manager = $manager;
50        $this->_uid = md5($_SERVER['REMOTE_ADDR']);
51    }
52   
53    /**
54     * Did we save a file?
55     * @return int 1 if the file was saved sucessfully,
56     * 0 no save operation, -1 file save error.
57     */
58    function isFileSaved()
59    {
60        Return $this->filesaved;
61    }
62
63    /**
64     * Process the image, if not action, just display the image.
65     * @return array with image information, empty array if not an image.
66     * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
67     * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
68     */
69    function processImage()
70    {
71        if(isset($_GET['img']))
72            $relative = rawurldecode($_GET['img']);
73        else
74            Return array();
75       
76        //$relative = '/Series2004NoteFront.jpg';
77
78        $imgURL = $this->manager->getFileURL($relative);
79        $fullpath = $this->manager->getFullPath($relative);
80       
81        $imgInfo = @getImageSize($fullpath);
82        if(!is_array($imgInfo))
83            Return array();
84
85        $action = $this->getAction();
86
87        if(!is_null($action))
88        {
89            $image = $this->processAction($action, $relative, $fullpath);
90        }
91        else
92        {
93            $image['src'] = $imgURL;
94            $image['dimensions'] = $imgInfo[3];
95            $image['file'] = $relative;
96            $image['fullpath'] = $fullpath;
97        }
98
99        Return $image;
100    }
101
102    /**
103     * Process the actions, crop, scale(resize), rotate, flip, and save.
104     * When ever an action is performed, the result is save into a
105     * temporary image file, see createUnique on the filename specs.
106     * It does not return the saved file, alway returning the tmp file.
107     * @param string $action, should be 'crop', 'scale', 'rotate','flip', or 'save'
108     * @param string $relative the relative image filename
109     * @param string $fullpath the fullpath to the image file
110     * @return array with image information
111     * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
112     * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
113     */
114    function processAction($action, $relative, $fullpath)
115    {
116        $params = '';
117       
118        if(isset($_GET['params']))
119            $params = $_GET['params'];
120
121        $values explode(',',$params,4);
122        $saveFile = $this->getSaveFileName($values[0]);
123
124        $img = Image_Transform::factory(IMAGE_CLASS);
125        $img->load($fullpath);
126
127        switch ($action)
128        {
129            case 'crop':
130                $img->crop(intval($values[0]),intval($values[1]),
131                            intval($values[2]),intval($values[3]));
132            break;
133            case 'scale':
134                $img->resize(intval($values[0]),intval($values[1]));
135                break;
136            case 'rotate':
137                $img->rotate(floatval($values[0]));
138                break;
139            case 'flip':
140                if ($values[0] == 'hoz')
141                    $img->flip(true);
142                else if($values[0] == 'ver')
143                    $img->flip(false);
144                break;
145            case 'save':
146                if(!is_null($saveFile))
147                {
148                    $quality = intval($values[1]);
149                    if($quality <0) $quality = 85;
150                    $newSaveFile = $this->makeRelative($relative, $saveFile);
151                    $newSaveFile = $this->getUniqueFilename($newSaveFile);
152                   
153                    //get unique filename just returns the filename, so
154                    //we need to make the relative path once more.
155                    $newSaveFile = $this->makeRelative($relative, $newSaveFile);
156          $image['saveFile'] = $newSaveFile;
157                    $newSaveFullpath = $this->manager->getFullPath($newSaveFile);
158                    $img->save($newSaveFullpath, $values[0], $quality);
159                    if(is_file($newSaveFullpath))
160                        $this->filesaved = 1;
161                    else
162                        $this->filesaved = -1;
163                }
164                break;
165        }
166       
167        //create the tmp image file
168        $filename = $this->createUnique($fullpath);
169        $newRelative = $this->makeRelative($relative, $filename);
170        $newFullpath = $this->manager->getFullPath($newRelative);
171        $newURL = $this->manager->getFileURL($newRelative);
172       
173        //save the file.
174        $img->save($newFullpath);
175        $img->free();
176
177        //get the image information
178        $imgInfo = @getimagesize($newFullpath);
179
180        $image['src'] = $newURL;
181    $image['width'] = $imgInfo[0];
182    $image['height'] = $imgInfo[1];
183        $image['dimensions'] = $imgInfo[3];
184        $image['file'] = $newRelative;
185        $image['fullpath'] = $newFullpath;
186
187        Return $image;
188   
189    }
190
191    /**
192     * Get the file name base on the save name
193     * and the save type.
194     * @param string $type image type, 'jpeg', 'png', or 'gif'
195     * @return string the filename according to save type
196     */
197    function getSaveFileName($type)
198    {
199        if(!isset($_GET['file']))
200            Return null;
201
202        $filename = Files::escape(rawurldecode($_GET['file']));
203        $index = strrpos($filename,'.');
204        $base = substr($filename,0,$index);
205        $ext = strtolower(substr($filename,$index+1,strlen($filename)));
206
207        if($type == 'jpeg' && !($ext=='jpeg' || $ext=='jpg'))
208        {
209            Return $base.'.jpeg';
210        }
211        if($type=='png' && $ext != 'png')
212            Return $base.'.png';
213        if($type=='gif' && $ext != 'gif')
214            Return $base.'.gif';
215
216        Return $filename;
217    }
218
219    /**
220     * Get the default save file name, used by editor.php.
221     * @return string a suggestive filename, this should be unique
222     */
223    function getDefaultSaveFile()
224    {
225        if(isset($_GET['img']))
226            $relative = rawurldecode($_GET['img']);
227        else
228            Return null;
229
230        Return $this->getUniqueFilename($relative);
231    }
232
233    /**
234     * Get a unique filename. If the file exists, the filename
235     * base is appended with an increasing integer.
236     * @param string $relative the relative filename to the base_dir
237     * @return string a unique filename in the current path
238     */
239    function getUniqueFilename($relative)
240    {
241        $fullpath = $this->manager->getFullPath($relative);
242       
243        $pathinfo = pathinfo($fullpath);
244
245        $path = Files::fixPath($pathinfo['dirname']);
246        $file = Files::escape($pathinfo['basename']);
247       
248        $filename = $file;
249
250        $dotIndex = strrpos($file, '.');
251        $ext = '';
252
253        if(is_int($dotIndex))
254        {
255            $ext = substr($file, $dotIndex);
256            $base = substr($file, 0, $dotIndex);
257        }
258
259        $counter = 0;
260        while(is_file($path.$filename))
261        {
262            $counter++;
263            $filename = $base.'_'.$counter.$ext;
264        }
265       
266        Return $filename;
267       
268    }
269
270    /**
271     * Specifiy the original relative path, a new filename
272     * and return the new filename with relative path.
273     * i.e. $pathA (-filename) + $file
274     * @param string $pathA the relative file
275     * @param string $file the new filename
276     * @return string relative path with the new filename
277     */
278    function makeRelative($pathA, $file)
279    {
280        $index = strrpos($pathA,'/');
281        if(!is_int($index))
282            Return $file;
283
284        $path = substr($pathA, 0, $index);
285        Return Files::fixPath($path).$file;
286    }
287
288    /**
289     * Get the action GET parameter
290     * @return string action parameter
291     */
292    function getAction()
293    {
294        $action = null;
295        if(isset($_GET['action']))
296            $action = $_GET['action'];
297        Return $action;
298    }
299
300    /**
301     * Generate a unique string based on md5(microtime()).
302     * Well not so uniqe, as it is limited to 6 characters
303     * @return string unique string.
304     */
305    function uniqueStr()
306    {
307      return substr(md5(microtime()),0,6);
308    }
309
310    /**
311     * Create unique tmp image file name.
312     * The filename is based on the tmp file prefix
313     * specified in config.inc.php plus
314     * the UID (basically a md5 of the remote IP)
315     * and some random 6 character string.
316     * This function also calls to clean up the tmp files.
317     * @param string $file the fullpath to a file
318     * @return string a unique filename for that path
319     * NOTE: it only returns the filename, path no included.
320     */
321    function createUnique($file)
322    {
323        $pathinfo = pathinfo($file);
324        $path = Files::fixPath($pathinfo['dirname']);
325        $imgType = $this->getImageType($file);
326
327        $unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
328
329       //make sure the the unique temp file does not exists
330        while (file_exists($path.$unique_str))
331        {
332            $unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
333        }
334
335        $this->cleanUp($path,$pathinfo['basename']);
336
337        Return $unique_str;
338    }
339
340    /**
341     * Delete any tmp image files.
342     * @param string $path the full path
343     * where the clean should take place.
344     */
345    function cleanUp($path,$file)
346    {
347        $path = Files::fixPath($path);
348
349        if(!is_dir($path))
350            Return false;
351
352        $d = @dir($path);
353       
354        $tmp = $this->manager->getTmpPrefix();
355        $tmpLen = strlen($tmp);
356
357        $prefix = $tmp.$this->_uid;
358        $len = strlen($prefix);
359
360        while (false !== ($entry = $d->read()))
361        {
362            //echo $entry."<br>";
363            if(is_file($path.$entry) && $this->manager->isTmpFile($entry))
364            {
365                if(substr($entry,0,$len)==$prefix && $entry != $file)
366                    Files::delFile($path.$entry);
367                else if(substr($entry,0,$tmpLen)==$tmp && $entry != $file)
368                {
369                    if(filemtime($path.$entry)+$this->lapse_time < time())
370                        Files::delFile($path.$entry);
371                }
372            }
373        }
374        $d->close();
375    }
376
377    /**
378     * Get the image type base on an image file.
379     * @param string $file the full path to the image file.
380     * @return string of either 'gif', 'jpeg', 'png' or 'bmp'
381     * otherwise it will return null.
382     */
383    function getImageType($file)
384    {
385        $imageInfo = @getImageSize($file);
386
387        if(!is_array($imageInfo))
388            Return null;
389
390        switch($imageInfo[2])
391        {
392            case 1:
393                Return 'gif';
394            case 2:
395                Return 'jpeg';
396            case 3:
397                Return 'png';
398            case 6:
399                Return 'bmp';
400        }
401
402        Return null;
403    }
404
405    /**
406     * Check if the specified image can be edit by GD
407     * mainly to check that GD can read and save GIFs
408     * @return int 0 if it is not a GIF file, 1 is GIF is editable, -1 if not editable.
409     */
410    function isGDEditable()
411    {
412        if(isset($_GET['img']))
413            $relative = rawurldecode($_GET['img']);
414        else
415            Return 0;
416        if(IMAGE_CLASS != 'GD')
417            Return 0;
418
419        $fullpath = $this->manager->getFullPath($relative);
420
421        $type = $this->getImageType($fullpath);
422        if($type != 'gif')
423            Return 0;
424
425        if(function_exists('ImageCreateFrom'+$type)
426            && function_exists('image'+$type))
427            Return 1;
428        else
429            Return -1;
430    }
431
432    /**
433     * Check if GIF can be edit by GD.
434     * @return int 0 if it is not using the GD library, 1 is GIF is editable, -1 if not editable.
435     */
436    function isGDGIFAble()
437    {
438        if(IMAGE_CLASS != 'GD')
439            Return 0;
440
441        if(function_exists('ImageCreateFromGif')
442            && function_exists('imagegif'))
443            Return 1;
444        else
445            Return -1;
446    }
447}
448
449?>
Note: See TracBrowser for help on using the browser.