Using the following code:
foreach ($form_state['values']['uploads'] as $key => $value) {
if (strlen($value)) {
$file = file_save_upload($key, array(
'file_validate_is_image' => array(), // Validates file is really an image.
'file_validate_extensions' => array('png gif jpg jpeg'), // Validate extensions.
));
// If the file passed validation:
if ($file) {
// Move the file, into the Drupal file system
if ($file = file_move($file, 'public://')) {
// Save the file for use in the submit handler.
$form_state['values']['uploaded_photos'][] = $file;
$x++;
} else {
form_set_error($key, t('Failed to write the uploaded file the site\'s file folder.'));
}
}
}
}
I am trying to save images attached to a form into a cached object with CTools. All of that was working fine, until I tried uploading the same file again, and I got a white screen that said "Error - The website encountered an error." (ripped from watchdog):
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'public://ad-10.jpg'; for key 2: UPDATE {file_managed} SET uid=:db_update_placeholder_0, filename=:db_update_placeholder_1, uri=:db_update_placeholder_2, filemime=:db_update_placeholder_3, filesize=:db_update_placeholder_4, status=:db_update_placeholder_5, timestamp=:db_update_placeholder_6
WHERE (fid = :db_condition_placeholder_0) ; Array
(
[:db_update_placeholder_0] => 0
[:db_update_placeholder_1] => ad-10.jpg
[:db_update_placeholder_2] => public://ad-10.jpg
[:db_update_placeholder_3] => image/jpeg
[:db_update_placeholder_4] => 4912
[:db_update_placeholder_5] => 0
[:db_update_placeholder_6] => 1326221376
[:db_condition_placeholder_0] => 834
)
";s:9:"%function";s:21:"drupal_write_record()";
Since I am not setting the $replace argument, shouldn't it default to FILE_EXISTS_RENAME and therefor not throw this error? How can I resolve this?
Try this module. It fixes the issue of concurrent file object being written to db. https://drupal.org/project/upload_qc
I can't comment as to why that doesn't work (it looks like it certainly should) but I can offer a simpler solution.
Drupal actually has a form widget built-in to handle file uploads, the managed_file type. It handles all of the file uploading/validating for you, you just need to mark the files as permanent in your form's submit handler.
So in your form function:
$form['files'] = array('#tree' => TRUE);
for ($i = 0; $i < $num_file_fields; $i++) {
$form['files']["file_$i"] = array(
'#type' => 'managed_file',
'#title' => 'Select a file',
'#upload_location' => 'public://',
'#upload_validators' => array(
'file_validate_is_image' => array(),
'file_validate_extensions' => array('png gif jpg jpeg')
)
);
}
And then in your submit handler:
$count = count($form_state['values']['files']);
for ($i = 0; $i < $count; $i++) {
$file = file_load($form_state['values']['files']["file_$i"]);
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
}
Hope that helps
I had the same issue because of strange unused temporary files.
You can check if you have unused files using:
$result = db_select('file_managed', 'f')
->fields('f', array('fid'))
->condition('f.uri', db_like('temporary://') . '%', 'LIKE')
->execute()->fetchCol();
foreach ($result as $fid) {
if ($file = file_load($fid)) {
if (!file_usage_list($file)) {
dpm($file->filename);
}
}
}
If you fail to upload file with the same name that you see, you can remove them(But double check and backup first):
/**
* Clean unused temporary files.
*/
function MYMODULE_update_7007() {
$files = db_select('file_managed', 'f')
->fields('f', array('fid'))
->condition('f.uri', db_like('temporary://') . '%', 'LIKE')
->execute()->fetchCol();
foreach ($files as $fid) {
if ($file = file_load($fid)) {
$references = file_usage_list($file);
if (empty($references)) {
if (!file_delete($file)) {
watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->uri), WATCHDOG_ERROR);
}
}
else {
watchdog('file system', 'Did not delete temporary file "%path" during garbage collection, because it is in use by the following modules: %modules.', array('%path' => $file->uri, '%modules' => implode(', ', array_keys($references))), WATCHDOG_INFO);
}
}
}
}
Related
Situation
I have a custom module that is using hook_field_formatter_info() to add an "fancy" option to the image field in the manage display of a content type. When this option is chosen I want to be able to surround the ENTIRE field in custom divs and markup. I want the solution to be a hook in my custom module and not a template override.
Process
A user wants the display of an image field to be the "fancy" option. They check it in the drop down and click save from the content types Manage Display admin page. Now on that content type node there should be custom markup surrounding the entire field even if there are multiple images, the new markup should surround ALL the images and not each individual image.
hook_field_formatter_view
hook_field_formatter_view seems to demand it's output be an array and I can't seem to be able to wrap the output in a div.
Template overrides
I can't just make a field.tpl.php for the specific field because like I initially mentioned I need to be able to switch themes and the module still work like normal. Unless there is a way to get my module to override any field where said field has hook_field_formatter_info() set specifically.
/**
* Implements hook_field_formatter_view().
*/
function bootstrap_modal_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
foreach ($items as $delta => $item) {
if ($index === NULL || $index === $delta) {
$element[$delta] = array(
'#theme' => 'bootstrap_modal_image_formatter',
'#item' => $item,
'#entity_type' => $entity_type,
'#entity' => $entity,
'#node' => $entity, // Left for legacy support.
'#field' => $field,
'#display_settings' => $display['settings'],
'#delta' => $delta,
);
}
}
// $element = '<div id="CUSTOMDIVSTUFF">' . $element . '</div>';
return $element;
}
And here is the #theme function:
function theme_bootstrap_modal_image_formatter($variables) {
$item = $variables['item'];
$entity_type = $variables['entity_type'];
$entity = $variables['entity'];
$field = $variables['field'];
$settings = $variables['display_settings'];
$image = array(
'path' => $item['uri'],
'alt' => isset($item['alt']) ? $item['alt'] : '',
'title' => isset($item['title']) ? $item['title'] : '',
'style_name' => $settings['bootstrap_modal_node_style'],
);
if (isset($item['width']) && isset($item['height'])) {
$image['width'] = $item['width'];
$image['height'] = $item['height'];
}
if (isset($item['attributes'])) {
$image['attributes'] = $item['attributes'];
}
// Allow image attributes to be overridden.
if (isset($variables['item']['override']['attributes'])) {
foreach (array('width', 'height', 'alt', 'title') as $key) {
if (isset($variables['item']['override']['attributes'][$key])) {
$image[$key] = $variables['item']['override']['attributes'][$key];
unset($variables['item']['override']['attributes'][$key]);
}
}
if (isset($image['attributes'])) {
$image['attributes'] = $variables['item']['override']['attributes'] + $image['attributes'];
}
else {
$image['attributes'] = $variables['item']['override']['attributes'];
}
}
$entity_title = entity_label($entity_type, $entity);
if ($style_name = $settings['bootstrap_modal_image_style']) {
$path = image_style_url($style_name, $image['path']);
}
else {
$path = file_create_url($image['path']);
}
$caption = 'some value';
$gallery_id = 'some value';
return theme('bootstrap_modal_imagefield', array('image' => $image, 'path' => $path, 'title' => $caption, 'gid' => $gallery_id));
}
Been working on this for days, I'm drowning here.
This is possible via HOOK_process_field(). Use this hook to define a new template file for your field by adding it to theme_hook_suggestions array (place the template file in your module directory, because you wanted to change the theme -as you mentioned-). More info about field template suggestions.
Then, you will need to add the module path to drupal theme registry, so it picks up the template file. Demo.
I found out it was:
function bootstrap_modal_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array(
'#prefix' => '<div class="wrapper">',
'#suffix' => '</div>',
);
I have form with file upload element. This file (image) must be user picture, but it not set.
form:
$form['f']['step4']['file_e'] = array(
'#title' => t('Image'),
'#type' => 'managed_file',
'#description' => t('The uploaded image will be displayed on this page using the image style choosen below.'),
'#upload_location' => 'public://images/',
);
validate
function fill_up_profile_form_validate($form, &$form_state) {
global $user;
if (isset($form['f']['actions']['submit']['#value']) && $form_state['triggering_element']['#value'] == $form['f']['actions']['submit']['#value']) {
$file = file_load($form_state['values']['f']['step4']['file_e']);
$validators = array(
'file_validate_is_image' => array(),
'file_validate_size' => array(1 * 1024 * 1024),
);
$errors = file_validate($file, $validators);
if (!empty($errors)) {
$message = '';
foreach ($errors as $error)
$message .= $error . '<br/>';
form_set_error('Error!', $message);
} else {
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
$edit['picture'] = $file;
user_save($user, $edit);
}
}
}
Thank you for your help.
You should try with $form_state instead of $form. $form_state contains the submitted values! Also, with Drupal 7.x, you can not get the values by simply using $form_state['values']['f']['step4']['file_e']! Just do a echo "<pre>"; print_R($form_state); echo "</pre>"; to learn the correct structure of the array!
Also have a look at this code: http://ly2.in/hBMxPK
How can I process a file upload in a module configuration section? Here is what I have so far.
<?php
function dc_staff_directory_admin_settings()
{
$form['dc_staff_directory_upload_file'] = array(
'#type' => 'file',
'#title' => t('Upload staff directory excel (.xls) file'),
'#description' => t('Uploading a file will replace the current staff directory'),
);
$form['#submit'][] = 'dc_staff_directory_process_uploaded_file';
return system_settings_form($form);
}
function dc_staff_directory_process_uploaded_file($form, &$form_state)
{
//What can I do here to get the file data?
}
If you use the managed_file type instead Drupal will do most of the processing for you, you just need to mark the file for permanent storage in your submit function:
function dc_staff_directory_admin_settings() {
$form['dc_staff_directory_upload_file'] = array(
'#type' => 'managed_file',
'#title' => t('Upload staff directory excel (.xls) file'),
'#description' => t('Uploading a file will replace the current staff directory'),
'#upload_location' => 'public://path/'
);
$form['#submit'][] = 'dc_staff_directory_process_uploaded_file';
$form['#validate'][] = 'dc_staff_directory_validate_uploaded_file';
return system_settings_form($form);
}
function db_staff_directory_validate_uploaded_file($form, &$form_state) {
if (!isset($form_state['values']['dc_staff_directory_upload_file']) || !is_numeric($form_state['values']['dc_staff_directory_upload_file'])) {
form_set_error('dc_staff_directory_upload_file', t('Please select an file to upload.'));
}
}
function dc_staff_directory_process_uploaded_file($form, &$form_state) {
if ($form_state['values']['dc_staff_directory_upload_file'] != 0) {
// The new file's status is set to 0 or temporary and in order to ensure
// that the file is not removed after 6 hours we need to change it's status
// to 1.
$file = file_load($form_state['values']['dc_staff_directory_upload_file']);
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
}
}
The validate function is probably a good idea as well, obviously you won't need it if the file is not a required field.
This is mostly taken from the image_example module, part of the Examples Module. If you really don't want to use the managed_file type have a look at the file_example module in that same collection, it has examples of how to uploaded an unmanaged file.
Hope that helps
I am trying to create a node based on a custom form submission. Everything works great except for the images that get uploaded.
I can capture them fine and set them in the form object cache. When I pass the data into the function to create the node, I get this error:
"The specified file could not be copied, because no file by that name exists. Please check that you supplied the correct filename."
I also receive the error multiple times, despite only submitting one or two images at a time.
Here is the code I am using. $uploads is passed in and is an array of file objects returned from file_save_upload() in a previous step:
if (isset($uploads)) {
foreach ($uploads as $upload) {
if (isset($upload)) {
$file = new stdClass;
$file->uid = 1;
$file->uri = $upload->filepath;
$file->filemime = file_get_mimetype($upload->uri);
$file->status = 1;
$file = file_copy($file, 'public://images');
$node->field_image[$node->language][] = (array) $file;
}
}
}
node_save($node);
I also tried this:
if (isset($uploads)) {
foreach ($uploads as $upload) {
$upload->status = 1;
file_save($upload);
$node->field_image[$node->language][] = (array) $upload;
}
}
}
node_save($node);
The second causes a duplicate key error in MySQL on the URI field. Both of these examples I saw in tutorials, but neither are working?
For Drupal 7, I played around with this quite a bit and found the best way (and only way that I've got working) was to use Entity metadata wrappers
I used a managed file form element like so:
// Add file upload widget
// Use the #managed_file FAPI element to upload a document.
$form['response_document'] = array(
'#title' => t('Attach a response document'),
'#type' => 'managed_file',
'#description' => t('Please use the Choose file button to attach a response document<br><strong>Allowed extensions: pdf doc docx</strong>.'),
'#upload_validators' => array('file_validate_extensions' => array('pdf doc docx')),
'#upload_location' => 'public://my_destination/response_documents/',
);
I also pass along the $node object in my form as a value
$form['node'] = array('#type' => 'value', '#value' => $node);
Then in my submission handler I simply do the following:
$values = $form_state['values'];
$node = $values['node'];
// Load the file and save it as a permanent file, attach it to our $node.
$file = file_load($values['response_document']);
if ($file) {
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
// Attach the file to the node.
$wrapper = entity_metadata_wrapper('node', $node);
$wrapper->field_response_files[] = array(
'fid' => $file->fid,
'display' => TRUE,
'description' => $file->filename,
);
node_save($node);
}
i used your code to upload a file in the file field to a content("document" in my case) and it's worked. Just had to add a value for field_document_file 'display' in the code.
here is the exact script i used:
<?php
// Bootstrap Drupal
define('DRUPAL_ROOT', getcwd());
require_once './includes/bootstrap.inc';
require_once './includes/file.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Construct the new node object.
$path = 'Documents/document1.doc';
$filetitle = 'test';
$filename = 'document1.doc';
$node = new StdClass();
$file_temp = file_get_contents($path);
//Saves a file to the specified destination and creates a database entry.
$file_temp = file_save_data($file_temp, 'public://' . $filename, FILE_EXISTS_RENAME);
$node->title = $filetitle;
$node->body[LANGUAGE_NONE][0]['value'] = "The body of test upload document.\n\nAdditional Information";
$node->uid = 1;
$node->status = 1;
$node->type = 'document';
$node->language = 'und';
$node->field_document_files = array(
'und' => array(
0 => array(
'fid' => $file_temp->fid,
'filename' => $file_temp->filename,
'filemime' => $file_temp->filemime,
'uid' => 1,
'uri' => $file_temp->uri,
'status' => 1,
'display' => 1
)
)
);
$node->field_taxonomy = array('und' => array(
0 => array(
'tid' => 76
)
));
node_save($node);
?>
Kevin, that's what I found in the Drupal doc's under http://drupal.org/node/201594 below in the comments. But I am not sure at all. I try the same, so please let me know what you found out.
$path = './sites/default/files/test.jpg';
$filetitle = 'test';
$filename = 'test.jpg';
$node = new StdClass();
$file_temp = file_get_contents($path);
$file_temp = file_save_data($file_temp, 'public://' . $filename, FILE_EXISTS_RENAME);
$node->title = $filetitle;
$node->uid = 1;
$node->status = 1;
$node->type = '[content_type]';
$node->language = 'und';
$node->field_images = array(
'und' => array(
0 => array(
'fid' => $file_temp->fid,
'filename' => $file_temp->filename,
'filemime' => $file_temp->filemime,
'uid' => 1,
'uri' => $file_temp->uri,
'status' => 1
)
)
);
$node->field_taxonomy = array('und' => array(
0 => array(
'tid' => 76
)
));
node_save($node);
I have a piece of code on 1 drupal site to create a node another drupal site in a multi-site setup.
It looks like I'm getting the sessionid and logging in just fine, but when trying to create a "page" node, I get "Access denied". Under Services -> Settings I have "Key Authentication", "Use keys" is unchecked, and "Use sessid" is checked. I agev permissions for the logged in user: "create page content", "administer services", etc...
Below is my code:
<p>Test Page 1</p>
<? $url = 'http://drupal2.dev/xmlrpc.php'; ?>
<?
$conn = xmlrpc($url, 'system.connect');
print_r($conn);
?>
<p>--</p>
<?
$login = xmlrpc($url, 'user.login', $conn['sessid'], 'superuser_name', 'superuser_password');
print_r($login);
?>
<p>--</p>
<?
$data=array('type'=>'page', 'title'=>'Test', 'body'=>'test');
$data_s=serialize($data);
$result = xmlrpc($url, 'node.save', $login['sessid'], $data_s);
echo $result;
//echo $data_s;
?>
<?
if($error = xmlrpc_error()){
if($error->code > 0){
$error->message = t('Outgoing HTTP request failed because the socket could not be opened.');
}
drupal_set_message(t('Operation failed because the remote site gave an error: %message (#code).',
array(
'%message' => $error->message,
'#code' => $error->code
)
)
);
}
?>
The ouput of this script is:
Array ( [sessid] => 9eebdde9bf0bfd9610cc2f03af131a9c [user] => Array ( [uid] => 0 [hostname] => ::1 [roles] => Array ( [1] => anonymous user ) [session] => [cache] => 0 ) )
--
Array ( [sessid] => c0ca4c599e41e97e7a7ceb43ee43249e [user] => Array ( [uid] => 1 [name] => eric [pass] => 13583b155536098b98df41bb69fcc53 [mail] => email#gmail.com [mode] => 0 [sort] => 0 [threshold] => 0 [theme] => [signature] => [signature_format] => 0 [created] => 1271813934 [access] => 1275867734 [login] => 1275868794 [status] => 1 [timezone] => [language] => [picture] => [init] => email#gmail.com [data] => a:0:{} [roles] => Array ( [2] => authenticated user ) ) )
--
Access denied
I discovered recently that PHP session ids are more complex than I had thought.
For them to work, your XMLRPC transport needs to fully support cookies, which are used for Drupal's authentication.
Without cookies, each request is treated as a new anonymous request and is given a new session ID. So the fact that you've logged in means nothing to the next xmlrpc call you make.
I'm doing some work in python, and made a custom transport object to support cookies, and now it all works for me. I found out how to do this in python here:
http://osdir.com/ml/python.cherrypy/2005-12/msg00142.html
(edit-add) I might also add that the services module is pretty bad with its error reporting. For example, if you send an argument as a string when it's expecting an array (with the string in the array) you can often get access denied errors which don't really reflect the fact that there is a parameter error.
Check that the service is working as you expect by testing it out under Admin > Site Building > Services > Browse and click the service you want to use.
site 1 code:
function exmple2_cron() {
homes_sync_get_node_list();
}
function homes_sync_get_node_list() {
$methods = xmlrpc('http://example.com/map/xmlrpc.php', array('system.listMethods' => array()));
$node_ids = xmlrpc('http://example.com/map/xmlrpc.php', array('node.getAllHomes'=>array()));
if (xmlrpc_error()) {
$error = xmlrpc_error();
watchdog('homes_sync', 'Error getting node list from parent server. Error: #error.', array('#error' => $error);
}
else {
foreach ($node_ids as $nid) {
$nodes[] = $nid;
}
variable_set('parent_home_nodes', $nodes);
watchdog('homes_sync', 'Successfully retrieved node list from parent server.', array(), WATCHDOG_NOTICE);
}
homes_sync_perform_update();
}
function homes_sync_perform_update() {
$node_ids = variable_get('parent_home_nodes', 0);
foreach ($node_ids as $nid) {
$data = xmlrpc('http://example.com/map/xmlrpc.php', array('node.get' => array($nid)));print_r($data);exit;
$result = db_fetch_array(db_query('SELECT n.nid, n.title, n.type FROM {node} n WHERE n.title = "%s" AND n.type = "%s"', $data['title'], 'page'));
if (xmlrpc_error()) {
$error = xmlrpc_error();
watchdog('homes_sync', 'Could not perform XMLRPC request. Error: #error.', array('#error' => $error), WATCHDOG_CRITICAL);
} else {
if (is_array($data)) {
$node = "";
if ($result && $result['nid']) {
$node->nid = $result['nid'];
}
$node->type = $data['type'];
$node->uid = 1;
$node->status = $data['status'];
$node->created = $data['created'];
$node->changed = $data['changed'];
$node->comment = $data['comment'];
$node->promote = $data['promote'];
$node->moderate = $data['moderate'];
$node->sticky = $data['sticky'];
$node->tnid = $data['tnid'];
$node->translate = $data['translate'];
$node->title = $data['title'];
$node->body = $data['body'];
$node->teaser = $data['teaser'];
$node->format = $data['format'];
$node->name = $data['name'];
$node->data = $data['data'];
$node->path = $data['path'];
node_save($node);
unset($node);
}
}
}
}
remote site code:
function example_xmlrpc() {
$methods = array();
$methods[] = array(
'node.getAllHomes',
'homes_service_node_get_all_homes',
array('int'),
);
return $methods;
}
function homes_service_node_get_all_homes() {
$query = db_query('SELECT n.* FROM {node} n');
foreach ($query as $record){
$nid[] = $record;
}
return $nid;
}