Monday, August 10, 2009

Image Upload and Resize Component for CakePHP 1.2

############## image.php #####################
create a components image in following given path
/app/controllers/components/image.php

/*
File: /app/controllers/components/image.php
*/
class ImageComponent extends Object
{
/*
* Uploads an image and its thumbnail into $folderName/big and $folderName/small respectivley.
* the generated thumnail could either have the same aspect ratio as the uploaded image, or could
* be a zoomed and cropped version.

* Directions:
* In view where you upload the image, make sure your form creation is similar to the following
* create('FurnitureSet',array('type' => 'file')); ?>
*
* In view where you upload the image, make sure that you have a file input similar to the following
* file('Image/name1'); ?>
*
* In the controller, add the component to your components array
* var $components = array("Image");
*
* In your controller action (the parameters are expained below)
* $image_path = $this->Image->upload_image_and_thumbnail($this->data,"name1",573,80,"sets",true);
* this returns the file name of the result image. You can store this file name in the database
*
* Note that your image will be stored in 2 locations:
* Image: /webroot/img/$folderName/big/$image_path
* Thumbnail: /webroot/img/$folderName/small/$image_path
*
* Finally in the view where you want to see the images
* image('sets/big/'.$furnitureSet['FurnitureSet']['image_path']);
* where "sets" is the folder name we saved our pictures in, and $furnitureSet['FurnitureSet']['image_path'] is the file name we stored in the database

* Parameters:
* $data: CakePHP data array from the form
* $datakey: key in the $data array. If you used file('Image/name1'); ?> in your view, then $datakey = name1
* $imgscale: the maximum width or height that you want your picture to be resized to
* $thumbscale: the maximum width or height that you want your thumbnail to be resized to
* $folderName: the name of the parent folder of the images. The images will be stored to /webroot/img/$folderName/big/ and /webroot/img/$folderName/small/
* $square: a boolean flag indicating whether you want square and zoom cropped thumbnails, or thumbnails with the same aspect ratio of the source image
*/
function upload_image_and_thumbnail($data, $datakey, $imgscale, $thumbscale, $folderName, $square) {
if (strlen($data['Image'][$datakey]['name'])>4){
$error = 0;
$tempuploaddir = "img/temp"; // the /temp/ directory, should delete the image after we upload
$biguploaddir = "img/".$folderName."/big"; // the /big/ directory
$smalluploaddir = "img/".$folderName."/small"; // the /small/ directory for thumbnails

// Make sure the required directories exist, and create them if necessary
if(!is_dir($tempuploaddir)) mkdir($tempuploaddir,true);
if(!is_dir($biguploaddir)) mkdir($biguploaddir,true);
if(!is_dir($smalluploaddir)) mkdir($smalluploaddir,true);

$filetype = $this->getFileExtension($data['Image'][$datakey]['name']);
$filetype = strtolower($filetype);

if (($filetype != "jpeg") && ($filetype != "jpg") && ($filetype != "gif") && ($filetype != "png"))
{
// verify the extension
return;
}
else
{
// Get the image size
$imgsize = GetImageSize($data['Image'][$datakey]['tmp_name']);
}

// Generate a unique name for the image (from the timestamp)
$id_unic = str_replace(".", "", strtotime ("now"));
$filename = $id_unic;

settype($filename,"string");
$filename.= ".";
$filename.=$filetype;
$tempfile = $tempuploaddir . "/$filename";
$resizedfile = $biguploaddir . "/$filename";
$croppedfile = $smalluploaddir . "/$filename";

if (is_uploaded_file($data['Image'][$datakey]['tmp_name']))
{
// Copy the image into the temporary directory
if (!copy($data['Image'][$datakey]['tmp_name'],"$tempfile"))
{
print "Error Uploading File!.";
exit();
}
else {
/*
* Generate the big version of the image with max of $imgscale in either directions
*/
$this->resize_img($tempfile, $imgscale, $resizedfile);

if($square) {
/*
* Generate the small square version of the image with scale of $thumbscale
*/
$this->crop_img($tempfile, $thumbscale, $croppedfile);
}
else {
/*
* Generate the big version of the image with max of $imgscale in either directions
*/
$this->resize_img($tempfile, $thumbscale, $croppedfile);
}

// Delete the temporary image
unlink($tempfile);
}
}

// Image uploaded, return the file name
return $filename;
}
}

/*
* Deletes the image and its associated thumbnail
* Example in controller action: $this->Image->delete_image("1210632285.jpg","sets");
*
* Parameters:
* $filename: The file name of the image
* $folderName: the name of the parent folder of the images. The images will be stored to /webroot/img/$folderName/big/ and /webroot/img/$folderName/small/
*/
function delete_image($filename,$folderName) {
unlink("img/".$folderName."/big/".$filename);
unlink("img/".$folderName."/small/".$filename);
}

function crop_img($imgname, $scale, $filename) {
$filetype = $this->getFileExtension($imgname);
$filetype = strtolower($filetype);

switch($filetype){
case "jpeg":
case "jpg":
$img_src = ImageCreateFromjpeg ($imgname);
break;
case "gif":
$img_src = imagecreatefromgif ($imgname);
break;
case "png":
$img_src = imagecreatefrompng ($imgname);
break;
}

$width = imagesx($img_src);
$height = imagesy($img_src);
$ratiox = $width / $height * $scale;
$ratioy = $height / $width * $scale;

//-- Calculate resampling
$newheight = ($width <= $height) ? $ratioy : $scale;
$newwidth = ($width <= $height) ? $scale : $ratiox;

//-- Calculate cropping (division by zero)
$cropx = ($newwidth - $scale != 0) ? ($newwidth - $scale) / 2 : 0;
$cropy = ($newheight - $scale != 0) ? ($newheight - $scale) / 2 : 0;

//-- Setup Resample & Crop buffers
$resampled = imagecreatetruecolor($newwidth, $newheight);
$cropped = imagecreatetruecolor($scale, $scale);

//-- Resample
imagecopyresampled($resampled, $img_src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
//-- Crop
imagecopy($cropped, $resampled, 0, 0, $cropx, $cropy, $newwidth, $newheight);

// Save the cropped image
switch($filetype)
{

case "jpeg":
case "jpg":
imagejpeg($cropped,$filename,80);
break;
case "gif":
imagegif($cropped,$filename,80);
break;
case "png":
imagepng($cropped,$filename,80);
break;
}
}

function resize_img($imgname, $size, $filename) {
$filetype = $this->getFileExtension($imgname);
$filetype = strtolower($filetype);

switch($filetype) {
case "jpeg":
case "jpg":
$img_src = ImageCreateFromjpeg ($imgname);
break;
case "gif":
$img_src = imagecreatefromgif ($imgname);
break;
case "png":
$img_src = imagecreatefrompng ($imgname);
break;
}

$true_width = imagesx($img_src);
$true_height = imagesy($img_src);

if ($true_width>=$true_height)
{
$width=$size;
$height = ($width/$true_width)*$true_height;
}
else
{
$width=$size;
$height = ($width/$true_width)*$true_height;
}
$img_des = ImageCreateTrueColor($width,$height);
imagecopyresampled ($img_des, $img_src, 0, 0, 0, 0, $width, $height, $true_width, $true_height);

// Save the resized image
switch($filetype)
{
case "jpeg":
case "jpg":
imagejpeg($img_des,$filename,80);
break;
case "gif":
imagegif($img_des,$filename,80);
break;
case "png":
imagepng($img_des,$filename,80);
break;
}
}

function getFileExtension($str) {

$i = strrpos($str,".");
if (!$i) { return ""; }
$l = strlen($str) - $i;
$ext = substr($str,$i+1,$l);
return $ext;
}
} ?>

A easy captcha component implentation into cakephp.

First save the code below into a file called captcha.php and put it in your controller components folder

Then very important create a folder called "fonts" in your files which is located in webroot/files/ copy the fonts you want to use inside that folder you can add as many fonts as you like and use only true type fonts but here is a link to the fonts i used. www.getkeywords.co.za/files/fonts.tar.gz

uses('security');

class CaptchaComponent extends Object {

var $length = 6;

var $fontpath;

var $fonts;

var $components = array('Session');

var $controller = array();

var $sessionKey = 'Captcha';

var $case = false;

var $filters = array();

var $imgFormat = "png";

var $bgColor = array(255, 255, 255);

var $stringColor = array(0, 0, 0);


function startup(&$controller) {

if (strtolower($controller->name) == 'app' || (strtolower($controller->name) == 'tests' && Configure::read() > 0)) {
return;
}

$this->controller = $controller;

if (!method_exists($controller, 'captcha')) {

trigger_error(__('Could not find function captcha. Please create a captcha function in Controller::$controller.', true), E_USER_WARNING);
die();

}

$this->fontpath = $this->__getFontPath();

if(is_null($this->__getFonts())) {

trigger_error(__('Could not find any fonts in webroot/files/fonts/ please confirm you have created directory and have uploaded only true type fonts!', true), E_USER_WARNING);
die();

}

}


// Add this to controller action which you want to use captcha
// for and the model that has the capchta variable in.
function protect($model = 'Captcha') {

if(isset($this->controller->data[$model]['captcha']) && !empty($this->controller->data[$model]['captcha'])) {

if($this->__check($this->controller->data[$model]['captcha'])) {

$this->Session->del($this->sessionKey);
unset($this->controller->data[$model]['captcha']);

return true;

} else {

$this->__generate();
$this->Session->setFlash(__('Incorrect image verification please retry!',true));
unset($this->controller->data[$model]['captcha']);

return false;

}

} else {

$this->__generate();
return false;

}
}

// Create a function called captcha in a controller and reference
// the captcha image src in the view to it.
function show() {

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: no-store, no-cache");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");

$this->fontpath = $this->__getFontPath();

$this->fonts = $this->__getFonts();

$this->__makeCaptcha();

$this->controller->autoRender=false;

} //captcha

function __check($string) {

return ($string === $this->Session->read($this->sessionKey));

}

function __generate($protect = false) {

if(!$protect) {

$protect = !$this->Session->check($this->sessionKey);

}

if ($protect) {

$this->Session->write($this->sessionKey, $this->__stringGen());

}

}

function __getFontPath() {

return WWW_ROOT . 'files' . DS . 'fonts' . DS;

}

function __getFonts() {

$fonts = array();

if ($handle = @opendir($this->fontpath)) {

while (($file = readdir($handle)) !== FALSE) {

$extension = strtolower(substr($file, strlen($file) - 3, 3));

if ($extension == 'ttf') {

$fonts[] = $file;

}

}

closedir($handle);

} else {

return null;

}

if (count($fonts) == 0) {

return null;

} else {

return $fonts;

}

} //getFonts

function __getRandFont() {

return $this->fontpath . $this->fonts[mt_rand(0, count($this->fonts) - 1)];

} //getRandFont


function __stringGen() {

$results = null;
$uppercase = range('A', 'Z');
$numeric = range(0, 9);

$CharPool = array_merge($uppercase, $numeric);

if($this->case) {

$lowercase = range('a', 'z');
$CharPool = array_merge($CharPool, $lowercase);

}

$PoolLength = count($CharPool) - 1;

for ($i = 0; $i < $this->length; $i++) {

$results .= $CharPool[mt_rand(0, $PoolLength)];

}

return $results;

} //StringGen

function __makeCaptcha() {

$this->__generate(true);
$captchaString = $this->Session->read($this->sessionKey);

$imagelength = $this->length * 25 + 16;
$imageheight = 75;

$image = imagecreate($imagelength, $imageheight);

$bgcolor = imagecolorallocate($image, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);

$stringcolor = imagecolorallocate($image, $this->stringColor[0], $this->stringColor[1], $this->stringColor[2]);

$this->__signs($image, $this->__getRandFont());

for ($i = 0; $i < strlen($captchaString); $i++) {

imagettftext($image, 25, mt_rand(-15, 15), $i * 25 + 10,
mt_rand(30, 70),
$stringcolor,
$this->__getRandFont(),
$captchaString{$i});

}

if(isset($this->filters['noise']) && is_numeric($this->filters['noise'])) {

$this->__noise($image, $this->filters['noise']);

}

if(isset($this->filters['blur']) && is_numeric($this->filters['blur'])) {

$this->__blur($image, $this->filters['blur']);

}

switch($this->imgFormat) {

case "png" : header('Content-type: image/png');
imagepng($image);
break;

case "jpg" : header('Content-type: image/jpg');
imagejpeg($image);
break;

case "jpeg" : header('Content-type: image/jpg');
imagejpeg($image);
break;

case "gif" : header('Content-type: image/gif');
imagegif($image);
break;

default : header('Content-type: image/png');
imagejpeg($image);
break;

}

imagedestroy($image);

} //MakeCaptcha


/*-----------------------------
* FILTER FOR CAPTCHA
*
*
*------------------------------*/

function __noise(&$image, $runs = 30) {

$w = imagesx($image);
$h = imagesy($image);

for ($n = 0; $n < $runs; $n++) {

for ($i = 1; $i <= $h; $i++) {

$randcolor = imagecolorallocate($image,
mt_rand(0, 255),
mt_rand(0, 255),
mt_rand(0, 255));

imagesetpixel($image,
mt_rand(1, $w),
mt_rand(1, $h),
$randcolor);

}

}

} //noise

function __signs(&$image, $font, $cells = 3) {

$w = imagesx($image);
$h = imagesy($image);

for ($i = 0; $i < $cells; $i++) {

$centerX = mt_rand(1, $w);
$centerY = mt_rand(1, $h);
$amount = mt_rand(1, 15);
$stringcolor = imagecolorallocate($image, 175, 175, 175);

for ($n = 0; $n < $amount; $n++) {

$signs = range('A', 'Z');
$sign = $signs[mt_rand(0, count($signs) - 1)];

imagettftext($image, 25,
mt_rand(-15, 15),
$centerX + mt_rand(-50, 50),
$centerY + mt_rand(-50, 50),
$stringcolor, $font, $sign);

}

}

} //signs

function __blur(&$image, $radius = 3) {

$radius = round(max(0, min($radius, 50)) * 2);

$w = imagesx($image);
$h = imagesy($image);

$imgBlur = imagecreate($w, $h);

for ($i = 0; $i < $radius; $i++) {

imagecopy ($imgBlur, $image, 0, 0, 1, 1, $w - 1, $h - 1);
imagecopymerge($imgBlur, $image, 1, 1, 0, 0, $w, $h, 50.0000);
imagecopymerge($imgBlur, $image, 0, 1, 1, 0, $w - 1, $h, 33.3333);
imagecopymerge($imgBlur, $image, 1, 0, 0, 1, $w, $h - 1, 25.0000);
imagecopymerge($imgBlur, $image, 0, 0, 1, 0, $w - 1, $h, 33.3333);
imagecopymerge($imgBlur, $image, 1, 0, 0, 0, $w, $h, 25.0000);
imagecopymerge($imgBlur, $image, 0, 0, 0, 1, $w, $h - 1, 20.0000);
imagecopymerge($imgBlur, $image, 0, 1, 0, 0, $w, $h, 16.6667);
imagecopymerge($imgBlur, $image, 0, 0, 0, 0, $w, $h, 50.0000);
imagecopy ($image , $imgBlur, 0, 0, 0, 0, $w, $h);

}

imagedestroy($imgBlur);

} //blur

} //class: captcha

