[![Build Status](https://travis-ci.org/josegonzalez/cakephp-upload.png?branch=master)](https://travis-ci.org/josegonzalez/cakephp-upload) [![Coverage Status](https://coveralls.io/repos/josegonzalez/cakephp-upload/badge.png?branch=master)](https://coveralls.io/r/josegonzalez/cakephp-upload?branch=master) [![Total Downloads](https://poser.pugx.org/josegonzalez/cakephp-upload/d/total.png)](https://packagist.org/packages/josegonzalez/cakephp-upload) [![Latest Stable Version](https://poser.pugx.org/josegonzalez/cakephp-upload/v/stable.png)](https://packagist.org/packages/josegonzalez/cakephp-upload)

# Upload Plugin 2.0

The Upload Plugin is an attempt to sanely upload files using techniques garnered packages such as [MeioUpload](http://github.com/jrbasso/MeioUpload) , [UploadPack](http://github.com/szajbus/cakephp-uploadpack) and [PHP documentation](http://php.net/manual/en/features.file-upload.php).

## Background

Media Plugin is too complicated, and it was a PITA to merge the latest updates into MeioUpload, so here I am, building yet another upload plugin. I'll build another in a month and call it "YAUP".

## Requirements

* CakePHP 2.x
* Imagick/GD PHP Extension (for Thumbnail Creation)
* PHP5
* Patience

## Installation

_[Using [Composer](http://getcomposer.org/)]_

[View on Packagist](https://packagist.org/packages/josegonzalez/cakephp-upload), and copy the json snippet for the latest version into your project's `composer.json`. Eg, v. 1.0.0 would look like this:

```javascript
{
	"require": {
		"josegonzalez/cakephp-upload": "1.0.0"
	}
}
```

Because this plugin has the type `cakephp-plugin` set in it's own `composer.json`, composer knows to install it inside your `/Plugins` directory, rather than in the usual vendors file. It is recommended that you add `/Plugins/Upload` to your .gitignore file. (Why? [read this](http://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md).)

_[Manual]_

* Download this: [http://github.com/josegonzalez/cakephp-upload/zipball/master](http://github.com/josegonzalez/cakephp-upload/zipball/master)
* Unzip that download.
* Copy the resulting folder to `app/Plugin`
* Rename the folder you just copied to `Upload`

_[GIT Submodule]_

In your app directory type:

```shell
git submodule add -b master git://github.com/josegonzalez/cakephp-upload.git Plugin/Upload
git submodule init
git submodule update
```

_[GIT Clone]_

In your `Plugin` directory type:

```shell
git clone -b master git://github.com/josegonzalez/cakephp-upload.git Upload
```

### Imagick Support

To enable Imagick support, you need to have imagick installed:

```shell
# Debian systems
sudo apt-get install php-imagick

# OS X Homebrew
brew tap homebrew/dupes
brew tap josegonzalez/homebrew-php
brew install php54-imagick

# From pecl
pecl install imagick
```

If you cannot install imagick, please do not use imagick, and instead configure the plugin with `'thumbnailMethod'  => 'php'` in your setup options.

### Enable plugin

In 2.0 you need to enable the plugin your `app/Config/bootstrap.php` file:

```php
<?php
CakePlugin::load('Upload');
?>
```

If you are already using `CakePlugin::loadAll();`, then this is not necessary.

## Usage

```sql
CREATE table users (
	id int(10) unsigned NOT NULL auto_increment,
	username varchar(20) NOT NULL,
	photo varchar(255)
);
```

```php
<?php
class User extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'photo'
		)
	);
}
?>
```

```php
<?php echo $this->Form->create('User', array('type' => 'file')); ?>
<?php echo $this->Form->input('User.username'); ?>
<?php echo $this->Form->input('User.photo', array('type' => 'file')); ?>
<?php echo $this->Form->end(); ?>
```

Using the above setup, uploaded files cannot be deleted. To do so, a field must be added to store the directory of the file as follows:

```sql
CREATE table users (
	`id` int(10) unsigned NOT NULL auto_increment,
	`username` varchar(20) NOT NULL,
	`photo` varchar(255) DEFAULT NULL,
	`photo_dir` varchar(255) DEFAULT NULL,
	PRIMARY KEY (`id`)
);
```

```php
<?php
class User extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'photo' => array(
				'fields' => array(
					'dir' => 'photo_dir'
				)
			)
		)
	);
}
?>
```

In the above example, photo can be a file upload via a file input within a form, a file grabber (from a url) via a text input, OR programatically used on the controller to file grab via a url.

### File Upload Example

```php
<?php echo $this->Form->create('User', array('type' => 'file')); ?>
	<?php echo $this->Form->input('User.username'); ?>
	<?php echo $this->Form->input('User.photo', array('type' => 'file')); ?>
	<?php echo $this->Form->input('User.photo_dir', array('type' => 'hidden')); ?>
<?php echo $this->Form->end(); ?>
```

### File Grabbing via Form Example

```php
<?php echo $this->Form->create('User', array('type' => 'file')); ?>
	<?php echo $this->Form->input('User.username'); ?>
	<?php echo $this->Form->input('User.photo', array('type' => 'file')); ?>
	<?php echo $this->Form->input('User.photo_dir', array('type' => 'hidden')); ?>
<?php echo $this->Form->end(); ?>
```

### Programmatic File Retrieval without a Form

```php
<?php
$this->User->set(array('photo' => $image_url));
$this->User->save();
?>
```

### Thumbnail Creation

Thumbnails are not automatically created. To do so, thumbnail sizes must be defined:
Note: by default thumbnails will be generated by imagick, if you want to use GD you need to set the thumbnailMethod attribute. Example: `'thumbnailMethod'  => 'php'`.

```php
<?php
class User extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'photo' => array(
				'thumbnailSizes' => array(
					'xvga' => '1024x768',
					'vga' => '640x480',
					'thumb' => '80x80'
				)
			)
		)
	);
}
?>
```

### Uploading Multiple files

Multiple files can also be attached to a single record:

```php
<?php
class User extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'resume',
			'photo' => array(
				'fields' => array(
					'dir' => 'profile_dir'
				)
			)
		)
	);
}
?>
```

Each key in the `Upload.Upload` array is a field name, and can contain it's own configuration. For example, you might want to set different fields for storing file paths:

```php
<?php
class User extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'resume' => array(
				'fields' => array(
					'dir' => 'resume_dir',
					'type' => 'resume_type',
					'size' => 'resume_size',
				)
			),
			'photo' => array(
				'fields' => array(
					'dir' => 'photo_dir',
					'type' => 'photo_type',
					'size' => 'photo_size',
				)
			)
		)
	);
}
?>
```

Keep in mind that while this plugin does not have any limits in terms of number of files uploaded per request, you should keep this down in order to decrease the ability of your users to block other requests.

### PDF Support

It is now possible to generate a thumbnail for the first page of a PDF file. (Only works with the `imagick` `thumbnailMethod`.)

Please read about the Behavior options for more details as to how to configure this plugin.

### Using a Polymorphic Attachment Model for File Storage

In some cases you will want to store multiple file uploads for a multiple models, but will not want to use multiple tables for some reason. For example, we might have a `Post` model that can have many images for a gallery, and a `Message` model that has many videos. In this case, we would use an `Attachment` model:

Post hasMany Attachment

We could use the following database schema for the `Attachment` model:

```sql
CREATE table attachments (
	`id` int(10) unsigned NOT NULL auto_increment,
	`model` varchar(20) NOT NULL,
	`foreign_key` int(11) NOT NULL,
	`name` varchar(32) NOT NULL,
	`attachment` varchar(255) NOT NULL,
	`dir` varchar(255) DEFAULT NULL,
	`type` varchar(255) DEFAULT NULL,
	`size` int(11) DEFAULT 0,
	`active` tinyint(1) DEFAULT 1,
	PRIMARY KEY (`id`)
);
```

Our attachment records would thus be able to have a name and be activated/de-activated on the fly. The schema is simply an example, and such functionality would need to be implemented within your application.

Once the `attachments` table has been created, we would create the following model:

```php
<?php
class Attachment extends AppModel {
	public $actsAs = array(
		'Upload.Upload' => array(
			'attachment' => array(
				'thumbnailSizes' => array(
					'xvga' => '1024x768',
					'vga' => '640x480',
					'thumb' => '80x80',
				),
			),
		),
	);

	public $belongsTo = array(
		'Post' => array(
			'className' => 'Post',
			'foreignKey' => 'foreign_key',
		),
		'Message' => array(
			'className' => 'Message',
			'foreignKey' => 'foreign_key',
		),
	);
}
?>
```

We would also need to present a valid counter-relationship in the `Post` model:

```php
<?php
class Post extends AppModel {
	public $hasMany = array(
		'Image' => array(
			'className' => 'Attachment',
			'foreignKey' => 'foreign_key',
			'conditions' => array(
				'Image.model' => 'Post',
			),
		),
	);
}
?>
```

The key thing to note here is the `Post` model has some conditions on the relationship to the `Attachment` model, where the `Image.model` has to be `Post`. Remember to set the `model` field to `Post`, or whatever model it is you'd like to attach it to, otherwise you may get incorrect relationship results when performing find queries.

We would also need a similar relationship in our `Message` model:

```php
<?php
class Message extends AppModel {
	public $hasMany = array(
		'Video' => array(
			'className' => 'Attachment',
			'foreignKey' => 'foreign_key',
			'conditions' => array(
				'Video.model' => 'Message',
			),
		),
	);
}
?>
```

Now that we have our models setup, we should create the proper actions in our controllers. To keep this short, we shall only document the Post model:

```php
<?php
class PostsController extends AppController {
	/* the rest of your controller here */
	public function add() {
		if ($this->request->is('post')) {
			try {
				$this->Post->createWithAttachments($this->request->data);
				$this->Session->setFlash(__('The message has been saved'));
			} catch (Exception $e) {
				$this->Session->setFlash($e->getMessage());
			}
		}
	}
}
?>
```

In the above example, we are calling our custom `createWithAttachments` method on the `Post` model. This will allow us to unify the Post creation logic together in one place. That method is outlined below:

```php
<?php
class Post extends AppModel {
	/* the rest of your model here */

	public function createWithAttachments($data) {
		// Sanitize your images before adding them
		$images = array();
		if (!empty($data['Image'][0])) {
			foreach ($data['Image'] as $i => $image) {
				if (is_array($data['Image'][$i])) {
					// Force setting the `model` field to this model
					$image['model'] = 'Post';

					// Unset the foreign_key if the user tries to specify it
					if (isset($image['foreign_key'])) {
						unset($image['foreign_key']);
					}

					$images[] = $image;
				}
			}
		}
		$data['Image'] = $images;

		// Try to save the data using Model::saveAll()
		$this->create();
		if ($this->saveAll($data)) {
			return true;
		}

		// Throw an exception for the controller
		throw new Exception(__("This post could not be saved. Please try again"));
	}
}
?>
```

The above model method will:

- Ensure we only try to save valid images
- Force the foreign_key to be unspecified. This will allow saveAll to properly associate it
- Force the model field to `Post`

Now that this is set, we just need a view for our controller. A sample view for `View/Posts/add.ctp` is as follows (fields not necessary for the example are omitted):

```php
<?php
	echo $this->Form->create('Post', array('type' => 'file'));
	echo $this->Form->input('Image.0.attachment', array('type' => 'file', 'label' => 'Image'));
	echo $this->Form->input('Image.0.model', array('type' => 'hidden', 'value' => 'Post'));
	echo $this->Form->end(__('Add'));
?>
```

The one important thing you'll notice is that I am not referring to the `Attachment` model as `Attachment`, but rather as `Image`; when I initially specified the `$hasMany` relationship between an `Attachment` and a `Post`, I aliased `Attachment` to `Image`. This is necessary for cases where many of your Polymorphic models may be related to each other, as a type of *hint* to the CakePHP ORM to properly reference model data.

I'm also using `Model.{n}.field` notation, which would allow you to add multiple attachment records to the Post. This is necessary for `$hasMany` relationships, which we are using for this example.

Once you have all the above in place, you'll have a working Polymorphic upload!

Please note that this is not the only way to represent file uploads, but it is documented here for reference.

### Alternative Behaviors

The Upload plugin also comes with a `FileImport` behavior and a `FileGrabber` behavior.

#### FileImportBehavior

`FileImportBehavior` may be used to import files directly from the disk. This is useful in importing from a directory already on the filesystem.

## Behavior options:

* `pathMethod`: The method to use for file paths. This is appended to the `path` option below
	* Default: (string) `primaryKey`
	* Options:
		* `flat`: Does not create a path for each record. Files are moved to the value of the 'path' option.
		* `primaryKey`: Path based upon the record's primaryKey is generated. Persists across a record update.
		* `random`: Random path is generated for each file upload in the form `nn/nn/nn` where `nn` are random numbers. Does not persist across a record update.
		* `randomCombined`: Random path - with model id - is generated for each file upload in the form `ID/nn/nn/nn` where `ID` is the current model's ID and `nn` are random numbers. Does not persist across a record update.
* `path`: A path relative to the `rootDir`. Should end in `{DS}`
	* Default: (string) `'{ROOT}webroot{DS}files{DS}{model}{DS}{field}{DS}'`
	* Tokens:
		* {ROOT}: Replaced by a `rootDir` option
		* {DS}: Replaced by a `DIRECTORY_SEPARATOR`
		* {model}: Replaced by the Model Alias.
		* {field}: Replaced by the field name.
		* {primaryKey}: Replaced by the record primary key, when available. If used on a new record being created, will have undefined behavior.
		* {size}: Replaced by a zero-length string (the empty string) when used for the regular file upload path. Only available for resized thumbnails.
		* {geometry}: Replaced by a zero-length string (the empty string) when used for the regular file upload path. Only available for resized thumbnails.
* `fields`: An array of fields to use when uploading files
	* Default: (array) `array('dir' => 'dir', 'type' => 'type', 'size' => 'size')`
	* Options:
		* dir: Field to use for storing the directory
		* type: Field to use for storing the filetype
		* size: Field to use for storing the filesize
* `rootDir`: Root directory for moving images. Auto-prepended to `path` and `thumbnailPath` where necessary
	* Default (string) `ROOT . DS . APP_DIR . DS`
* `mimetypes`: Array of mimetypes to use for validation
	* Default: (array) empty
* `extensions`: Array of extensions to use for validation
	* Default: (array) empty
* `maxSize`: Max filesize in bytes for validation
	* Default: (int) `2097152`
* `minSize`: Minimum filesize in bytes for validation
	* Default: (int) `8`
* `maxHeight`: Maximum image height for validation
	* Default: (int) `0`
* `minHeight`: Minimum image height for validation
	* Default: (int) `0`
* `maxWidth`: Maximum image width for validation
	* Default: (int) `0`
* `minWidth`: Minimum image width for validation
	* Default: (int) `0`
* `deleteOnUpdate`: Whether to delete files when uploading new versions (potentially dangerous due to naming conflicts)
	* Default: (boolean) `false`
* `thumbnails`: Whether to create thumbnails or not
	* Default: (boolean) `true`
* `thumbnailMethod`: The method to use for resizing thumbnails
	* Default: (string) `imagick`
	* Options:
		* imagick: Uses the PHP `imagick` extension to generate thumbnails
		* php: Uses the built-in PHP methods (`GD` extension) to generate thumbnails. Does not support BMP images.
* `thumbnailName`: Naming style for a thumbnail
	* Default: `NULL`
	* Note: The tokens `{size}`, `{geometry}` and `{filename}` are valid for naming and will be auto-replaced with the actual terms.
	* Note: As well, the extension of the file will be automatically added.
	* Note: When left unspecified, will be set to `{size}_{filename}` or `{filename}_{size}` depending upon the value of `thumbnailPrefixStyle`
* `thumbnailPath`: A path relative to the `rootDir` where thumbnails will be saved. Should end in `{DS}`. If not set, thumbnails will be saved at `path`.
	* Default: `NULL`
	* Tokens:
		* {ROOT}: Replaced by a `rootDir` option
		* {DS}: Replaced by a `DIRECTORY_SEPARATOR`
		* {model}: Replaced by the Model Alias
		* {field}: Replaced by the field name
		* {size}: Replaced by the size key specified by a given `thumbnailSize`
		* {geometry}: Replaced by the geometry value specified by a given `thumbnailSize`
* `thumbnailPrefixStyle`: Whether to prefix or suffix the style onto thumbnails
	* Default: (boolean) `true` prefix the thumbnail
	* Note that this overrides `thumbnailName` when `thumbnailName` is not specified in your config
* `thumbnailQuality`: Quality of thumbnails that will be generated, on a scale of 0-100. Not supported gif images when using GD for image manipulation.
	* Default: (int) `75`
* `thumbnailSizes`: Array of thumbnail sizes, with the size-name mapping to a geometry
	* Default: (array) empty
* `thumbnailType`: Override the type of the generated thumbnail
	* Default: (mixed) `false` or `png` when the upload is a Media file
	* Options:
		* Any valid image type
* `mediaThumbnailType`: Override the type of the generated thumbnail for a non-image media (`pdfs`). Overrides `thumbnailType`
	* Default: (mixed) `png`
	* Options:
		* Any valid image type
* `saveDir`: Can be used to turn off saving the directory
	* Default: (boolean) `true`
	* Note: Because of the way in which the directory is saved, if you are using a `pathMethod` other than flat and you set `saveDir` to false, you may end up in situations where the file is in a location that you cannot predict. This is more of an issue for a `pathMethod` of `random` and `randomCombined` than `primaryKey`, but keep this in mind when fiddling with this option
* `mode`: The UNIX permissions to set on the created upload directories.
	* Default: (integer) `0777`

## Thumbnail Sizes and Styles

Styles are the definition of thumbnails that will be generated for original image. You can define as many as you want.

```php
<?php
class User extends AppModel {
	public $name = 'User';
	public $actsAs = array(
		'Upload.Upload' => array(
			'photo' => array(
				'thumbnailSizes' => array(
					'big' => '200x200',
					'small' => '120x120'
					'thumb' => '80x80'
				)
			)
		)
	);
}
?>
```

Styles only apply to images of the following types:

* image/bmp
* image/gif
* image/jpeg
* image/pjpeg
* image/png
* image/vnd.microsoft.icon
* image/x-icon

You can specify any of the following resize modes for your sizes:

* `100x80` - resize for best fit into these dimensions, with overlapping edges trimmed if original aspect ratio differs
* `[100x80]` - resize to fit these dimensions, with white banding if original aspect ratio differs
* `100w` - maintain original aspect ratio, resize to 100 pixels wide
* `80h` - maintain original aspect ratio, resize to 80 pixels high
* `80l` - maintain original aspect ratio, resize so that longest side is 80 pixels
* `600mw` - maintain original aspect ratio, resize to max 600 pixels wide, or copy the original image if it is less than 600 pixels wide
* `800mh` - maintain original aspect ratio, resize to max 800 pixels high, or copy the original image if it is less than 800 pixels high
* `960ml` - maintain original aspect ratio, resize so that longest side is max 960 pixels, or copy the original image if the thumbnail would be bigger than the origina

## Validation rules

By default, no validation rules are attached to the model. One must explicitly attach each rule if needed. Rules not referring to PHP upload errors are configurable but fallback to the behavior configuration.

#### isUnderPhpSizeLimit

Check that the file does not exceed the max file size specified by PHP

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isUnderPhpSizeLimit',
		'message' => 'File exceeds upload filesize limit'
	)
);
?>
```

#### isUnderFormSizeLimit

Check that the file does not exceed the max file size specified in the HTML Form

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isUnderFormSizeLimit',
		'message' => 'File exceeds form upload filesize limit'
	)
);
?>
```

#### isCompletedUpload

Check that the file was completely uploaded

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isCompletedUpload',
		'message' => 'File was not successfully uploaded'
	)
);
?>
```

#### isFileUpload

Check that a file was uploaded

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isFileUpload',
		'message' => 'File was missing from submission'
	)
);
?>
```

#### isFileUploadOrHasExistingValue

Check that either a file was uploaded, or the existing value in the database is not blank

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isFileUploadOrHasExistingValue',
		'message' => 'File was missing from submission'
	)
);
?>
```

#### tempDirExists

Check that the PHP temporary directory is missing

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'tempDirExists',
		'message' => 'The system temporary directory is missing'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('tempDirExists', false),
		'message' => 'The system temporary directory is missing'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isSuccessfulWrite

Check that the file was successfully written to the server

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'isSuccessfulWrite',
		'message' => 'File was unsuccessfully written to the server'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isSuccessfulWrite', false),
		'message' => 'File was unsuccessfully written to the server'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### noPhpExtensionErrors

Check that a PHP extension did not cause an error

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => 'noPhpExtensionErrors',
		'message' => 'File was not uploaded because of a faulty PHP extension'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('noPhpExtensionErrors', false),
		'message' => 'File was not uploaded because of a faulty PHP extension'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isValidMimeType

Check that the file is of a valid mimetype

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidMimeType', array('application/pdf', 'image/png')),
		'message' => 'File is not a pdf or png'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidMimeType', array('application/pdf', 'image/png'), false),
		'message' => 'File is not a pdf or png'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isWritable

Check that the upload directory is writable

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isWritable'),
		'message' => 'File upload directory was not writable'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isWritable', false),
		'message' => 'File upload directory was not writable'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isValidDir

Check that the upload directory exists

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidDir'),
		'message' => 'File upload directory does not exist'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidDir', false),
		'message' => 'File upload directory does not exist'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isBelowMaxSize

Check that the file is below the maximum file upload size (checked in bytes)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxSize', 1024),
		'message' => 'File is larger than the maximum filesize'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxSize', 1024, false),
		'message' => 'File is larger than the maximum filesize'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isAboveMinSize

Check that the file is above the minimum file upload size (checked in bytes)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinSize', 1024),
		'message' => 'File is below the mimimum filesize'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinSize', 1024, false),
		'message' => 'File is below the mimimum filesize'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isValidExtension

Check that the file has a valid extension

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidExtension', array('pdf', 'png', 'txt')),
		'message' => 'File does not have a pdf, png, or txt extension'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isValidExtension', array('pdf', 'png', 'txt'), false),
		'message' => 'File does not have a pdf, png, or txt extension'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isAboveMinHeight

Check that the file is above the minimum height requirement (checked in pixels)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinHeight', 150),
		'message' => 'File is below the minimum height'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinHeight', 150, false),
		'message' => 'File is below the minimum height'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isBelowMaxHeight

Check that the file is below the maximum height requirement (checked in pixels)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxHeight', 150),
		'message' => 'File is above the maximum height'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxHeight', 150, false),
		'message' => 'File is above the maximum height'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isAboveMinWidth

Check that the file is above the minimum width requirement (checked in pixels)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinWidth', 150),
		'message' => 'File is below the minimum width'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isAboveMinWidth', 150, false),
		'message' => 'File is below the minimum width'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

#### isBelowMaxWidth

Check that the file is below the maximum width requirement (checked in pixels)

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxWidth', 150),
		'message' => 'File is above the maximum width'
	)
);
?>
```

If the argument `$requireUpload` is passed, we can skip this check when a file is not uploaded:

```php
<?php
public $validate = array(
	'photo' => array(
		'rule' => array('isBelowMaxWidth', 150, false),
		'message' => 'File is above the maximum width'
	)
);
?>
```

In the above, the variable `$requireUpload` has a value of false. By default, `requireUpload` is set to true.

## Remove a current file without deleting the entire record

In some cases you might want to remove a photo or uploaded file without having to
remove the entire record from the Model.  In this case you would use the following code:

```php
<?php
echo $this->Form->create('Model', array('type' => 'file'));
echo $this->Form->input('Model.file.remove', array('type' => 'checkbox', 'label' => 'Remove existing file'));
?>
```

## License

The MIT License (MIT)

Copyright (c) 2010 Jose Diaz-Gonzalez

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
