Monthly Archives: March 2012

Installing memcached on ubuntu

Memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.
Memcached on Ubuntu has two components. A standalone program running on your server and a client PHP extension.
To install memcached, run the following command:

sudo aptitude install memcached php5-memcache php-pear
.....
Creating config file /etc/php5/conf.d/memcache.ini with new version
sudo aptitude install php5-dev  libmemcached-dev
sudo pecl install Memcache
.....
Installing '/usr/lib/php5/20090626+lfs/memcache.so'

Please choose

Enable memcache session handler support? [yes] : yes

Find your php.ini file and execute following command

php -i | grep php.ini
nano /etc/php5/apache2/php.ini

Add the line to the bottom of the file: memcache.hash_strategy=”consistent”

memcached -d -m 1024 -l 127.0.0.1 -p 11211

If you get a message saying, “can’t run as root without -u switch” then try this:

memcached -d -u www-data -m 1024 -l 127.0.0.1 -p 11211

To active new configuration execute following command

/etc/init.d/apache2 restart
/etc/init.d/memcached restart

Verify memcached

pgrep -fl memcached
netstat -an | grep 11211

Check memcached status

memcached -u www-data -vv
ps -A | grep memcached

There are either procedural, or object oriented functions already available. Here is an example of a script which will store a simple variable and then retrieve it and display it.

/* procedural API */
if (class_exists('Memcache')) {
    $memcache = new Memcache;
	try {
	 /* connect to memcached server */
	$memcache_obj = memcache_connect('localhost', 11211);
	} catch (Exception $e) { print_r($e);}
	if ($memcache_obj) {
	/*set value of item with key 'var_key' using 0 as flag value,
         * compression is not used expire time is 30 seconds*/
	memcache_set($memcache_obj, 'var_key', 'some variable', 0, 30);
	echo memcache_get($memcache_obj, 'var_key');
	}
}

I’d recommend downloading memcache.php which is a php script showing you a lot of useful information about your memcached servers. Once downloaded, put it in a web facing directory on your server, and modify the $MEMCACHE_SERVERS array with your server addresses.

SIGN MEDIA COMPETITION APPLICATION PROJECT

url: competition.signmedia.ca.
Project duration: 260 hours
Sign Media Canada’s national sign competition acknowledges the very best achievements in Canadian sign making. This competition is open to the national sign industry.
Futures list:

  1. Uploading and validation images with and without javascript
  2. Sign Media Canada can accept digital images that are minimum 300 dpi in resolution, large enough for layout (minimum specs are 4×6 inches) and formatted as ‘AI’, ‘BMP’, ‘EPS’, ‘GIF’, ‘JPEG’, ‘PDF’, ‘PNG’, ‘PS’, ‘PSD’, ‘TIFF’, ‘EPT’ files. No low-resolution files will be accepted. All images are converted to png format for display purpose.
  3. Client component:
    • sign up form, login and password recovery (one-time password )
    • clients to create project with a variety of fields and upload up to 5 image files
    • Images should be viewed after they are uploaded like photo gallery. For each project the images appear as thumbnails and then can be enlarged when clicked on.
    • email notification about new sign media project
  4. Judge component:
    • form containing a score box for each project. Each characteristic is scored from 1 – 10, so that each project receives a total score out of 40 (with 40 being a perfect score)
    • the judges would be able to select sign media category and see all projects in that category
    • judge can score Project or decline
    • The application calculates the total score for each project and an average score for all projects
  5. Admin component:
    • can manipulate with client accounts, judge accounts and edit projects
    • can manually approve or disqualify projects and/or images if they don’t qualify for sing media competition
    • is able to download selected images and pick the folder that they will download to
    • run different types of reports to view:
    • projects per category
    • projects per category the judge has scored
    • projects the judge declined to score
    • the average score of each project shown by category (ranked highest to lowest)
    • the average score of each project showing all ranked highest to lowest
    • projects remain to be scored
    • declined projects.