?>
Then create a helper called captcha.php and put it in your helpers folder.
Helper Class:
/******************************************************************

Projectname: CAPTCHA Helper class
Version: 1.0
Author: Michael James (mikeyjsa@gmail.com)
Website: http://www.getkeywords.co.za
Last modified: 11. June 2008

* GNU General Public License (Version 2, June 1991)
*
* This program 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 2 of the License,
* or (at your option) any later version.
*
* This program 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.

Description:
This helper is used to generate CAPTCHAs.

******************************************************************/
class CaptchaHelper extends AppHelper {

var $helpers = array('html', 'form');

function input($controller = null) {

if(is_null($controller)) {
$controller = $this->params['controller'];
}

$output = array();
$output[] = $this->html->image('/' . $controller . '/captcha/image.png', array('id' => 'captcha_image'));
$output[] = $this->form->input('Captcha/captcha', array('div' => 'captcha_input', 'label' => '(Case Sensitive!)'));

return implode("\r\n", $output);

}

}
?>

First in the controller you want to implement it into add the component then create a function called captcha and and in the following and on the form you want to protect where it checks if the data is empty just add "&& $this->Captcha->protect()". That will return a boolean back.






var $components = array('Captcha');
var $helpers = array('Captcha');

function captcha() {

$this->Captcha->show();

}

function add() {
if (!empty($this->data) && $this->Captcha->protect()) {
$this->ModelName->save($this->data);
}
}


?>
View Template:
Input this code in your .ctp file where you need a captcha

echo $captcha->input();