Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

add custom filename option #93

Closed
wants to merge 9 commits into from

5 participants

@intuxicated

hi and thanks for great plugin.
i explained about how to use custom filename option in readme file. my English is not good so maybe i missed something in readme.

i added 2 function to UploadBehavior and modified afterSave to add this feature.
there is several ways to implement custom filename, i decide to use Pattern. its very simple and easy to use.

for example this pattern :

{#NAME}_{id}

will return something like this : orginal_name_1.
you can create function in your model and call it to generate custom filename:

{!myMethod}-suffix
class Model extends AppModel {
    public function myMethod($filename = ''){
        return md5($filename);
    }
}

in above example suffix is just a simple text. and $filename is optional.it will be passed to each method you call in Pattern.

i used http://stackoverflow.com/a/13767184/440754 sanitizeFilename function to remove unsupported character from filename.

@intuxicated

filename changed currectly , but in database , i have the orginal name instead custom filename, so , what you prefer ? update again ? or should i move actions from afterSave to beforeSave ?

@vxtrim

You have mistake in document. In your example it's not working.
Shoul be:
'photo' => array(
'fields' => array(
'dir' => 'photo_dir',

),
'customName' => '{!getName}'
)

@intuxicated

@vxtrim where exactly ?

@awtprod

Thanks for taking the time to solve this problem!

I have an issue with the code though, I wanted to use the md5 hash for a file name (the last option). However, my filenames are all renamed to "1." Also, the new filename is not saved to my database.

https://gist.github.com/awtprod/4751891

Any ideas? Thanks!!

@intuxicated

@awtprod hi, in last commit i fixed the database problem.

Sorry i didn't see the gist :)

now i find out you did not apply the last commit :)

@awtprod

That worked! Thanks!

However, I can't figure out why the filename is always changed to "1." Is it because it's not reading the filename in my model?

@intuxicated

if filename changed in database correctly so you must have the exact filename.
in line 288 check $filePath value, and make sure you are in right path.

@awtprod

I'm not quite sure what you mean by that....I haven't changed that line. $path is set to the default path.

@wink-

Great improvement intuxicated. Any idea on how I could set the file name to be the same as the id of the entry it is going to be stored in the table?

@intuxicated

@wink- sorry that's not possible now , but you can use afterSave method

jlis and others added some commits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 23, 2013
  1. @intuxicated
  2. @intuxicated

    add customName document

    intuxicated authored
Commits on Jan 24, 2013
  1. @intuxicated

    fix saving in database

    intuxicated authored
Commits on Mar 1, 2013
  1. @jlis

    delete existing files only when new files are uploaded if deleteOnUpd…

    jlis authored
    …ate options is set to true
  2. @jlis
  3. @jlis
  4. @intuxicated

    Merge pull request #1 from jlis/master

    intuxicated authored
    Fixed some problems when using the deleteOnUpdate option with custom file names
Commits on Mar 4, 2013
  1. @jlis
  2. @intuxicated

    Merge pull request #2 from jlis/master

    intuxicated authored
    Fixed 'undefined index: dir' error when deleting files (i.e. on record update/delete) and no 'dir' option ist set.
This page is out of date. Refresh to see the latest.
Showing with 235 additions and 71 deletions.
  1. +83 −4 Model/Behavior/UploadBehavior.php
  2. +152 −67 README.markdown
