* Copyright 2001-2010 Strangecode, LLC * * This file is part of The Strangecode Codebase. * * The Strangecode Codebase is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * The Strangecode Codebase is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * The Strangecode Codebase. If not, see . */ /** * ImageThumb.inc.php * * @author Quinn Comendant * @requires Netpbm and libjpeg or GD. * @version 2.0 */ // Image proprtion options. define('IMAGETHUMB_FIT_WIDTH', 1); define('IMAGETHUMB_FIT_HEIGHT', 2); define('IMAGETHUMB_FIT_LARGER', 3); define('IMAGETHUMB_STRETCH', 4); define('IMAGETHUMB_NO_SCALE', 5); // Image resize options. define('IMAGETHUMB_METHOD_NETPBM', 6); define('IMAGETHUMB_METHOD_GD', 7); class ImageThumb { // General object parameters. var $_params = array( // The location for images to create thumbnails from. 'source_dir' => null, // Existing files will be overwritten when there is a name conflict? 'allow_overwriting' => false, // The file permissions of the uploaded files. Remember, files will be owned by the web server user. 'dest_file_perms' => 0600, // Permissions of auto-created directories. Must be at least 0700 with owner=apache. 'dest_dir_perms' => 0700, // Require file to have one of the following file name extensions. 'valid_file_extensions' => array('jpg', 'jpeg', 'gif', 'png'), // Method to use for resizing. (IMAGETHUMB_METHOD_NETPBM or IMAGETHUMB_METHOD_GD) 'resize_method' => IMAGETHUMB_METHOD_NETPBM, // Netpbm and libjpeg binary locations. 'anytopnm_binary' => '/usr/bin/anytopnm', 'pnmscale_binary' => '/usr/bin/pnmscale', 'cjpeg_binary' => '/usr/bin/cjpeg', // Which messages do we pass to raiseMsg? Use one of the MSG_* constants or false to disable. 'display_messages' => MSG_ALL, ); // Default image size specs. var $_default_image_specs = array( // The destination for an image thumbnail size. // Use initial / to specify absolute paths, leave off to specify a path relative to source_dir (eg: ../thumbs). 'dest_dir' => null, // Destination file types. (IMG_JPG, IMG_PNG, IMG_GIF, IMG_WBMP) 'dest_file_type' => IMG_JPG, // Destination file types. ('jpg', 'png', 'gif', 'wbmp') 'dest_file_extension' => 'jpg', // Type of scaling to perform, and sizes used to calculate max dimensions. 'scaling_type' => IMAGETHUMB_FIT_LARGER, 'width' => null, 'height' => null, // Percentage quality of image compression output 0-100. 'quality' => 65, // Create progressive jpegs? 'progressive' => false, // If using GD method, apply sharpen filter. Requires PHP > 5.1. 'sharpen' => true, // Integers between 1-100, useful values are 65-85. 'sharpen_value' => 75, // If source image is smaller than thumbnail, allow upscaling? 'allow_upscaling' => false, // If thumb exists and filesize is smaller than this, do not overwrite the thumb. 'keep_filesize' => null, ); // Final specifications for image sizes, set with setSpec(). var $_image_specs = array(); /** * Set (or overwrite existing) parameters by passing an array of new parameters. * * @access public * @param array $params Array of parameters (key => val pairs). */ function setParam($params) { $app =& App::getInstance(); if (isset($params) && is_array($params)) { // Enforce valid source_dir parameter. if (isset($params['source_dir'])) { $params['source_dir'] = realpath($params['source_dir']); // Source must be directory. if (!is_dir($params['source_dir'])) { $app->logMsg(sprintf('Attempting to auto-create source directory: %s', $params['source_dir']), LOG_NOTICE, __FILE__, __LINE__); if (phpversion() > '5') { // Recursive. mkdir($params['source_dir'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms'), true); } else { mkdir($params['source_dir'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms')); } if (!is_dir($params['source_dir'])) { $app->logMsg(sprintf('Source directory invalid: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__); trigger_error(sprintf('Source directory invalid: %s', $params['source_dir']), E_USER_ERROR); } } // Source must be readable. if (!is_readable($params['source_dir'])) { $app->logMsg(sprintf('Source directory not readable: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__); trigger_error(sprintf('Source directory not readable: %s', $params['source_dir']), E_USER_ERROR); } } // Merge new parameters with old overriding only those passed. $this->_params = array_merge($this->_params, $params); } else { $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__); } } /** * Return the value of a parameter, if it exists. * * @access public * @param string $param Which parameter to return. * @return mixed Configured parameter value. */ function getParam($param) { $app =& App::getInstance(); if (isset($this->_params[$param])) { return $this->_params[$param]; } else { $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__); return null; } } /** * Set the specification of thumbnails. * * @access public * @param array $spec The specifications for a size of output image. * @param int $index The position of the specification in the spec array * Use to overwrite existing spec array values. */ function setSpec($spec, $index=null) { $app =& App::getInstance(); // A little sanity checking. if (!isset($spec['dest_dir']) || '' == trim($spec['dest_dir'])) { $app->logMsg('setSpec error: dest_dir not specified.', LOG_ERR, __FILE__, __LINE__); } else { $spec['dest_dir'] = trim($spec['dest_dir']); } if (isset($spec['dest_file_type'])) { switch ($spec['dest_file_type']) { case IMG_JPG : if (imagetypes() & IMG_JPG == 0) { $app->logMsg(sprintf('IMG_JPG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__); } $spec['dest_file_extension'] = 'jpg'; break; case IMG_PNG : if (imagetypes() & IMG_PNG == 0) { $app->logMsg(sprintf('IMG_PNG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__); } $spec['dest_file_extension'] = 'png'; break; case IMG_GIF : if (imagetypes() & IMG_GIF == 0) { $app->logMsg(sprintf('IMG_GIF is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__); } $spec['dest_file_extension'] = 'gif'; break; case IMG_WBMP : if (imagetypes() & IMG_WBMP == 0) { $app->logMsg(sprintf('IMG_WBMP is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__); } $spec['dest_file_extension'] = 'wbmp'; break; default : $app->logMsg(sprintf('Invalid dest_file_type: %s', $spec['dest_file_type']), LOG_ERR, __FILE__, __LINE__); break; } } if (!isset($spec['width']) || !is_int($spec['width'])) { $app->logMsg('setSpec error: width not specified.', LOG_ERR, __FILE__, __LINE__); } if (!isset($spec['height']) || !is_int($spec['height'])) { $app->logMsg('setSpec error: height not specified.', LOG_ERR, __FILE__, __LINE__); } if (isset($spec['quality']) && IMG_JPG != $spec['dest_file_type']) { $app->logMsg('The "quality" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__); } if (isset($spec['progressive']) && IMG_JPG != $spec['dest_file_type']) { $app->logMsg('The "progressive" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__); } // Add to _image_specs array. if (isset($index) && isset($this->_image_specs[$index])) { // Merge with existing spec if index is provided. $final_spec = array_merge($this->_image_specs[$index], $spec); $this->_image_specs[$index] = $final_spec; } else { // Merge with spec defaults. $final_spec = array_merge($this->_default_image_specs, $spec); $this->_image_specs[] = $final_spec; } return $final_spec; } /* * Retrieve a value of a thumb specification. * * @access public * @param string $key Key to return. See _default_image_specs above for a list. * @param int $index The index in the spec array of the value to retrieve. The first if not specified. * @return mixed Value of requested index. * @author Quinn Comendant * @version 1.0 * @since 08 May 2007 15:26:39 */ function getSpec($key, $index=null) { $index = isset($index) ? $index : 0; return $this->_image_specs[$index][$key]; } /** * Process an entire directory of images. * * @access public * @return bool true on success, false on failure. */ function processAll($runtime_specs=null) { $app =& App::getInstance(); // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } // Get all files in source directory. $dir_handle = opendir($this->getParam('source_dir')); while ($dir_handle && ($file = readdir($dir_handle)) !== false) { // If the file name does not start with a dot (. or .. or .htaccess). if (!preg_match('/^\./', $file) && in_array(mb_strtolower(mb_substr($file, mb_strrpos($file, '.') + 1)), $this->getParam('valid_file_extensions'))) { $files[] = $file; } } // Process each found file. if (is_array($files) && !empty($files)) { $return_val = 0; foreach ($files as $file_name) { $return_val += (false === $this->processFile($file_name, $runtime_specs) ? 1 : 0); } $this->_raiseMsg(sprintf(_("Resized %s images."), sizeof($files)), MSG_SUCCESS, __FILE__, __LINE__); $this->_raiseMsg(sprintf(_("Resized %s images."), sizeof($files)), MSG_SUCCESS, __FILE__, __LINE__); return 0 === $return_val; } else { $app->logMsg(sprintf('No source images found in directory: %s', $this->getParam('source_dir')), LOG_NOTICE, __FILE__, __LINE__); return false; } } /** * Generate thumbnails for the specified file. * * @access public * @param string $file_name Name of file with extension. * @param array $runtime_specs Array of specifications that will override all configured specifications. * @return bool true on success, false on failure. */ function processFile($file_name, $runtime_specs=null) { $app =& App::getInstance(); // Source file determined by provided file_name. $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name)); // Ensure we have a source. if (sizeof($this->_image_specs) < 1) { if (is_array($runtime_specs)) { $this->setSpec($runtime_specs, 0); } else { $app->logMsg(sprintf('Image specifications not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } } // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } // Confirm source image exists. if (!file_exists($source_file)) { $this->_raiseMsg(sprintf(_("Image resizing failed: source image %s was not found."), $file_name), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Source image not found: %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return false; } // Confirm source image is readable. if (!is_readable($source_file)) { $this->_raiseMsg(sprintf(_("Image resizing failed: source image %s is not readable."), $file_name), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Source image not readable: %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return false; } // Confirm source image contains data. if (filesize($source_file) <= 0) { $this->_raiseMsg(sprintf(_("Image resizing failed: source image %s is zero bytes."), $file_name), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Source image is zero bytes: %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return false; } // Confirm source image has a valid file extension. if (!$this->_validFileExtension($file_name)) { $this->_raiseMsg(sprintf(_("Image resizing failed: source image %s not a valid type. It must have one of the following file name extensions: %s"), $file_name, join(', ', $this->getParam('valid_file_extensions'))), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Image resizing failed: source image not of valid type: %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return false; } // Ensure destination directories are created. This will only be called once per page load. if (!$this->_createDestDirs()) { $app->logMsg('Image resizing failed: unable to create dest dirs.', LOG_WARNING, __FILE__, __LINE__); return false; } // To keep this script running even if user tries to stop browser. ignore_user_abort(true); ini_set('max_execution_time', 300); ini_set('max_input_time', 300); // This remains zero until something goes wrong. $return_val = 0; foreach ($this->_image_specs as $index => $spec) { if (is_array($runtime_specs)) { // Override with runtime specs. $spec = $this->setSpec($runtime_specs, $index); } // Destination filename uses the extension defined by dest_file_extension. if ('/' == $spec['dest_dir']{0}) { // Absolute path. $dest_file = sprintf('%s/%s.%s', $spec['dest_dir'], mb_substr($file_name, 0, mb_strrpos($file_name, '.')), $spec['dest_file_extension']); } else { // Relative path. $dest_file = sprintf('%s/%s/%s.%s', $this->getParam('source_dir'), $spec['dest_dir'], mb_substr($file_name, 0, mb_strrpos($file_name, '.')), $spec['dest_file_extension']); } // Ensure destination directory exists and is writable. if (!is_dir(dirname($dest_file)) || !is_writable(dirname($dest_file))) { $this->_createDestDirs($dest_file); if (!is_dir(dirname($dest_file)) || !is_writable(dirname($dest_file))) { $app->logMsg(sprintf('Image resizing failed, dest_dir invalid: %s', dirname($dest_file)), LOG_ERR, __FILE__, __LINE__); $return_val++; continue; } } // Skip existing thumbnails with file size below $spec['keep_filesize']. if (isset($spec['keep_filesize']) && file_exists($dest_file)) { $file_size = filesize($dest_file); if (false !== $file_size && $file_size < $spec['keep_filesize']) { $app->logMsg(sprintf('Skipping thumbnail %s. File already exists and file size is less than %s bytes.', $spec['dest_dir'] . '/' . $file_name, $spec['keep_filesize']), LOG_INFO, __FILE__, __LINE__); continue; } } // Determine if original file size is smaller than specified thumbnail size. Do not scale-up if $spec['allow_upscaling'] config is set to false. $image_size = getimagesize($source_file); if ($image_size['0'] <= $spec['width'] && $image_size['1'] <= $spec['height'] && !$spec['allow_upscaling']) { $spec['scaling_type'] = IMAGETHUMB_NO_SCALE; $app->logMsg(sprintf('Image %s smaller than specified %s thumbnail size. Keeping original size.', $file_name, basename($spec['dest_dir'])), LOG_INFO, __FILE__, __LINE__); } // DO IT! Based on available method. if (IMAGETHUMB_METHOD_NETPBM === $this->getParam('resize_method') && file_exists($this->getParam('anytopnm_binary')) && file_exists($this->getParam('pnmscale_binary')) && file_exists($this->getParam('cjpeg_binary'))) { // Resize using Netpbm binaries. $app->logMsg(sprintf('Resizing with Netpbm: %s', $source_file), LOG_DEBUG, __FILE__, __LINE__); $return_val += $this->_resizeWithNetpbm($source_file, $dest_file, $spec); } else if (IMAGETHUMB_METHOD_GD === $this->getParam('resize_method') && extension_loaded('gd')) { // Resize with GD. $app->logMsg(sprintf('Resizing with GD: %s', $source_file), LOG_DEBUG, __FILE__, __LINE__); $return_val += $this->_resizeWithGD($source_file, $dest_file, $spec); } else { $app->logMsg(sprintf('Image thumbnailing canceled. Neither Netpbm or GD is available.', null), LOG_ERR, __FILE__, __LINE__); return false; } } // If > 0, there was a problem thumb-nailing. return 0 === $return_val; } /* * Use the Netpbm and libjpg cjpeg tools to generate a rescaled compressed image. * This is the preferred method over GD which has (supposedly) less quality. * * @access private * @param string $source_file Full path to source image file. * @param string $dest_file Full path to destination image file. * @param array $spec Array of image size specifications. * @return int 0 if no error, n > 0 if errors. * @author Quinn Comendant * @version 1.0 * @since 19 May 2006 13:55:46 */ function _resizeWithNetpbm($source_file, $dest_file, $spec) { $app =& App::getInstance(); // Define pnmscale arguments. switch ($spec['scaling_type']) { case IMAGETHUMB_FIT_WIDTH : $pnmscale_args = sprintf(' -width %s ', escapeshellarg($spec['width'])); break; case IMAGETHUMB_FIT_HEIGHT : $pnmscale_args = sprintf(' -height %s ', escapeshellarg($spec['height'])); break; case IMAGETHUMB_FIT_LARGER : $pnmscale_args = sprintf(' -xysize %s %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height'])); break; case IMAGETHUMB_STRETCH : $pnmscale_args = sprintf(' -width %s -height %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height'])); break; case IMAGETHUMB_NO_SCALE : default : $pnmscale_args = ' 1 '; break; } // Define cjpeg arguments. $cjpeg_args = sprintf(' -optimize -quality %s ', escapeshellarg($spec['quality'])); $cjpeg_args .= (true === $spec['progressive']) ? ' -progressive ' : ''; // Format the command that creates the thumbnail. $command = sprintf('%s %s | %s %s | %s %s > %s', escapeshellcmd($this->getParam('anytopnm_binary')), escapeshellcmd($source_file), escapeshellcmd($this->getParam('pnmscale_binary')), escapeshellcmd($pnmscale_args), escapeshellcmd($this->getParam('cjpeg_binary')), escapeshellcmd($cjpeg_args), escapeshellcmd($dest_file) ); $app->logMsg(sprintf('ImageThumb Netpbm command: %s', $command), LOG_DEBUG, __FILE__, __LINE__); // Execute! exec($command, $output, $return_val); if (0 === $return_val) { // Success! // Make the thumbnail writable so the user can delete it over ftp without being 'apache'. chmod($dest_file, $this->getParam('dest_file_perms')); $app->logMsg(sprintf('Successfully resized image %s', $spec['dest_dir'] . '/' . basename($dest_file), $return_val), LOG_DEBUG, __FILE__, __LINE__); } else { // An error occurred. $app->logMsg(sprintf('Image %s failed resizing with return value: %s%s', $spec['dest_dir'] . '/' . basename($dest_file), $return_val, empty($output) ? '' : ' (' . truncate(getDump($output, true), 128, 'end') . ')'), LOG_ERR, __FILE__, __LINE__); } // Return from the command will be > 0 if there was an error. return $return_val; } /* * Use PHP's built-in GD tools to generate a rescaled compressed image. * * @access private * @param string $source_file Full path to source image file. * @param string $dest_file Full path to destination image file. * @param array $spec Array of image size specifications. * @return int 0 if no error, n > 0 if errors. * @author Quinn Comendant * @version 1.0 * @since 19 May 2006 15:46:02 */ function _resizeWithGD($source_file, $dest_file, $spec) { $app =& App::getInstance(); // Get original file dimensions and type. list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_file); // Define destination image dimensions. switch ($spec['scaling_type']) { case IMAGETHUMB_FIT_WIDTH : $dest_image_width = $spec['width']; $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width); break; case IMAGETHUMB_FIT_HEIGHT : $dest_image_height = $spec['height']; $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height); break; case IMAGETHUMB_FIT_LARGER : if (($source_image_width * ($spec['height'] / $source_image_height)) <= $spec['width']) { // Height is larger. $dest_image_height = $spec['height']; $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height); } else { // Width is larger. $dest_image_width = $spec['width']; $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width); } break; case IMAGETHUMB_STRETCH : $dest_image_width = $spec['width']; $dest_image_height = $spec['height']; break; case IMAGETHUMB_NO_SCALE : default : $dest_image_width = $source_image_width; $dest_image_height = $source_image_height; break; } // Create source image data in memory. switch ($source_image_type) { case IMAGETYPE_JPEG : // = 2 $source_image_resource = imagecreatefromjpeg($source_file); break; case IMAGETYPE_PNG : // = 3 $source_image_resource = imagecreatefrompng($source_file); break; case IMAGETYPE_GIF : // = 1 $source_image_resource = imagecreatefromgif($source_file); break; case IMAGETYPE_WBMP : // = 15 $source_image_resource = imagecreatefromwbmp($source_file); case IMAGETYPE_SWF : // = 4 case IMAGETYPE_PSD : // = 5 case IMAGETYPE_BMP : // = 6 case IMAGETYPE_TIFF_II : // = 7 case IMAGETYPE_TIFF_MM : // = 8 case IMAGETYPE_JPC : // = 9 case IMAGETYPE_JP2 : // = 10 case IMAGETYPE_JPX : // = 11 case IMAGETYPE_JB2 : // = 12 case IMAGETYPE_SWC : // = 13 case IMAGETYPE_IFF : // = 14 case IMAGETYPE_XBM : // = 16 default : $app->logMsg(sprintf('Source image type %s not supported.', $source_image_type), LOG_WARNING, __FILE__, __LINE__); return 1; } if (!$source_image_resource) { $app->logMsg(sprintf('Error creating %s image in memory from %s', $source_image_type, $source_file), LOG_WARNING, __FILE__, __LINE__); return 1; } // Create destination image data in memory. $dest_image_resource = imagecreatetruecolor($dest_image_width, $dest_image_height); // Resample! if (!imagecopyresampled($dest_image_resource, $source_image_resource, 0, 0, 0, 0, $dest_image_width, $dest_image_height, $source_image_width, $source_image_height)) { // Always cleanup images from memory. imagedestroy($source_image_resource); imagedestroy($dest_image_resource); $app->logMsg(sprintf('Error resampling image %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return 1; } // Sharpen image using a custom filter matrix. if (phpversion() > '5.1' && true === $spec['sharpen'] && $spec['sharpen_value'] > 0) { $sharpen_value = round((((48 - 10) / (100 - 1)) * (100 - $spec['sharpen_value'])) + 10); /// WTF is this math? imageconvolution($dest_image_resource, array(array(-1,-1,-1),array(-1,$sharpen_value,-1),array(-1,-1,-1)), ($sharpen_value - 8), 0); } // Save image. $return_val = true; switch ($spec['dest_file_type']) { case IMG_JPG : imageinterlace($dest_image_resource, (true == $spec['progressive'] ? 1 : 0)); $return_val = imagejpeg($dest_image_resource, $dest_file, $spec['quality']); break; case IMG_PNG : $return_val = imagepng($dest_image_resource, $dest_file); break; case IMG_GIF : $return_val = imagegif($dest_image_resource, $dest_file); break; case IMG_WBMP : $return_val = imagewbmp($dest_image_resource, $dest_file); break; default : $app->logMsg(sprintf('Destination image type %s not supported for image %s.', $spec['dest_file_type'], $dest_file), LOG_WARNING, __FILE__, __LINE__); // Always cleanup images from memory. imagedestroy($source_image_resource); imagedestroy($dest_image_resource); return 1; break; } // Always cleanup images from memory. imagedestroy($source_image_resource); imagedestroy($dest_image_resource); if ($return_val) { // Success! // Make the thumbnail writable so the user can delete it over ftp without being 'apache'. chmod($dest_file, $this->getParam('dest_file_perms')); $app->logMsg(sprintf('Successfully resized image: %s', $dest_file), LOG_DEBUG, __FILE__, __LINE__); return 0; } else { // An error occurred. $app->logMsg(sprintf('Failed resizing image: %s', $dest_file), LOG_ERR, __FILE__, __LINE__); return 1; } } /** * Delete the thumbnails for the specified file name. * * @access public * @param string $file_name The file name to delete, with extension. * @param bool $use_glob Set true to use glob to find the filename (using $file_name as a pattern) * @return bool true on success, false on failure. */ function deleteThumbs($file_name, $use_glob=false) { $app =& App::getInstance(); // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } $return_val = 0; foreach ($this->_image_specs as $spec) { // Get base directory depending on if the spec's dest_dir is an absolute path or not. $dest_dir = '/' == $spec['dest_dir']{0} ? $spec['dest_dir'] : sprintf('%s/%s', $this->getParam('source_dir'), $spec['dest_dir']); if ($use_glob) { $dest_file = realpath(sprintf('%s/%s', $dest_dir, $this->_glob($file_name, $dest_dir))); } else { $dest_file = realpath(sprintf('%s/%s.%s', $dest_dir, mb_substr($file_name, 0, mb_strrpos($file_name, '.')), $spec['dest_file_extension'])); } if (file_exists($dest_file)) { if (!unlink($dest_file)) { $return_val++; $app->logMsg(sprintf('Delete thumbs failed: %s', $dest_file), LOG_WARNING, __FILE__, __LINE__); } } } $app->logMsg(sprintf('Thumbnails deleted for file: %s', $dest_file), LOG_DEBUG, __FILE__, __LINE__); return 0 === $return_val; } /** * Delete the source image with the specified file name. * * @access public * @param string $file_name The file name to delete, with extension. * @return bool true on success, false on failure. */ function deleteOriginal($file_name) { $app =& App::getInstance(); // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } // Ensure we have a source. if ('' == $file_name) { $app->logMsg(sprintf('Cannot delete, filename empty.'), LOG_WARNING, __FILE__, __LINE__); return false; } $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name)); if (is_file($source_file) && unlink($source_file)) { $app->logMsg(sprintf('Original file successfully deleted: %s', $file_name), LOG_INFO, __FILE__, __LINE__); return true; } else { $app->logMsg(sprintf('Delete original failed: %s', $source_file), LOG_WARNING, __FILE__, __LINE__); return false; } } /** * Returns true if file exists. * * @access public * @param string $file_name The file name to test, with extension. * @return bool true on success, false on failure. */ function exists($file_name) { $app =& App::getInstance(); // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); return false; } $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name)); return file_exists($source_file); } /** * Tests if extension of $file_name is in the array valid_file_extensions. * * @access public * @param string $file_name A file name. * @return bool True on success, false on failure. */ function _validFileExtension($file_name) { preg_match('/.*?\.(\w+)$/i', $file_name, $ext); return !empty($ext) && in_array(mb_strtolower($ext[1]), $this->getParam('valid_file_extensions')); } /** * Make directory for each specified thumbnail size, if it doesn't exist. * * @access public * @return bool true on success, false on failure. */ function _createDestDirs($filename=null) { $app =& App::getInstance(); // Keep track of directories we've already created. $dd_hash = md5(isset($filename) ? dirname($filename) : 'none'); static $already_checked = array(); $return_val = 0; if (!isset($already_checked[$dd_hash])) { // Ensure we have a source. if ('' == $this->getParam('source_dir')) { $app->logMsg(sprintf('Source directory not set before creating destination directories.'), LOG_ERR, __FILE__, __LINE__); return false; } // Loop through specs and ensure all dirs are created. foreach ($this->_image_specs as $spec) { if (isset($filename)) { $dest_dir = dirname($filename); } else { // Get base directory depending on if the spec's dest_dir is an absolute path or not. $dest_dir = '/' == $spec['dest_dir']{0} ? $spec['dest_dir'] : sprintf('%s/%s', $this->getParam('source_dir'), $spec['dest_dir']); } if (!file_exists($dest_dir)) { $app->logMsg(sprintf('Creating dest dir: %s', $dest_dir), LOG_DEBUG, __FILE__, __LINE__); if (phpversion() > '5') { // Recursive. if (!file_exists($dest_dir) && !($ret = mkdir($dest_dir, $this->getParam('dest_dir_perms'), true))) { $return_val++; $app->logMsg(sprintf('mkdir failure: %s', $dest_dir), LOG_ERR, __FILE__, __LINE__); } } else { // Recursive mkdir for php 4. $path = ''; $depth = 0; $ret = true; foreach (array_diff(explode('/', $dest_dir), array('')) as $dir) { $path .= '/' . $dir; $depth++; /// FIXME: This is a dirty way to make this work when open_basedir prevents looking at the first 3 directories in an absolute path. if ($depth > 3 && !($ret = file_exists($path) ? true : mkdir($path, $this->getParam('dest_dir_perms')))) { $return_val++; $app->logMsg(sprintf('mkdir failure: %s', $path), LOG_ERR, __FILE__, __LINE__); break; } else { $app->logMsg(sprintf('mkdir attempt: %s', $path), LOG_DEBUG, __FILE__, __LINE__); } } } if ($ret) { $app->logMsg(sprintf('mkdir success: %s', $dest_dir), LOG_DEBUG, __FILE__, __LINE__); } } else { $app->logMsg(sprintf('Dest dir exists: %s', $dest_dir), LOG_DEBUG, __FILE__, __LINE__); } } } $already_checked[$dd_hash] = true; // If > 0, there was a problem creating dest dirs. return 0 === $return_val; } /** * An alias for $app->raiseMsg that only sends messages configured by display_messages. * * @access public * * @param string $message The text description of the message. * @param int $type The type of message: MSG_NOTICE, * MSG_SUCCESS, MSG_WARNING, or MSG_ERR. * @param string $file __FILE__. * @param string $line __LINE__. */ function _raiseMsg($message, $type, $file, $line) { $app =& App::getInstance(); if ($this->getParam('display_messages') === true || (is_int($this->getParam('display_messages')) && $this->getParam('display_messages') & $type > 0)) { $app->raiseMsg($message, $type, $file, $line); } } /** * Get filename by glob pattern. Searches a directory for an image that matches the * specified glob pattern and returns the filename of the first file found. * * @access public * @param string $pattern Pattern to match filename. * @param string $directory Pattern to match containing directory. * @return string filename on success, empty string on failure. * @author Quinn Comendant * @since 15 Nov 2005 20:55:22 */ function _glob($pattern, $directory) { $file_list = glob(sprintf('%s/%s', $directory, $pattern)); if (isset($file_list[0])) { return basename($file_list[0]); } else { return ''; } } } // End of class. ?>