I am trying to implement Miles Johnsons excellent Uploader Component into my app. But here is the problem: Depending on the dimensions of the uploaded image, I need to change the resize dimensions.
I tried to modify the transformation in the callback:
public $actsAs = array(
'Uploader.Attachment' => array(
'image' => array(
'nameCallback' => 'formatName',
'tempDir' => TMP,
'uploadDir' => UPLOADDIR,
'finalPath' => '/img/photos/',
'overwrite' => false,
'stopSave' => true,
'allowEmpty' => false,
'transforms' => array(
array(
'class' => 'exif',
'self' => true
),
'image_sized' => array(
'class' => 'resize',
'nameCallback' => 'transformSizedNameCallback',
'width' => 1680,
'height' => 980,
'aspect' => true
)
)
)
),
'Uploader.FileValidation' => array(
'image' => array(
'extension' => array(
'value' => array('jpg', 'png', 'jpeg'),
'error' => 'Nicht unterstütztes Dateiformat - bitte JPG- oder PNG-Datei hochladen.'
),
'minHeight' => array(
'value' => 980,
'error' => 'Das Bild muss mindestens 980 Pixel hoch sein.'
),
'minWidth' => array(
'value' => 980,
'error' => 'Das Bild muss mindestens 980 Pixel breit sein.'
),
'required' => array(
'value' => true,
'error' => 'Bitte wählen Sie ein Bild aus.'
)
)
)
);
public function beforeTransform($options) {
if($options['dbColumn'] == 'image_sized') {
if($height > $width) {
$options['width'] = 980;
$options['height'] = 1680;
}
}
return $options;
}
I am able to pinpoint the correct transform, but how do I access the dimensions of the image to be transformed inside of beforeTransform? Where do I get $width and $height from?
I'm not familiar with it, but from looking at the code it seems like the only option you have at that point is using the dbColumn value to access the currently processed field data, something like
$file = $this->data[$this->alias][$options['dbColumn']];
Of course this requires the dbColumn value to match the input field name! If that's not the case, then you'll need an additional option that holds the field name and use that one instead.
Now $file is just the raw data, most probably a file upload array. Assuming a single file, check tmp_name for its dimensions, either by yourself, or utilize the Transite\File class which can handle file upload arrays and exposes a method for retrieving the dimensions of a possible image:
$transitFile = new File($file);
$dimensions = $transitFile->dimensions();
https://github.com/milesj/transit/blob/1.5.1/src/Transit/File.php#L121
So finally you could do something like this:
public function beforeTransform($options) {
if($options['dbColumn'] == 'image_sized') {
$file = $this->data[$this->alias][$options['dbColumn']];
$transitFile = new \Transit\File($file);
$dimensions = $transitFile->dimensions();
if($dimensions === null) {
// not an image or something else gone wrong,
// maybe throw an exception or wave your arms and call for help
} elseif($dimensions['height'] > $dimensions['width']) {
$options['width'] = 980;
$options['height'] = 1680;
}
}
return $options;
}
Please not that this is all untested example code.
Related
How can I keep the same checkboxes checked after submit? All the other input fields on the form automatically keeps the values. I thought this would also go for checkboxes, but nope.
echo $this->Form->input('type_id', array(
'multiple' => 'checkbox',
'options' => array(
'1' => 'Til salgs',
'2' => 'Ønskes kjøpt',
'3' => 'Gis bort'
),
'div' => false,
'label' => false
));
I believe this can be done in the controller, but how?
Edit:
Since I posted this question I've changed to CakeDcs Search plugin, because I've gotten this to work with that before. Still... I can't get it to work this time.
Adding model and controller code:
AppController
public $components = array('DebugKit.Toolbar',
'Session',
'Auth' => array(
'loginAction' => '/',
'loginRedirect' => '/login',
'logoutRedirect' => '/',
'authError' => 'Du må logge inn for å vise denne siden.',
'authorize' => array('Controller'),
),
'Search.Prg'
);
public $presetVars = true; //Same as in model filterArgs(). For Search-plugin.
AdsController
public function view() {
$this->set('title_for_layout', 'Localtrade Norway');
$this->set('show_searchbar', true); //Shows searchbar div in view
$this->log($this->request->data, 'debug');
//Setting users home commune as default filter when the form is not submitted.
$default_filter = array(
'Ad.commune_id' => $this->Auth->user('User.commune_id')
);
$this->Prg->commonProcess(); //Search-plugin
$this->paginate = array(
'conditions' => array_merge($default_filter, $this->Ad->parseCriteria($this->passedArgs)), //If Ad.commune_id is empty in second array, then the first will be used.
'fields' => $this->Ad->setFields(),
'limit' => 3
);
$this->set('res', $this->paginate());
}
Model
public $actsAs = array('Search.Searchable');
public $filterArgs = array(
'search_field' => array('type' => 'query', 'method' => 'filterSearchField'),
'commune_id' => array('type' => 'value'),
'type_id' => array('type' => 'int')
);
public function filterSearchField($data) {
if (empty($data['search_field'])) {
return array();
}
$str_filter = '%' . $data['search_field'] . '%';
return array(
'OR' => array(
$this->alias . '.title LIKE' => $str_filter,
$this->alias . '.description LIKE' => $str_filter,
)
);
}
/**
* Sets the fields which will be returned by the search.
*
* #access public
* #return array Database table fields
* #author Morten Flydahl
*
*/
public function setFields() {
return array(
'Ad.id',
'Ad.title',
'Ad.description',
'Ad.price',
'Ad.modified',
'User.id',
'User.first_name',
'User.middle_name',
'User.last_name',
'User.link',
'User.picture_url',
'Commune.name',
'Type.id',
'Type.name'
);
}
You have to set manually the selected option of the input, as an array with "keys = values = intval(checkbox id)"
I cannot explain why this format, but this is the only way I get it to work.
Here is my code:
echo $this->Form->create('User');
// Read the submitted value
$selected = $this->Form->value('User.Albums');
// Formats the value
if (empty($selected)) {
$selected = array(); // avoid mess
} else {
$selected = array_map('intval', $selected);
$selected = array_combine ($selected, $selected);
}
// Renders the checkboxes
echo $this->Form->input('Albums',array(
'type' => 'select',
'multiple' => 'checkbox',
'options' => $albums, // array ( (int)id => string(label), ... )
'selected' => $selected, // array ( (int)id => (int)id, ... )
));
Hope this helps.
++
I'd like to use the 'between' example from cakedc, but just can't make sense out of it.
'range' => array('type' => 'expression', 'method' => 'makeRangeCondition', 'field' => 'Article.views BETWEEN ? AND ?'),
I have field qca_start in my table and want user to provide two values (from, to) and search for qca_start between from and to.
My controller:
(I've used other simpler searches without problem. (employee_id works just fine here)
public $presetVars = array(
array('field' => 'employee_id', 'type' => 'value'),
array('field' => 'qca_start', 'type' => 'value') // not sure what type to use here for between search.
};
The field on my table is qca_start, not user how would i name the presetVar for this?
On my model
public $filterArgs = array(
array('name' => 'employee_id', 'type' => 'value'),
'range' => array('type' => 'expression', 'method' => 'makeRangeCondition', 'field' => 'Article.views BETWEEN ? AND ?'),
);
I don't know how to format this for filterArgs:
'range' => array('type' => 'expression', 'method' => 'makeRangeCondition', 'field' => 'Article.views BETWEEN ? AND ?'),
I want qca_start to be between search values One and Two.
Can you help?
a copy/paste from the answer i gave # CakeDC Plugin Search Between Dates
in model :
'creationDateBetween' => array(
'type' => 'expression',
'method' => 'CreationDateRangeCondition',
'field' => 'MODEL.creationdate BETWEEN ? AND ?',
),
public function CreationDateRangeCondition($data = array()){
if(strpos($data['creationDateBetween'], ' - ') !== false){
$tmp = explode(' - ', $data['creationDateBetween']);
$tmp[0] = $tmp[0]."-01-01";
$tmp[1] = $tmp[1]."-12-31";
return $tmp;
}else{
return array($data['creationDateBetween']."-01-01", $data['creationDateBetween']."-12-31");
}
}
in view : note that i'm using a slider for year range
echo $this->Form->input('creationDateBetween',
array(
'label' => __('Creation date between X and Y'),
'div' => false,
'style' => 'border: 0; color: #49AFCD; font-weight: bold;'
)
);
?><div id="creationDateBetweenSlider" style="padding:0;"></div><?php
<script>
$(function() {
var creationDateBetweenSlider = $( "#creationDateBetweenSlider" ),
institutionCreationDateBetween = $( "#MODELCreationDateBetween" ),
lock = 0;
creationDateBetweenSlider.slider({
range: true,
min: 1900,
max: 2050,
values: [ 2000, 2013 ],
slide: function( event, ui ) {
MODELCreationDateBetween.val( ui.values[ 0 ] + " - " + ui.values[ 1 ] );
}
});
if(lock != 0) MODELCreationDateBetween.val( creationDateBetweenSlider.slider( "values", 0 ) + " - " + creationDateBetweenSlider.slider( "values", 1 ) );
lock = 1;
});
</script>
waiting for feedback to see if it works for you ;)
You should read the documenation # https://github.com/cakedc/search
'expression' type useful if you want to add condition that will
generate by some method, and condition field contain several parameter
like in previous sample used for 'range'. Field here contains
'Article.views BETWEEN ? AND ?' and Article::makeRangeCondition
returns array of two values.
So just return 2 values in your method:
public function makeRangeCondition() {
...
return array($from, $to);
}
They will automatically replace the two ? in this order then.
I have a site in cakephp 2 and I want to upload a file with the plugin Uploader of Miles J.
But return me this error:
Column not found: 1054 Unknown column 'Array' in 'field list'
SQL Query:
UPDATE `db148147_trimalcione`.`ingredient_images`
SET
`id` = 6,
`filename` = Array,
`ingredient_id` = 8,
`modified` = '2012-08-21 23:01:13'
WHERE `db148147_trimalcione`.`ingredient_images`.`id` = '6'
Why?
I have create a table ingredient_images where I have the column filename.
This is my Model:
<?php
class IngredientImage extends AppModel {
public $name = 'IngredientImage';
public $useTable = 'ingredient_images';
public $actsAs = array (
'Uploader.Attachment' => array (
'filename' => array(
'name' => 'setNameAsImgId', // Name of the function to use to format filenames
'saveAsFilename' => true,
// 'baseDir' => '', // See UploaderComponent::$baseDir
'uploadDir' => '/files/ingredient_images/', // See UploaderComponent::$uploadDir
'dbColumn' => 'filename', // The database column name to save the path to
'defaultPath' => 'default.png', // Default file path if no upload present
'maxNameLength' => 20, // Max file name length
'overwrite' => true, // Overwrite file with same name if it exists - Se si effettua un transform è da usare al suo interno altrimenti c'è un override a false
'stopSave' => true, // Stop the model save() if upload fails
'allowEmpty' => true, // Allow an empty file upload to continue
'transforms' => array (
array('method' => 'resize', 'width' => 160, 'height' => 160, 'dbColumn' => 'filename', 'append' => false, 'overwrite' => true)
)
)
),
'Uploader.FileValidation' => array (
'filename' => array (
'maxWidth' => array (
'value' => 1280,
'error' => 'La lunghezza dell\'avatar non deve superare i 1280 pixel'
),
'maxHeight' => array (
'value' => 1280,
'error' => 'L\'altezza dell\'avatar non deve superare i 1280 pixel'
),
'extension' => array (
'value' => array('gif', 'jpg', 'png', 'jpeg'),
'error' => 'Il formato dell\'avatar deve essere una GIF, JPG o PNG'
),
'filesize' => array (
'value' => 5242880,
'error' => 'La dimensione dell\'avatar non deve superare i 500kB'
)
)
)
);
public $belongsTo = array(
'Ingredient' => array(
'className' => 'Ingredient',
'foreignKey' => 'ingredient_id',
'conditions' => '',
'order' => ''
)
);
public function setNameAsImgId ($name, $field, $file) {
/**
* Format the filename a specific way before uploading and attaching.
*
* #access public
* #param string $name - The current filename without extension
* #param string $field - The form field name
* #param array $file - The $_FILES data
* #return string
*/
// devo ricavare l'id dell'immagine appena creata per rinominare il file
return $this->id;
}
}
?>
This is my controller:
CakePlugin::load('Uploader');
App::import('Vendor', 'Uploader.Uploader');
//.. my action
$this->request->data['IngredientImage']['ingredient_id'] = $this->Ingredient->id;
$this->Ingredient->IngredientImage->save($this->request->data
//..
This is my view:
echo $this->Form->create('Ingredient', array ('class' => 'form', 'type' => 'file'));
echo $this->Form->input('IngredientImage.id', array ('type'=>'hidden', 'value'=> $ingredient[0]['IngredientImage']['id'],'label'=> false, 'id' => 'IngredientImage.id'));
echo $this->Form->input('IngredientImage.filename', array('type' => 'file'));
echo $this->Form->submit('Modifica', array('id'=>'edit'));
echo $this->Form->end();
Help me please
Check what it prints with pr($this->request->data); You will find an array for the filename index. Let's suppose you are getting the filename at $this->request->data['IngredientImage'][filename']['actual_filename']. Then you can manipulate it using:
CakePlugin::load('Uploader');
App::import('Vendor', 'Uploader.Uploader');
//.. my action
$this->request->data['IngredientImage']['ingredient_id'] = $this->Ingredient->id;
$this->request->data['IngredientImage']['filename'] = $this->request->data['IngredientImage']['filename']['actual_filename'];
$this->Ingredient->IngredientImage->save($this->request->data)
//..
Check the permissions on the folder 'uploadDir' => '/files/ingredient_images/'
That was the problem on my set up
Hope it hepls
For some reason my allowed_values_function never gets called when showing a field on a user bundle. Code:
function get_business_units()
{
$options = entity_load('business_unit', FALSE, NULL, FALSE);
$opt = bu_to_list_values($options);
return $opt;
}
function MYMODULE_enable()
{
if (!field_info_field('field_user_business_unit')) {
$field = array(
'field_name' => 'field_user_business_unit',
'type' => 'text',
'settings' => array(
'allowed_values' => array(),
'allowed_values_function' => 'get_business_units',
)
);
field_create_field($field);
// Create the instance on the bundle.
$instance = array(
'field_name' => 'field_user_business_unit',
'entity_type' => 'user',
'label' => 'Business Unit',
'bundle' => 'user',
'required' => FALSE,
'settings' => array(
'user_register_form' => 1,
),
'widget' => array(
'type' => 'options_select',
),
);
field_create_instance($instance);
}
}
The field is created, and even displayed on the users "edit" page when editing their info. But the only value is "Select" or "None". My method is never called (I even placed a debug point). This is all in MYMODULE.install file.
The problem is: 'type' => 'text'.
You have to use: 'type' => 'list_text'.
Allowed values is meaningless for a text type.
Your get_business_units() function needs to be in the MYMODULE.module file; the .install files aren't included in a normal Drupal bootstrap.
Have you tried
drush features-revert MYMODULE ?
I am currently trying to use Miles J plugin located here http://milesj.me/code/cakephp/uploader Although I have made great progress learning CakePhp, I am currently having a problem using the plugin and I would appreciate any help provided.
I have followed all the necessary steps to use the plugin. It has been downloaded put on Plugin folder, I bootstrapped with CakePlugin::loadAll().
So far so good.
Next I have proceed to set up the table as indicated by the plugin developer.
Ok, now back to my own code. I have the following set up:
images_controller.php , image.php and their views.
My goal now is to use the plugin inside those files as such:
App::import('Vendor', 'Uploader.Uploader');
Class ImagesController extends AppController {
var $components = array('Auth');
var $helpers = array('Design');
var $uses = array('Image', 'Uploader.Upload');
function manage(){
//here I show a simple upload form that uses the action saveimage
}
function saveimage(){
$this->Uploader = new Uploader();
if(!empty($this->data)){
$this->Upload->save($this->data);
}
}
}
Now, my model is set as follows
Class Image extends AppModel {
public $useTable = 'uploads';
public $actsAs = array(
'Uploader.FileValidation' => array(
'file' => array(
'extension' => array(
'value' => array('gif', 'jpg', 'jpeg'),
'error' => 'Only gif, jpg and jpeg images are allowed!'
),
'minWidth' => 500,
'minHeight' => 500,
'required' => true
),
'import' => array(
'required' => false
)
),
'Uploader.Attachment' => array(
'file' => array(
'name' => 'uploaderFilename',
'uploadDir' => '/files/uploads/',
'dbColumn' => 'path',
'maxNameLength' => 30,
'overwrite' => true,
'stopSave' => false,
'transforms' => array(
// Save additional images in the databases after transforming
array(
'method' => 'resize',
'width' => 100,
'height' => 100,
'dbColumn' => 'path_alt'
)
),
'metaColumns' => array(
'size' => 'filesize', // The size value will be saved to the filesize column
'type' => 'type' // And the same for the mimetype
)
),
'import' => array(
'uploadDir' => '/files/uploads/',
'name' => 'uploaderFilename',
'dbColumn' => 'path',
'overwrite' => true,
'stopSave' => false,
'transforms' => array(
array(
'method' => 'scale',
'percent' => .5,
'dbColumn' => 'path' // Overwrite the original image
)
)
)
)
);
}
}
On my model for testing purposes I have not changed anything but just copied and pasted the very same array as shown in the the Test/Model folder inside the plugin, which is meant to show the functionality of the plugin.
The following confusion, errors or lack of understanding is taking place:
My file is not being uploaded to the webroot/files/uploads folder
My data is being inserted in the database, but not in a complete manner by leaving empty as shown:
id | caption | path | path_alt | created |
4 | | | |2012:02:..|
Above, I expect the path to be saved, but it doesn't.
I have to admit my confusion comes mainly from my inexperience using plugins, so I am aware I might be doing something wrong regarding my models or my configuration.
Any prompt help would be appreciated greatly as I have tried to work this out on my own without any success.
A few things:
1 - In your controller you do not need to import the Uploader class. You also don't need to use the Uploader.Upload model (it's merely an example test case). All you need to do in the controller is call $this->Image->save() which will upload the file and save the path as a row into the database (if you defined the Attachment in Image).
2 - In your view, create the file input. Pay attention to the input name.
echo $this->Form->input('FILE_INPUT_NAME', array('type' => 'file'));
3 - In your Image model, setup the AttachmentBehavior and its options for that specific input field:
'Uploader.Attachment' => array(
'FILE_INPUT_NAME' => array(
'uploadDir' => '/files/uploads/',
'dbColumn' => 'path'
),
'ANOTHER_INPUT' => array()
);
Be sure that the column "path" exists in your images table. And thats it.
For more information on what each option does, check out the following: http://milesj.me/code/cakephp/uploader#uploading-files-through-the-model