blob: 6c4a5d514abf1c1402994f2b360b1380d2ab75fd [file] [log] [blame]
<?php
/**
* ImageManager, list images, directories, and thumbnails.
* @author $Author:ray $
* @version $Id:ImageManager.php 709 2007-01-30 23:22:04Z ray $
* @package ImageManager
*/
require_once('../ImageManager/Classes/Files.php');
// uncomment to turn on debugging
// _ddtOn();
/**
* ImageManager Class.
* @author $Author:ray $
* @version $Id:ImageManager.php 709 2007-01-30 23:22:04Z ray $
*/
class ImageManager
{
/**
* Configuration array.
*/
var $config;
/**
* Array of directory information.
*/
var $dirs;
/**
* Constructor. Create a new Image Manager instance.
* @param array $config configuration array, see config.inc.php
*/
function ImageManager($config)
{
$this->config = $config;
}
/**
* Get the images base directory.
* @return string base dir, see config.inc.php
*/
function getImagesDir()
{
Return $this->config['images_dir'];
}
/**
* Get the images base URL.
* @return string base url, see config.inc.php
*/
function getImagesURL()
{
Return $this->config['images_url'];
}
function isValidBase()
{
return is_dir($this->getImagesDir());
}
/**
* Get the tmp file prefix.
* @return string tmp file prefix.
*/
function getTmpPrefix()
{
Return $this->config['tmp_prefix'];
}
/**
* Get the sub directories in the base dir.
* Each array element contain
* the relative path (relative to the base dir) as key and the
* full path as value.
* @return array of sub directries
* <code>array('path name' => 'full directory path', ...)</code>
*/
function getDirs()
{
if(is_null($this->dirs))
{
$dirs = $this->_dirs($this->getImagesDir(),'/');
ksort($dirs);
$this->dirs = $dirs;
}
return $this->dirs;
}
/**
* Recursively travese the directories to get a list
* of accessable directories.
* @param string $base the full path to the current directory
* @param string $path the relative path name
* @return array of accessiable sub-directories
* <code>array('path name' => 'full directory path', ...)</code>
*/
function _dirs($base, $path)
{
$base = Files::fixPath($base);
$dirs = array();
if($this->isValidBase() == false)
return $dirs;
$d = @dir($base);
while (false !== ($entry = $d->read()))
{
//If it is a directory, and it doesn't start with
// a dot, and if is it not the thumbnail directory
if(is_dir($base.$entry)
&& substr($entry,0,1) != '.'
&& $this->isThumbDir($entry) == false)
{
$relative = Files::fixPath($path.$entry);
$fullpath = Files::fixPath($base.$entry);
$dirs[$relative] = $fullpath;
$dirs = array_merge($dirs, $this->_dirs($fullpath, $relative));
}
}
$d->close();
Return $dirs;
}
/**
* Get all the files and directories of a relative path.
* @param string $path relative path to be base path.
* @return array of file and path information.
* <code>array(0=>array('relative'=>'fullpath',...), 1=>array('filename'=>fileinfo array(),...)</code>
* fileinfo array: <code>array('url'=>'full url',
* 'relative'=>'relative to base',
* 'fullpath'=>'full file path',
* 'image'=>imageInfo array() false if not image,
* 'stat' => filestat)</code>
*/
function getFiles($path)
{
$files = array();
$dirs = array();
if($this->isValidBase() == false)
return array($files,$dirs);
$path = Files::fixPath($path);
$base = Files::fixPath($this->getImagesDir());
$fullpath = Files::makePath($base,$path);
$d = @dir($fullpath);
while (false !== ($entry = $d->read()))
{
//not a dot file or directory
if(substr($entry,0,1) != '.')
{
if(is_dir($fullpath.$entry)
&& $this->isThumbDir($entry) == false)
{
$relative = Files::fixPath($path.$entry);
$full = Files::fixPath($fullpath.$entry);
$count = $this->countFiles($full);
$dirs[$relative] = array('fullpath'=>$full,'entry'=>$entry,'count'=>$count);
}
else if(is_file($fullpath.$entry) && $this->isThumb($entry)==false && $this->isTmpFile($entry) == false)
{
$img = $this->getImageInfo($fullpath.$entry);
if(!(!is_array($img)&&$this->config['validate_images']))
{
$file['url'] = Files::makePath($this->config['base_url'],$path).$entry;
$file['relative'] = $path.$entry;
$file['fullpath'] = $fullpath.$entry;
$file['image'] = $img;
$file['stat'] = stat($fullpath.$entry);
$files[$entry] = $file;
}
}
}
}
$d->close();
ksort($dirs);
ksort($files);
Return array($dirs, $files);
}
/**
* Count the number of files and directories in a given folder
* minus the thumbnail folders and thumbnails.
*/
function countFiles($path)
{
$total = 0;
if(is_dir($path))
{
$d = @dir($path);
while (false !== ($entry = $d->read()))
{
//echo $entry."<br>";
if(substr($entry,0,1) != '.'
&& $this->isThumbDir($entry) == false
&& $this->isTmpFile($entry) == false
&& $this->isThumb($entry) == false)
{
$total++;
}
}
$d->close();
}
return $total;
}
/**
* Get image size information.
* @param string $file the image file
* @return array of getImageSize information,
* false if the file is not an image.
*/
function getImageInfo($file)
{
Return @getImageSize($file);
}
/**
* Check if the file contains the thumbnail prefix.
* @param string $file filename to be checked
* @return true if the file contains the thumbnail prefix, false otherwise.
*/
function isThumb($file)
{
$len = strlen($this->config['thumbnail_prefix']);
if(substr($file,0,$len)==$this->config['thumbnail_prefix'])
Return true;
else
Return false;
}
/**
* Check if the given directory is a thumbnail directory.
* @param string $entry directory name
* @return true if it is a thumbnail directory, false otherwise
*/
function isThumbDir($entry)
{
if($this->config['thumbnail_dir'] == false
|| strlen(trim($this->config['thumbnail_dir'])) == 0)
Return false;
else
Return ($entry == $this->config['thumbnail_dir']);
}
/**
* Check if the given file is a tmp file.
* @param string $file file name
* @return boolean true if it is a tmp file, false otherwise
*/
function isTmpFile($file)
{
$len = strlen($this->config['tmp_prefix']);
if(substr($file,0,$len)==$this->config['tmp_prefix'])
Return true;
else
Return false;
}
/**
* For a given image file, get the respective thumbnail filename
* no file existence check is done.
* @param string $fullpathfile the full path to the image file
* @return string of the thumbnail file
*/
function getThumbName($fullpathfile)
{
$path_parts = pathinfo($fullpathfile);
$thumbnail = $this->config['thumbnail_prefix'].$path_parts['basename'];
if( strlen(trim($this->config['thumbnail_dir'])) == 0 || $this->config['safe_mode'] == true)
{
Return Files::makeFile($path_parts['dirname'],$thumbnail);
}
else
{
$path = Files::makePath($path_parts['dirname'],$this->config['thumbnail_dir']);
if(!is_dir($path))
Files::createFolder($path);
Return Files::makeFile($path,$thumbnail);
}
}
/**
* Similar to getThumbName, but returns the URL, base on the
* given base_url in config.inc.php
* @param string $relative the relative image file name,
* relative to the base_dir path
* @return string the url of the thumbnail
*/
function getThumbURL($relative)
{
_ddt( __FILE__, __LINE__, "getThumbURL(): relative is '$relative'" );
$path_parts = pathinfo($relative);
$thumbnail = $this->config['thumbnail_prefix'].$path_parts['basename'];
if($path_parts['dirname']=='\\') $path_parts['dirname']='/';
if($this->config['safe_mode'] == true
|| strlen(trim($this->config['thumbnail_dir'])) == 0)
{
Return Files::makeFile($this->getImagesURL(),rawurlencode($thumbnail));
}
else
{
if(strlen(trim($this->config['thumbnail_dir'])) > 0)
{
$path = Files::makePath($path_parts['dirname'],$this->config['thumbnail_dir']);
$url_path = Files::makePath($this->getImagesURL(), $path);
_ddt( __FILE__, __LINE__, "getThumbURL(): url_path is '$url_path'" );
Return Files::makeFile($url_path,rawurlencode($thumbnail));
}
else //should this ever happen?
{
//error_log('ImageManager: Error in creating thumbnail url');
}
}
}
/**
* For a given image file, get the respective resized filename
* no file existence check is done.
* @param string $fullpathfile the full path to the image file
* @param integer $width the intended width
* @param integer $height the intended height
* @param boolean $mkDir whether to attempt to make the resized_dir if it doesn't exist
* @return string of the resized filename
*/
function getResizedName($fullpathfile, $width, $height, $mkDir = TRUE)
{
$path_parts = pathinfo($fullpathfile);
$thumbnail = $this->config['resized_prefix']."_{$width}x{$height}_{$path_parts['basename']}";
if( strlen(trim($this->config['resized_dir'])) == 0 || $this->config['safe_mode'] == true )
{
Return Files::makeFile($path_parts['dirname'],$thumbnail);
}
else
{
$path = Files::makePath($path_parts['dirname'],$this->config['resized_dir']);
if($mkDir && !is_dir($path))
Files::createFolder($path);
Return Files::makeFile($path,$thumbnail);
}
}
/**
* Check if the given path is part of the subdirectories
* under the base_dir.
* @param string $path the relative path to be checked
* @return boolean true if the path exists, false otherwise
*/
function validRelativePath($path)
{
$dirs = $this->getDirs();
if($path == '/')
Return true;
//check the path given in the url against the
//list of paths in the system.
for($i = 0; $i < count($dirs); $i++)
{
$key = key($dirs);
//we found the path
if($key == $path)
Return true;
next($dirs);
}
Return false;
}
/**
* Process uploaded files, assumes the file is in
* $_FILES['upload'] and $_POST['dir'] is set.
* The dir must be relative to the base_dir and exists.
* If 'validate_images' is set to true, only file with
* image dimensions will be accepted.
* @return null
*/
function processUploads()
{
if($this->isValidBase() == false)
return;
$relative = null;
if(isset($_POST['dir']))
$relative = rawurldecode($_POST['dir']);
else
return;
//check for the file, and must have valid relative path
if(isset($_FILES['upload']) && $this->validRelativePath($relative))
{
$this->_processFiles($relative, $_FILES['upload']);
}
}
/**
* Process upload files. The file must be an
* uploaded file. If 'validate_images' is set to
* true, only images will be processed. Any duplicate
* file will be renamed. See Files::copyFile for details
* on renaming.
* @param string $relative the relative path where the file
* should be copied to.
* @param array $file the uploaded file from $_FILES
* @return boolean true if the file was processed successfully,
* false otherwise
*/
function _processFiles($relative, $file)
{
if($file['error']!=0)
{
Return false;
}
if(!is_file($file['tmp_name']))
{
Return false;
}
if(!is_uploaded_file($file['tmp_name']))
{
Files::delFile($file['tmp_name']);
Return false;
}
if($this->config['validate_images'] == true)
{
$imgInfo = @getImageSize($file['tmp_name']);
if(!is_array($imgInfo))
{
Files::delFile($file['tmp_name']);
Return false;
}
}
$valid_extensions = $this->config['allowed_image_extensions'];
$afruext = strtolower(substr(strrchr($file['name'], "."), 1));
if(!in_array($afruext, $valid_extensions))
{
Files::delFile($file['tmp_name']);
Return 'Cannot upload $extension='.$afruext.'$ Files. Permission denied.';
}
//now copy the file
$path = Files::makePath($this->getImagesDir(),$relative);
$result = Files::copyFile($file['tmp_name'], $path, $file['name']);
//no copy error
if(!is_int($result))
{
Files::delFile($file['tmp_name']);
Return true;
}
//delete tmp files.
Files::delFile($file['tmp_name']);
Return false;
}
/**
* Get the URL of the relative file.
* basically appends the relative file to the
* base_url given in config.inc.php
* @param string $relative a file the relative to the base_dir
* @return string the URL of the relative file.
*/
function getFileURL($relative)
{
Return Files::makeFile($this->getImagesURL(),$relative);
}
/**
* Get the fullpath to a relative file.
* @param string $relative the relative file.
* @return string the full path, .ie. the base_dir + relative.
*/
function getFullPath($relative)
{
Return Files::makeFile($this->getImagesDir(),$relative);;
}
/**
* Get the default thumbnail.
* @return string default thumbnail, empty string if
* the thumbnail doesn't exist.
*/
function getDefaultThumb()
{
// FIXME: hack
Return $this->config['default_thumbnail'];
if(is_file($this->config['default_thumbnail']))
{
Return $this->config['default_thumbnail'];
}
else
Return '';
}
/**
* Get the thumbnail url to be displayed.
* If the thumbnail exists, and it is up-to-date
* the thumbnail url will be returns. If the
* file is not an image, a default image will be returned.
* If it is an image file, and no thumbnail exists or
* the thumbnail is out-of-date (i.e. the thumbnail
* modified time is less than the original file)
* then a thumbs.php?img=filename.jpg is returned.
* The thumbs.php url will generate a new thumbnail
* on the fly. If the image is less than the dimensions
* of the thumbnails, the image will be display instead.
* @param string $relative the relative image file.
* @return string the url of the thumbnail, be it
* actually thumbnail or a script to generate the
* thumbnail on the fly.
*/
function getThumbnail($relative)
{
global $IMConfig;
_ddt( __FILE__, __LINE__, "getThumbnail(): top with '$relative'" );
$fullpath = Files::makeFile($this->getImagesDir(),$relative);
//not a file???
if(!is_file($fullpath))
Return $this->getDefaultThumb();
$imgInfo = @getImageSize($fullpath);
//not an image
if(!is_array($imgInfo))
Return $this->getDefaultThumb();
//the original image is smaller than thumbnails,
//so just return the url to the original image.
if ($imgInfo[0] <= $this->config['thumbnail_width']
&& $imgInfo[1] <= $this->config['thumbnail_height'])
Return $this->getFileURL($relative);
$thumbnail = $this->getThumbName($fullpath);
//check for thumbnails, if exists and
// it is up-to-date, return the thumbnail url
if(is_file($thumbnail))
{
if(filemtime($thumbnail) >= filemtime($fullpath))
{
_ddt( __FILE__, __LINE__, "getThumbnail(): returning url '" . $this->getThumbURL($relative) . "'" );
Return $this->getThumbURL($relative);
}
}
//well, no thumbnail was found, so ask the thumbs.php
//to generate the thumbnail on the fly.
Return $IMConfig['backend_url'] . '__function=thumbs&img='.rawurlencode($relative);
}
/**
* Delete and specified files.
* @return boolean true if delete, false otherwise
*/
function deleteFiles()
{
if(isset($_GET['delf']))
$this->_delFile(rawurldecode($_GET['delf']));
}
/**
* Delete and specified directories.
* @return boolean true if delete, false otherwise
*/
function deleteDirs()
{
if(isset($_GET['deld']))
return $this->_delDir(rawurldecode($_GET['deld']));
else
Return false;
}
/**
* Delete the relative file, and any thumbnails.
* @param string $relative the relative file.
* @return boolean true if deleted, false otherwise.
*/
function _delFile($relative)
{
$fullpath = Files::makeFile($this->getImagesDir(),$relative);
//check that the file is an image
if($this->config['validate_images'] == true)
{
if(!is_array($this->getImageInfo($fullpath)))
return false; //hmmm not an Image!!???
}
$thumbnail = $this->getThumbName($fullpath);
if(Files::delFile($fullpath))
Return Files::delFile($thumbnail);
else
Return false;
}
/**
* Delete directories recursively.
* @param string $relative the relative path to be deleted.
* @return boolean true if deleted, false otherwise.
*/
function _delDir($relative)
{
$fullpath = Files::makePath($this->getImagesDir(),$relative);
if($this->countFiles($fullpath) <= 0)
return Files::delFolder($fullpath,true); //delete recursively.
else
Return false;
}
/**
* Create new directories.
* If in safe_mode, nothing happens.
* @return boolean true if created, false otherwise.
*/
function processNewDir()
{
if($this->config['safe_mode'] == true)
Return false;
if(isset($_GET['newDir']) && isset($_GET['dir']))
{
$newDir = rawurldecode($_GET['newDir']);
$dir = rawurldecode($_GET['dir']);
$path = Files::makePath($this->getImagesDir(),$dir);
$fullpath = Files::makePath($path, Files::escape($newDir));
if(is_dir($fullpath))
Return false;
Return Files::createFolder($fullpath);
}
}
}
?>