Technology used: Ubuntu 10.04.3 LTS, shell scripts, Apache/2.2.14, Memcached 1.4, MySQL 5.1, PHP 5.3, cakePHP 2.1, phpthumb, GD library, ImageMagick, Jquery, jquery.jqzoom, swfupload 2.5.
Pattern used:MVC, RAD, ORD.

How to validate image uploads in cakePHP 2?

The Validation class in CakePHP contains many validation rules that can make model data validation much easier. I need to validate an image resolution (min 300 dpi),  upload size (max 40 Mb) and formats like .jpg, .tif, .eps, .bmp, .png, .pdf, .psd, .ai, .ps or .gif.
The list of common MIME types and their corresponding file extensions are available via sites like reference.sitepoint.com/html/mime-types-full or www.webmaster-toolkit.com/mime-types.shtml.

App::uses('AppModel', 'Model');

class Image extends AppModel {
//array of accepted Mime types
private $allowedMime = array(
    'application/postscript'//EPS, AI, PS, EPT
    ,'image/bmp','image/x-windows-bmp' 	//BMP
    ,'image/gif'                            //GIFF
    ,'image/jpeg','image/pjpeg' 		//GPEG
    ,'application/pdf'			//PDF
    ,'image/png','image/x-png'		//PNG
    ,'application/octet-stream' 		//PSD
    ,'image/tiff','image/x-tiff','image/tiff','image/x-tiff'//TIF

);
private $maxSize = 40960;
private $imgProperties;
private $img;
//format used to validate file extension
public $verify = array(
    'format' => array('AI','BMP','EPS','GIF','JPEG','PDF','PNG','PS','PSD','TIFF','EPT'),
    'min_width_inches' => 4,
    'min_height_inches' => 4,
    'min_dpi'=>300, //PixelsPerInch
    'min_dpc'=>118 //PixelsPerCentimeter
);
public $uploadIds=array();
private $path;
public $Errors=array();
public $responce=array();

//function _getImageMimeType return back the image properties before save file to ftp and information to database
function beforeSave() {
$this->data['Image']+=array('format'=>$this->imgProperties['format']
   ,'dimensions_width_inches' =>number_format($this->imgProperties['geometry']['width_inches'])
   ,'dimensions_height_inches' =>number_format($this->imgProperties['geometry']['height_inches'])
   ,'dimensions_width_pixels' =>$this->imgProperties['geometry']['width']
   ,'dimensions_height_pixels' =>$this->imgProperties['geometry']['height']
   ,'resolution_width_dpi'=>$this->imgProperties['resolution']['x']
   ,'resolution_height_dpi'=>$this->imgProperties['resolution']['y']
   ,'colourspace'=>$this->imgProperties['colorSpace']
   ,'size'=>$this->data['Image']['file']['size']
   ,'path'=>$this->path
);
unset($this->data['Image']['file']);

return true;
}

public $validate = array(
   'file' => array(
   'checkupload' =>array(
   'rule' => array('checkUpload', false),
   'message' => 'No file was uploaded',//'Invalid file',
   'on' => 'create'
   ),
   'checkuploadedit' =>array(
   'rule' => array('checkUpload', false),
   'message' => 'No file was uploaded',//'Invalid file',
   'on' => 'update'
   ),
   'checksize' => array(
   'rule' => array('checkSize',false),
   'message' => 'Invalid File size',
   'on' => 'create'
   ),
   'checksizeedit' => array(
   'rule' => array('checkSize',false),
   'message' => 'Invalid File size',
   'on' => 'update'
   ),
   'checktype' =>array(
   'rule' => array('checkType',false),
   'message' => 'Invalid File type. Accepted file types: .jpg, .tif, .gif, .png, .psd, .pdf, .eps, .ai',
   'on' => 'create'
   ),
   'checktypeedit' =>array(
   'rule' => array('checkType',false),
   'message' => 'Invalid File type. Accepted file types: .jpg, .tif, .gif, .png, .psd, .pdf, .eps, .ai',
   'on' => 'update'
   ),
   'checkdimensions' =>array(
   'rule' => array('checkDimensions',false),
   'message' => 'Uploaded file does not have a min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI',
   'on' => 'create'
   ),
   'checkdimensionsedit' =>array(
   'rule' => array('checkDimensions',false),
   'message' => 'Uploaded file does not have a min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI',
   'on' => 'update'
   ),
   'checkuploadedfile' =>array(
   'rule' => array('checkUploadedFile',false),
   'message' => 'No file was uploaded',
   'on' => 'create'
   ),
   'checkuploadedfileedit' =>array(
   'rule' => array('checkUploadedFile',false),
   'message' => 'No file was uploaded',
   'on' => 'update'
   ),
),
);

//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* Custom validation rule for uploaded files.
*
* @param Array $data CakePHP File info.
* @param Boolean $required
* @return Boolean
*/
function checkUpload($data, $required = false){$data = $data["file"];

// Remove first level of Array ($data['Image']['size'] becomes $data['size']) $data = array_shift($this->data["Image"]["file"]);
if(!$required && $data['error'] == 4){$this->Errors["checkUpload"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}
// Check for Basic PHP file errors.
if($required && $data['error'] !== 0){$this->Errors["checkUpload"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." Failed to upload file: ".Debugger::exportVar($data,$recursion=1));
return false;}
// No file uploaded.
if($required && $data['size'] == 0){$this->Errors["checkUpload"]['error']=$data['size'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}

// Catch I/O Errors.
if ($required && !is_readable($data['tmp_name'])){$this->Errors["checkUpload"]['tmp_name']=1;
CakeLog::write("imageLogs",__METHOD__." Failed to write file to disk: {$data['tmp_name']}");
return false;
}
// Finally, use PHP’s own file validation method.
if ($required && !is_uploaded_file($data['tmp_name'])){$this->Errors["checkUpload"]['tmp_name']=2;
CakeLog::write("imageLogs", __METHOD__." Missing a temporary folder: ".Debugger::exportVar($data,$recursion=1));
return false;}
return true;
}

function checkSize($data, $required = false){$data = $data["file"];
if(!$required && $data['error'] == 4){
$this->Errors["checkSize"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}
if($data['size'] == 0||$data['size']/1024 > $this->maxSize){
$this->Errors["checkSize"]['error']=$data['size']/1024;
CakeLog::write("imageLogs", __METHOD__." The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form: ".($data['size']/1024).' '.Debugger::exportVar($data,$recursion=1));
return false;}
return true;
}

/**
* Use LibGD to determine if an uploaded file is a valid Image by
* running it's mime-type against a list of $valid_mime_types
*
* @param Array $data CakePHP File info
* @return Boolean
*/
function checkType($data, $required = false,$allowedMime = null){$data = $data["file"];
//File upload stopped by extension
if(!$required && $data['error'] == 4){
$this->Errors["checkType"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}

// Check the MimeType against the array of valid ones specified above
if(!in_array((strtolower($data['type'])), $allowedMime=$this->allowedMime)){
$this->Errors["checkType"]['type']=$data['type'];
CakeLog::write("imageLogs", __METHOD__." Uploaded image has rejected Mime Type: ".$data['type'].' '.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;}

// Retrieve the MimeType of Image, if none is returned, it's invalid
if (!$mime_type = $this->_getImageMimeType($data)){
$this->Errors["checkType"]['type']=$mime_type;
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a mime-type ".Debugger::exportVar($data,$recursion=1));
return false;
}

// Check the MimeType against the array of valid ones specified above
//if(empty($allowedMime)){$allowedMime=$this->allowedMime;}
if(!in_array((strtoupper($mime_type)), $this->verify['format'])){
$this->Errors["checkType"]['type']=$mime_type;//$this->Errors["checkType"]['mime_type']=$mime_type;
CakeLog::write("imageLogs", __METHOD__." Uploaded image has rejected Mime Type: ".$data['type'].' '.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;}

return true;
}

/**
* Use LibGD to return an uploaded Image's MimeType as a String, FALSE
* on errors or if the file is not an image
*
* @param String $filename Absolute path to file on disc
* @return String Image MimeType of $filename, false on failure
*/
function _getImageMimeType($data){

// If this error is thrown LibGD is not installed on your server.
if (!function_exists('getimagesize')){debug(__METHOD__." LibGD PHP Extension was not found, please refer to http://www.php.net/manual/en/book.image.php"); exit();}
try {
$image = new Imagick($data["tmp_name"]);
$imgProperties=$image->identifyImage(); //Imagick::getImageResolution()
//CakeLog::write("imageLogs",__METHOD__.Debugger::exportVar($imgProperties,$recursion=1) );
$imgProperties['format']=strtok($imgProperties['format'], ' ');
$imgProperties['geometry']['width_inches'] = $imgProperties['geometry']['width']/$imgProperties['resolution']['x'];
$imgProperties['geometry']['height_inches']= $imgProperties['geometry']['height']/$imgProperties['resolution']['y'];

} catch (Exception $e) {//throw new LogoReadErrorException();
$this->Errors["checkType"]['type']=$imgProperties['format'];
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a mime-type ".Debugger::exportVar($e).' '.Debugger::exportVar($data,$recursion=1));
}

if (isset($imgProperties['format'])){
$this->imgProperties=$imgProperties;
return $imgProperties['format'];}
return false;
}
/*
* Accepted min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI
* */
function checkDimensions() {$flag=1;
if(isset($this->imgProperties["units"]))
switch($this->imgProperties["units"]){
case "PixelsPerInch":
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
&& ((int)($this->imgProperties['resolution']['x']) >= (int)($this->verify['min_dpi']))
)?1:0;
break;
case "PixelsPerCentimeter": ///CakeLog::write("imageLogs",__METHOD__.' PixelsPerCentimeter '.$flag);
$this->imgProperties['geometry']['width_inches']=$this->imgProperties['geometry']['width']/($this->imgProperties['resolution']['x']*2.54);
$this->imgProperties['geometry']['height_inches']=$this->imgProperties['geometry']['height']/($this->imgProperties['resolution']['y']*2.54);;
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
&& ((int)($this->imgProperties['resolution']['x']) >= (int)($this->verify['min_dpc']))
)?1:0;
break;
case "Undefined":
switch($this->imgProperties["type"]){
case 'ColorSeparation':
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
)?1:0;
break;
case 'PaletteMatte':
$flag=(( (int)$this->imgProperties['geometry']['width'] > 1200)
&& ( (int)$this->imgProperties['geometry']['height'] > 1800)
)?1:0;
break;
}

break;
}
if($flag==0){
$this->Errors["checkDimensions"]['source']=$this->imgProperties;
$this->Errors["checkDimensions"]['required']=$this->verify;
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a min dimensions: 4.0x6.0 inches @300x300DPI"
.Debugger::exportVar($this->imgProperties,$recursion=1).' '
.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;
}
CakeLog::write("imageLogsUploadedOK",__METHOD__.Debugger::exportVar($this->imgProperties,$recursion=1)
.Debugger::exportVar($this->data["Image"],$recursion=1));
return true;
}

function checkUploadedFile($data, $required = false){
   $data = $data["file"];
   $this->path=$directory = WWW_ROOT.'img'.DS.'content'.DS.$this->data["Image"]["entries_id"];
   if(!is_dir($directory)) mkdir($directory);

   // Catch I/O Errors.
   if ($required && !is_readable($directory)){
      $this->Errors["checkUploadedFile"]['directory']="Failed to write file to disk: {$directory}";
      CakeLog::write("imageLogs",__METHOD__." Failed to write file to disk: {$directory}".' '.Debugger::exportVar($data,$recursion=1));
      return false;
}

   $this->data['Image']['name']=$this->data['Image']['priority']."_".$data['name'];
   if(!move_uploaded_file($data['tmp_name'], $directory.DS.$this->data['Image']['name'])){
      $this->Errors["checkUploadedFile"]['directory']=" Failed to move uploaded file: from {$data['tmp_name']}";
      CakeLog::write("imageLogs",__METHOD__." Failed to move uploaded file: from {$data['tmp_name']} to    ".$directory.DS.$data['name'].' '.Debugger::exportVar($data,$recursion=1));
      return false;
   }
return true;
}

}