View
87 Model/Behavior/UploadBehavior.php
@@ -45,6 +45,7 @@ class UploadBehavior extends ModelBehavior {
'deleteOnUpdate' => false,
'mediaThumbnailType'=> 'png',
'saveDir' => true,
+ 'customName' => false
);
protected $_imageMimetypes = array(
@@ -216,7 +217,9 @@ public function beforeSave(Model $model) {
$removing = isset($model->data[$model->alias][$field]['remove']);
if ($removing || ($this->settings[$model->alias][$field]['deleteOnUpdate']
&& isset($model->data[$model->alias][$field]['name'])
- && strlen($model->data[$model->alias][$field]['name']))) {
+ && strlen($model->data[$model->alias][$field]['name'])
+ && isset($this->runtime[$model->alias][$field]['tmp_name'])
+ && strlen($this->runtime[$model->alias][$field]['tmp_name']))) {
// We're updating the file, remove old versions
if (!empty($model->id)) {
$data = $model->find('first', array(
@@ -250,19 +253,29 @@ public function beforeSave(Model $model) {
unset($model->data[$model->alias][$field]);
continue;
}
-
+
$model->data[$model->alias] = array_merge($model->data[$model->alias], array(
$field => $this->runtime[$model->alias][$field]['name'],
$options['fields']['type'] => $this->runtime[$model->alias][$field]['type'],
$options['fields']['size'] => $this->runtime[$model->alias][$field]['size']
));
+
+ if (isset($model->data[$model->alias][$field])
+ && !empty($model->data[$model->alias][$field])
+ && $this->settings[$model->alias][$field]['customName'] != false
+ && isset($this->runtime[$model->alias][$field]['tmp_name'])
+ && strlen($this->runtime[$model->alias][$field]['tmp_name'])) {
+ $model->data[$model->alias][$field] = $this->_customName($model,$this->settings[$model->alias][$field]['customName'],$model->data[$model->alias][$field]);
+ }
}
+
return true;
}
public function afterSave(Model $model, $created) {
+
$temp = array($model->alias => array());
- foreach ($this->settings[$model->alias] as $field => $options) {
+ foreach ($this->settings[$model->alias] as $field => $options) {
if (!in_array($field, array_keys($model->data[$model->alias]))) continue;
if (empty($this->runtime[$model->alias][$field])) continue;
if (isset($this->_removingOnly[$field])) continue;
@@ -277,7 +290,9 @@ public function afterSave(Model $model, $created) {
$thumbnailPath .= $tempPath . DS;
}
$tmp = $this->runtime[$model->alias][$field]['tmp_name'];
- $filePath = $path . $model->data[$model->alias][$field];
+
+ $filePath = $path . $this->_sanitizeFilename($model->data[$model->alias][$field]);
+
if (!$this->handleUploadedFile($model->alias, $field, $tmp, $filePath)) {
$model->invalidate($field, 'Unable to move the uploaded file to '.$filePath);
throw new UploadException('Unable to upload file');
@@ -1255,7 +1270,11 @@ public function _getMimeType($filePath) {
public function _prepareFilesForDeletion(Model $model, $field, $data, $options) {
if (!strlen($data[$model->alias][$field])) return $this->__filesToRemove;
+ $dir = '';
+ if (isset($options['fields']['dir'])
+ && isset ($data[$model->alias][$options['fields']['dir']])) {
$dir = $data[$model->alias][$options['fields']['dir']];
+ }
$filePathDir = $this->settings[$model->alias][$field]['path'] . $dir . DS;
$filePath = $filePathDir.$data[$model->alias][$field];
$pathInfo = $this->_pathinfo($filePath);
@@ -1345,5 +1364,65 @@ public function _pathinfo($filename) {
}
return $pathInfo;
}
+
+ public function _customName(Model $model, $customName ,$filename ){
+ $filename = $this->_pathinfo($filename);
+ $customName = str_replace('{#NAME}',$filename['filename'],$customName);
+ preg_match_all("/(\{.*?})/", $customName, $matches);
+ if ($matches){
+ foreach ($matches[0] as $row){
+ $cm = substr($row,1,-1);
+ if ($cm[0] == '!'){
+ $cm = substr($cm,1);
+ $customName = str_replace($row,$model->$cm($filename['filename']),$customName);
+
+ }else{
+ $customName = str_replace($row,$model->$cm,$customName);
+ }
+ }
+ }
+ return $this->_sanitizeFilename("{$customName}.{$filename['extension']}");
+ }
+
+ /**
+ * Make a filename safe to use in any function. (Accents, spaces, special chars...)
+ * The iconv function must be activated.
+ *
+ * @param string $fileName The filename to sanitize (with or without extension)
+ * @param string $defaultIfEmpty The default string returned for a non valid filename (only special chars or separators)
+ * @param string $separator The default separator
+ * @param boolean $lowerCase Tells if the string must converted to lower case
+ *
+ * @author COil <https://github.com/COil>
+ * @see http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe
+ *
+ * @return string
+ */
+ public function _sanitizeFilename($fileName, $defaultIfEmpty = 'default', $separator = '_', $lowerCase = true)
+ {
+ // Gather file informations and store its extension
+ // use _pathinfo instead pathinfo
+ $fileInfos = $this->_pathinfo($fileName);
+ $fileExt = array_key_exists('extension', $fileInfos) ? '.'. strtolower($fileInfos['extension']) : '';
+
+ // Removes accents
+ $fileName = @iconv('UTF-8', 'us-ascii//TRANSLIT', $fileInfos['filename']);
+
+ // Removes all characters that are not separators, letters, numbers, dots or whitespaces
+ $fileName = preg_replace("/[^ a-zA-Z". preg_quote($separator). "\d\.\s]/", '', $lowerCase ? strtolower($fileName) : $fileName);
+
+ // Replaces all successive separators into a single one
+ $fileName = preg_replace('!['. preg_quote($separator).'\s]+!u', $separator, $fileName);
+
+ // Trim beginning and ending seperators
+ $fileName = trim($fileName, $separator);
+
+ // If empty use the default string
+ if (empty($fileName)) {
+ $fileName = $defaultIfEmpty;
+ }
+
+ return $fileName. $fileExt;
+ }
}
View
219 README.markdown
@@ -43,16 +43,16 @@ In your `Plugin` directory type:
To enable Imagick support, you need to have imagick installed:
- # Debian systems
- sudo apt-get install php-imagick
+ # Debian systems
+ sudo apt-get install php-imagick
- # OS X Homebrew
- brew tap homebrew/dupes
- brew tap josegonzalez/homebrew-php
- brew install php54-imagick
+ # OS X Homebrew
+ brew tap homebrew/dupes
+ brew tap josegonzalez/homebrew-php
+ brew install php54-imagick
- # From pecl
- pecl install 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.
@@ -248,6 +248,91 @@ We would also need a similar relationship in our `Message` model:
Please note that this is not the only way to represent file uploads, but it is documented here for reference.
+### Custom Filename
+
+Add `customName` option to your Model.
+
+ <?php
+ class User extends AppModel {
+ public $actsAs = array(
+ 'Upload.Upload' => array(
+ 'photo' => array(
+ 'fields' => array(
+ 'dir' => 'photo_dir',
+ 'customName' => 'PATTERN'
+ )
+ )
+ )
+ );
+ }
+
+`PATTERN` is simple text, you can use any text you want. there is 3 `Block` types.
+
+ * `{#NAME}` : will be replaced by orginal file name
+ * `{fieldname}` : will be replaced by `Model`.`fieldname` value
+ * `{!method}` : will be replaced by `Model`->`method` result
+
+#### PATTERN Usage
+
+ <?php
+ class User extends AppModel {
+ public $actsAs = array(
+ 'Upload.Upload' => array(
+ 'photo' => array(
+ 'fields' => array(
+ 'dir' => 'photo_dir',
+ ),
+ 'customName' => 'prefix_{#NAME}_suffix'
+ )
+ )
+ );
+ }
+ // File's orginal name : logo3w.png
+ // File's finally name : prefix_logo3w_suffix.png
+
+ <?php
+ class User extends AppModel {
+ public $actsAs = array(
+ 'Upload.Upload' => array(
+ 'photo' => array(
+ 'fields' => array(
+ 'dir' => 'photo_dir'
+ ),
+ 'customName' => '{name}_photo'
+ )
+ )
+ );
+
+ public $name = "Dani";
+ }
+ // File's orginal name : logo3w.png
+ // File's finally name : Dani_photo.png
+
+ <?php
+ class User extends AppModel {
+ public $actsAs = array(
+ 'Upload.Upload' => array(
+ 'photo' => array(
+ 'fields' => array(
+ 'dir' => 'photo_dir',
+ ),
+ 'customName' => '{!getName}'
+ )
+ )
+ );
+
+ /**
+ * @param string $filename File's orginal name. its optional.
+ */
+ public function getName($filename = ''){
+ return md5($filename);
+ }
+ }
+ // File's orginal name : logo3w.png
+ // File's finally name : 29068a87847a81f8bc00b600421ddbd6.png
+
+Note that `Blocks` are `optional`.
+
### Alternative Behaviors
The Upload plugin also comes with a `FileImport` behavior and a `FileGrabber` behavior.
@@ -281,87 +366,87 @@ Please note: FileGrabber input field does not need to have `'type' => 'file'` se
## 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. Does not persist across a record update.
- * randomCombined: Random path - with model id - is generated for each file upload. Does not persist across a record update.
+ * 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. Does not persist across a record update.
+ * randomCombined: Random path - with model id - is generated for each file upload. Does not persist across a record update.
* `path`: A path relative to the `APP_PATH`. 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.
+ * 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
+ * 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`
+ * Default (string) `ROOT . DS . APP_DIR . DS`
* `mimetypes`: Array of mimetypes to use for validation
- * Default: (array) empty
+ * Default: (array) empty
* `extensions`: Array of extensions to use for validation
- * Default: (array) empty
+ * Default: (array) empty
* `maxSize`: Max filesize in bytes for validation
- * Default: (int) `2097152`
+ * Default: (int) `2097152`
* `minSize`: Minimum filesize in bytes for validation
- * Default: (int) `8`
+ * Default: (int) `8`
* `maxHeight`: Maximum image height for validation
- * Default: (int) `0`
+ * Default: (int) `0`
* `minHeight`: Minimum image height for validation
- * Default: (int) `0`
+ * Default: (int) `0`
* `maxWidth`: Maximum image width for validation
- * Default: (int) `0`
+ * Default: (int) `0`
* `minWidth`: Minimum image width for validation
- * Default: (int) `0`
+ * Default: (int) `0`
* `deleteOnUpdate`: Whether to delete files when uploading new versions (potentially dangerous due to naming conflicts)
- * Default: (boolean) `false`
+ * Default: (boolean) `false`
* `thumbnails`: Whether to create thumbnails or not
- * Default: (boolean) `true`
+ * 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.
+ * 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}` and `{filename}` are both 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`
+ * Default: `NULL`
+ * Note: The tokens `{size}` and `{filename}` are both 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`
+ * 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
+ * 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`
+ * Default: (int) `75`
* `thumbnailSizes`: Array of thumbnail sizes, with the size-name mapping to a geometry
- * Default: (array) empty
+ * 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
+ * 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
+ * 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
+ * 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
## Thumbnail Sizes and Styles
Something went wrong with that request. Please try again.