I would like to call a Ruby script when a user uploads an image to my Drupal content-type. I have a CCK image field that serves as the main image, and ImageCache takes care of resizing and creating thumbnails for me.
I have a Ruby script that does some transformations to the image, however I don't exactly know how to call it (no experience with Ruby really). Basically the script applies some image transforms and then generates a new image.
My question is how to call this script from Drupal...is there some sort of hook regarding the CCK image upload that I would hijack?
ImageField uses the same API as FileField. Therefore, you could add a custom validator for your upload field, which would do some checks on the image (like calling a ruby script).
I described that some time ago on Drupal.org, see: http://drupal.org/node/546146
However, for your convenience, here the code.
First, define a validator for the upload form:
function example_form_alter(&$form, $form_state, $form_id) {
if ($form_id != 'ID_OF_YOUR_FORM')
return;
$form['FIELDNAME'][0]['#upload_validators']['example_FUNCTIONNAME'] = array();
return $form;
}
Second, implement the validator function:
function example_FUNCTIONNAME($field) {
// variable for error messages
$errors = array();
// do some processing on the field
$filepath = $field->filepath;
...
// in case of error, add error message
$errors[] = t('Validation failed, because...');
return $errors;
}
Put this code in a custom module, but make sure that your module is called after FileField and ImageField (adjust weight in system table).
Update: There is another way to hook into it. You can use hook_nodeapi to react on operation "presave". This also allows you to call a script on the content of the uploaded field.
Related
I have created normal Symfony form. However I require additional files to be uploaded (photos, certificates) which I handle by AJAX and are not retrieved via Symfony form.
Instead file upload is handled in separate controllers actions via file bag.
I would like to limit the maximum file size (only for this action).
If the upload was handled by classic form I would use the file constraint
but this way I do not now how to apply it.
I check mime-type and successful upload by methods on UploadedFile, however the documentation for method for retrieving the size is in a way discouraging (UploadedFile#method_getClientSize).
Is there a way how to use FileValidator/File constraint without the form?
I have got inspired by actual implementation of FileValidator:
$file = $request->files->get('file');
$path = $file->getPathname();
$size = round(filesize($path) / 1000000, 2);
$limit = (int) $constraint->maxSize;
if ($size > $limit) {
//Add violation
}
This solution works and is probably better then use getClientSize().
Problem
When I try to add a block into my transactional email template in the following manner:
{{block type='core/template' area='frontend' template='invent/baskettimer/email_items.phtml' record=$record}}
I get the following error, and nothing is rendered.
CRIT (2): Not valid template file:frontend/base/default/template/invent/baskettimer/email_items.phtml
Troubleshooting
Normally this warning points to a typo which is breaking the inheritance but I have quadruple checked and this should work.
I then copied the file into the base and did a test, it rendered correctly.
Create a custom block and set the template, same error is displayed.
Theory
To me it seems template inheritance is broken / not implemented for emails, so it is always looking in base, I cannot put my templates there so I am not sure how to call them.
Possible workarounds
Render the block to html then send it to as a variable to render, problem with this is I am sending the emails from Model level and am having a hard time pre rendering the block, even with a helper.
Render the data using a method, don't really want to do this as it is message / against MVC.
Any help is much appreciated.
Bounty update
So I have traced down the problem, it is probably an easy solution now.
The problem is that I am calling it from a cronjob does not have the correct store view, it is fairly easy to replicate similar situation by using a shell script, then changing the _appCode to null.
<?php
require_once 'abstract.php';
class Mage_Shell_Shell extends Mage_Shell_Abstract
{
protected $_appCode = ''; // works - remove to not work
/**
* Run script
*
*/
public function run()
{
Mage::getModel('invent_baskettimer/email')->sendJob();
}
}
$shell = new Mage_Shell_Shell();
$shell->run();
So basically the question has become:
How do I call a block->toHtml() regardless of store view?
There is not way of setting a cronjob to be like that. Lucky magento lets you emulate your store views, see the following to emulate the default store.
public function cronjob()
{
$iDefaultStoreId = Mage::app()
->getWebsite()
->getDefaultGroup()
->getDefaultStoreId();
$appEmulation = Mage::getSingleton('core/app_emulation');
$initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($iDefaultStoreId);
.. do your stuff here ..
$appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
}
For more info see: http://inchoo.net/ecommerce/magento/emulate-store-in-magento/
Although i can't find a title to fit this need but here is the problem.
I have an existing module called Files. when i use this module some javascript runs and creates thumbs for different kind of files. It is run on windown load.
Now i am developing a module called File Links.
In this module i need to create thumbs on windows load.
I have copied the code from Files module.
Here is some code
public function folder_contents()
{
$parent = $this->input->post('parent');
echo json_encode(Files::folder_contents($parent));
}
This takes the parent parameter coming from ajax request and gets the folder contents.
I want to display all files. For this i have created another method of model that fetches all files.
Now the problem is that while being in File Link Module on load ajax request runs and
hits the method above.
Here is what i have tried
public function folder_contents()
{
$parent = $this->input->post('parent');
$modules = $this->router->fetch_module();
if($modules == 'files'){
echo json_encode(Files::folder_contents($parent));
}else{
echo json_encode(Files::getAllFiles());
}
}
In this method i am checking module name if it is not files then get all files but when ever this method is called module is always files. This method resides in Files Controller. And other thing is that i dont want to write ajax request to point some where else because it is pre-maid and works great.
Now how can i set some condition which will let getAllFiles() run.
The reason why the module always shows as 'files' is because the ajax request does call the Files module regardless of which module triggered the ajax request. It is a separate request.
To get this working you can use the $parent value to determine whether or not you want all files or just the ones with a certain parent folder returned. If you remove the POST value from your custom ajax call then folder_contents() will return all files as shown below.
public function folder_contents()
{
$parent = $this->input->post('parent');
if($parent){
echo json_encode(Files::folder_contents($parent));
}else{
echo json_encode(Files::getAllFiles());
}
}
Since the default ajax call POSTs the folder id it will still return only the files from that folder.
Here is my BrandController.php
https://gist.github.com/a958926883b9e7cc68f7#file-brandcontroller-php-L53
I've gone through all my files of my custom module, and compared them to the one given from the custom module maker, and I couldn't find much differences.
Are you attempting to upload multiple files? If you're using multiple fileupload elements with the same name you'll get an array of items.
So when the following line is called,
//this way the name is saved in DB
$data['filename'] = $_FILES['filename']['name'];
It will have the value
["name"]=>array(2) {
[0]=>string(9)"file0.txt"
[1]=>string(9)"file1.txt"
}
you'll need to update the code to loop through each $_FILES['filename']['name'] and upload and save the files separately.
You may unknowingly uploaded multiple files. If you that is not your intention, you may check your in your HTML and check the name attribute of the tag. It must not be an array (like this).
<input type="file" name="my_files[]" />
If you only see Array() in your database, it means you are indeed uploading a multiple files. You can process them by using loops.
If you are really sure that you are uploading 1 image, you may follow #Palanikumar's suggestion. Use a print_r() and display the $_FILES and paste it here. IF you don't want to use that, You can use
json_encode($the-data-you-are-going-to-insert-to-the-database);
If you don't know where to put the print_r() function, you may put it after line 56 of this file.
https://gist.github.com/desbest/a958926883b9e7cc68f7#file-brandcontroller-php-L53
if(isset($_FILES['filename']['name']) && $_FILES['filename']['name'] != '') {
print_r($_FILES);
die;
If saveAction() is being called inside an ajax function you need to log the ajax response. Assuming you are using jquery..
$ajaxResponse = $.POST({...});
console.log($ajaxResponse.responseText);
Then, you you can view it inside a browser's console. If nothing appears, you may use a non-async request
$ajaxResponse = $.POST({
// your options,
// your another option,
async: FALSE
});
Usually file upload will return in array format. So that each uploaded file will have the information like name, type, size, temporary name, error. You can get the file information using print function (print_r($_FILES)). So if you want to display name of the file you have to use something like this $_FILES['filename']['name']
Use print function and debugging tool then save file information using loops.
For more info please check here.
You aren't setting the enctype of the form so the image will never be sent. updated the code to
$form = new Varien_Data_Form(array( 'enctype' => 'multipart/form-data'));
I have a script that has loaded a product using
$product = Mage::getModel("catalog/product")->loadByAttribute('sku', $data['id']);
If I modify or retrieve a system attribute, such as weight, everything works fine:
$product->getWeight(); // returns the correct weight
$product->setWeight(5.0); // correctly sets weight
I have two custom attributes: google_product_category and mpn. If I try to modify them using the same technique, like so:
$product->setMpn('123ABC');$product->setGoogleProductCategory('Electronics');the call does not throw an error but the data does not save (yes, I am calling $product->save() afterwards).
What am I doing wrong?
Can you post your scripts? Are you on developer mode?
It looks like the problem is because of store area
Take a look at Mage_Catalog_Model_Product -> setOrigData
public function setOrigData($key=null, $data=null)
{
if (Mage::app()->getStore()->isAdmin()) {
return parent::setOrigData($key, $data);
}
return $this;
}
When saving a product, it needs to compare the data with origdata.
It can retrieve origdata if it is run from 'admin' area. If your scripts run from 'default' area, it won't be able to get the origdata.
So that if you're on developer mode, in some point of code there gonna be a warning thrown that will broke your script (I may be wrong because you said no error was thrown)
You can do it by changing Mage::app('default') into Mage::app('admin')
or using Mage::app()->setCurrentStore('admin');