Skip to content
Snippets Groups Projects
Commit c54de861 authored by Dries Buytaert's avatar Dries Buytaert
Browse files

- Patch #42358 by dopry et al: fixed problem with attaching files.

parent 28c0c619
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
/** /**
* @file * @file
* File-handling and attaching files to nodes. * File-handling and attaching files to nodes.
*
*/ */
/** /**
...@@ -75,8 +76,8 @@ function upload_menu($may_cache) { ...@@ -75,8 +76,8 @@ function upload_menu($may_cache) {
} }
else { else {
// Add handlers for previewing new uploads. // Add handlers for previewing new uploads.
if ($_SESSION['file_uploads']) { if ($_SESSION['file_previews']) {
foreach ($_SESSION['file_uploads'] as $key => $file) { foreach ($_SESSION['file_previews'] as $fid => $file) {
$filename = file_create_filename($file->filename, file_create_path()); $filename = file_create_filename($file->filename, file_create_path());
$items[] = array( $items[] = array(
'path' => $filename, 'title' => t('file download'), 'path' => $filename, 'title' => t('file download'),
...@@ -84,7 +85,7 @@ function upload_menu($may_cache) { ...@@ -84,7 +85,7 @@ function upload_menu($may_cache) {
'access' => user_access('view uploaded files'), 'access' => user_access('view uploaded files'),
'type' => MENU_CALLBACK 'type' => MENU_CALLBACK
); );
$_SESSION['file_uploads'][$key]->_filename = $filename; $_SESSION['file_previews'][$fid]->_filename = $filename;
} }
} }
} }
...@@ -99,6 +100,12 @@ function upload_settings() { ...@@ -99,6 +100,12 @@ function upload_settings() {
'#size' => 15, '#maxlength' => 10, '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.') '#size' => 15, '#maxlength' => 10, '#description' => t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.')
); );
$form['settings_general']['upload_list_default'] = array('#type' => 'select', '#title' => t('List files by default'),
'#default_value' => variable_get('upload_list_default',1),
'#options' => array( 0 => t('No'), 1 => t('Yes') ),
'#description' => t('Set whether files attached to nodes are listed or not in the node view by default.'),
);
$roles = user_roles(0, 'upload files'); $roles = user_roles(0, 'upload files');
foreach ($roles as $rid => $role) { foreach ($roles as $rid => $role) {
...@@ -121,7 +128,7 @@ function upload_settings() { ...@@ -121,7 +128,7 @@ function upload_settings() {
} }
function upload_download() { function upload_download() {
foreach ($_SESSION['file_uploads'] as $file) { foreach ($_SESSION['file_previews'] as $file) {
if ($file->_filename == $_GET['q']) { if ($file->_filename == $_GET['q']) {
file_transfer($file->filepath, array('Content-Type: '. mime_header_encode($file->filemime), 'Content-Length: '. $file->filesize)); file_transfer($file->filepath, array('Content-Type: '. mime_header_encode($file->filemime), 'Content-Length: '. $file->filesize));
} }
...@@ -153,6 +160,61 @@ function upload_file_download($file) { ...@@ -153,6 +160,61 @@ function upload_file_download($file) {
} }
} }
/*
* Save new uploads and attach them to the node object.
* append file_previews to the node object as well.
*/
function _upload_prepare(&$node) {
// Clean up old file previews if a post didn't get the user to this page.
// i.e. the user left the edit page, because they didn't want to upload anything.
if(count($_POST) == 0) {
if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) {
foreach($_SESSION['file_previews'] as $fid => $file) {
file_delete($file->filepath);
}
unset($_SESSION['file_previews']);
}
}
// $_SESSION['file_submitted'] tracks the fid of the file submitted this page request.
// form_builder sets the value of file->list to 0 for checkboxes added to a form after
// it has been submitted. Since unchecked checkboxes have no return value and do not
// get a key in _POST form_builder has no way of knowing the difference between a check
// box that wasn't present on the last form build, and a checkbox that is unchecked.
unset($_SESSION['file_submitted']);
// Save new file uploads to tmp dir.
if (($file = file_check_upload()) && user_access('upload files')) {
global $user;
// Scale image uploads.
$file = _upload_image($file);
$key = 'upload_'. count($_SESSION['file_previews']);
$file->fid = $key;
$file->source = $key;
$file->list = variable_get('upload_list_default',1);
$_SESSION['file_previews'][$key] = $file;
// Store the uploaded fid for this page request in case of submit without
// preview or attach. See earlier notes.
$_SESSION['file_submitted'] = $key;
}
// Attach file previews to node object.
if (is_array($_SESSION['file_previews']) && count($_SESSION['file_previews'])) {
foreach($_SESSION['file_previews'] as $fid => $file) {
$node->files[$fid] = $file;
}
}
}
function upload_form_alter($form_id, &$form) { function upload_form_alter($form_id, &$form) {
if (isset($form['type'])) { if (isset($form['type'])) {
if ($form['type']['#value'] .'_node_settings' == $form_id) { if ($form['type']['#value'] .'_node_settings' == $form_id) {
...@@ -166,13 +228,7 @@ function upload_form_alter($form_id, &$form) { ...@@ -166,13 +228,7 @@ function upload_form_alter($form_id, &$form) {
if ($form['type']['#value'] .'_node_form' == $form_id && variable_get("upload_$node->type", TRUE) && user_access('upload files')) { if ($form['type']['#value'] .'_node_form' == $form_id && variable_get("upload_$node->type", TRUE) && user_access('upload files')) {
drupal_add_js('misc/progress.js'); drupal_add_js('misc/progress.js');
drupal_add_js('misc/upload.js'); drupal_add_js('misc/upload.js');
// Clears our files in session when you enter the edit view the first time.
// This is so files don't linger around if you happen to leave the node
// and come back into it.
if(count($_POST) == 0) {
unset($_SESSION['file_uploads']);
}
upload_nodeapi($node, 'validate', NULL);
$form['attachments'] = array( $form['attachments'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('File attachments'), '#title' => t('File attachments'),
...@@ -189,42 +245,28 @@ function upload_form_alter($form_id, &$form) { ...@@ -189,42 +245,28 @@ function upload_form_alter($form_id, &$form) {
} }
} }
/** function _upload_validate(&$node) {
* Implementation of hook_nodeapi(). // Accumulator for disk space quotas.
*/ $filesize = 0;
function upload_nodeapi(&$node, $op, $arg) {
switch ($op) {
case 'validate': // Check if node->files exists, and if it contains something.
$node->files = upload_load($node); if (count($node->files) && is_array($node->files)) {
// Double check existing files: // Update existing files with form data.
if (is_array($node->list)) { foreach($node->files as $fid => $file) {
foreach ($node->list as $key => $value) {
// Ensure file is valid and retrieve contents of $_FILES array
if ($file = file_check_upload($key)) {
$node->files[$file->source] = $file;
$node->files[$key]->list = $node->list[$key];
$node->files[$key]->remove = $node->remove[$key];
$node->files[$key]->description = $node->description[$key];
if ($file->source) {
$filesize += $file->filesize;
}
}
}
}
else {
foreach ($node->files as $key => $file) {
$node->list[$key] = $file->list;
}
}
if (($file = file_check_upload('upload')) && user_access('upload files')) { // Validate new uploads.
if (strpos($fid, 'upload') !== false && !$file->remove) {
global $user; global $user;
$file = _upload_image($file); // Bypass validation for uid = 1.
// Don't do any checks for uid #1.
if ($user->uid != 1) { if ($user->uid != 1) {
// Validate file against all users roles. Only denies an upload when //Update filesize accumulator.
// all roles prevent it. $filesize += $file->filesize;
// Validate file against all users roles.
// Only denies an upload when all roles prevent it.
$total_usersize = upload_space_used($user->uid) + $filesize; $total_usersize = upload_space_used($user->uid) + $filesize;
foreach ($user->roles as $rid => $name) { foreach ($user->roles as $rid => $name) {
$extensions = variable_get("upload_extensions_$rid", 'jpg jpeg gif png txt html doc xls pdf ppt pps'); $extensions = variable_get("upload_extensions_$rid", 'jpg jpeg gif png txt html doc xls pdf ppt pps');
...@@ -245,37 +287,41 @@ function upload_nodeapi(&$node, $op, $arg) { ...@@ -245,37 +287,41 @@ function upload_nodeapi(&$node, $op, $arg) {
$error['usersize']++; $error['usersize']++;
} }
} }
} if ($error['extension'] == count($user->roles) && $user->uid != 1) {
form_set_error('upload', t('The selected file %name can not be attached to this post, because it is only possible to attach files with the following extensions: %files-allowed.', array('%name' => theme('placeholder', $file->filename), '%files-allowed' => theme('placeholder', $extensions))));
if ($error['extension'] == count($user->roles) && $user->uid != 1) { }
form_set_error('upload', t('The selected file %name can not be attached to this post, because it is only possible to attach files with the following extensions: %files-allowed.', array('%name' => theme('placeholder', $file->filename), '%files-allowed' => theme('placeholder', $extensions)))); elseif ($error['uploadsize'] == count($user->roles) && $user->uid != 1) {
} form_set_error('upload', t('The selected file %name can not be attached to this post, because it exceeded the maximum filesize of %maxsize.', array('%name' => theme('placeholder', $file->filename), '%maxsize' => theme('placeholder', format_size($uploadsize)))));
elseif ($error['uploadsize'] == count($user->roles) && $user->uid != 1) { }
form_set_error('upload', t('The selected file %name can not be attached to this post, because it exceeded the maximum filesize of %maxsize.', array('%name' => theme('placeholder', $file->filename), '%maxsize' => theme('placeholder', format_size($uploadsize))))); elseif ($error['usersize'] == count($user->roles) && $user->uid != 1) {
} form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize)))));
elseif ($error['usersize'] == count($user->roles) && $user->uid != 1) { }
form_set_error('upload', t('The selected file %name can not be attached to this post, because the disk quota of %quota has been reached.', array('%name' => theme('placeholder', $file->filename), '%quota' => theme('placeholder', format_size($usersize)))));
}
else {
$key = 'upload_'. count($_SESSION['file_uploads']);
$file->source = $key;
$file->list = 1;
$file = file_save_upload($file);
$node->files[$key] = $file;
}
}
for ($x = 0; $x < count($_SESSION['file_uploads']); $x++) {
$key = 'upload_' . $x;
if ($file = file_check_upload($key)) {
$node->files[$key] = $file;
} }
} }
break; }
}
}
/**
* Implementation of hook_nodeapi().
*/
function upload_nodeapi(&$node, $op, $arg) {
switch ($op) {
case 'load': case 'load':
if (variable_get("upload_$node->type", 1) == 1) { if (variable_get("upload_$node->type", 1) == 1) {
$output['files'] = upload_load($node); $output['files'] = upload_load($node);
} }
return $output;
break;
case 'prepare':
_upload_prepare($node);
break;
case 'validate':
_upload_validate($node);
break; break;
case 'view': case 'view':
...@@ -286,13 +332,13 @@ function upload_nodeapi(&$node, $op, $arg) { ...@@ -286,13 +332,13 @@ function upload_nodeapi(&$node, $op, $arg) {
// Build list of attached files // Build list of attached files
foreach ($node->files as $key => $file) { foreach ($node->files as $key => $file) {
if ($file->list && !$node->remove[$key]) { if ($file->list) {
$rows[] = array( $rows[] = array(
'<a href="'. check_url(($file->fid ? file_create_url($file->filepath) : url(file_create_filename($file->filename, file_create_path())))) .'">'. check_plain($file->description ? $file->description : $file->filename) .'</a>', '<a href="'. check_url(($file->fid ? file_create_url($file->filepath) : url(file_create_filename($file->filename, file_create_path())))) .'">'. check_plain($file->description ? $file->description : $file->filename) .'</a>',
format_size($file->filesize) format_size($file->filesize)
); );
// We save the list of files still in preview for later // We save the list of files still in preview for later
if (!$file->fid) { if (strpos($file->fid, 'upload') !== false) {
$previews[] = $file; $previews[] = $file;
} }
} }
...@@ -326,14 +372,18 @@ function upload_nodeapi(&$node, $op, $arg) { ...@@ -326,14 +372,18 @@ function upload_nodeapi(&$node, $op, $arg) {
upload_save($node); upload_save($node);
} }
break; break;
case 'delete': case 'delete':
upload_delete($node); upload_delete($node);
break; break;
case 'delete revision': case 'delete revision':
upload_delete_revision($node); upload_delete_revision($node);
break; break;
case 'search result': case 'search result':
return $node->files ? format_plural(count($node->files), '1 attachment', '%count attachments') : null; return $node->files ? format_plural(count($node->files), '1 attachment', '%count attachments') : null;
case 'rss item': case 'rss item':
if ($node->files) { if ($node->files) {
$files = array(); $files = array();
...@@ -352,9 +402,8 @@ function upload_nodeapi(&$node, $op, $arg) { ...@@ -352,9 +402,8 @@ function upload_nodeapi(&$node, $op, $arg) {
} }
} }
return array(); return array();
}
return $output; }
} }
/** /**
...@@ -380,58 +429,54 @@ function upload_total_space_used() { ...@@ -380,58 +429,54 @@ function upload_total_space_used() {
} }
function upload_save($node) { function upload_save($node) {
$node->old_files = isset($node->files) ? $node->files : array(); foreach ($node->files as $fid => $file) {
upload_nodeapi($node, 'validate', NULL); // Convert file to object for compatability
$node->files = $node->old_files + $node->files; $file = (object)$file;
foreach ((array)$node->files as $key => $file) { // Remove file. Process removals first since no further processing
// New file upload // will be required.
if ($file->source) { if ($file->remove) {
// Only add a file if it's not marked for removal // Remove file previews...
if (!$node->remove[$key]) { if (strpos($file->fid, 'upload') !== false) {
if ($file = file_save_upload($file, $file->filename)) { file_delete($file->filepath);
$fid = db_next_id('{files}_fid');
db_query("INSERT INTO {files} (fid, nid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", $fid, $node->nid, $file->filename, $file->filepath, $file->filemime, $file->filesize);
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $fid, $node->vid, $node->list[$key], $node->description[$key]);
}
} }
// Clean up the session // Remove managed files.
unset($_SESSION['file_uploads'][$file->source]); else {
} db_query('DELETE FROM {file_revisions} WHERE fid = %d AND vid = %d', $fid, $node->vid);
// Update existing file
else {
// Remove existing file, as needed
if ($node->remove[$key]) {
db_query('DELETE FROM {file_revisions} WHERE fid = %d AND vid = %d', $key, $node->vid);
// Only delete a file if it isn't used by any revision // Only delete a file if it isn't used by any revision
$count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $key)); $count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $fid));
if ($count < 1) { if ($count < 1) {
db_query('DELETE FROM {files} WHERE fid = %d', $key); db_query('DELETE FROM {files} WHERE fid = %d', $fid);
file_delete($file->filepath); file_delete($file->filepath);
} }
} }
}
else { // New file upload
// Create a new revision, as needed elseif (strpos($file->fid, 'upload') !== false) {
if ($node->old_vid && is_numeric($key)) { if ($file = file_save_upload($file, $file->filename)) {
// new revision // Track the file which was submitted last, in case of a direct submission
if (isset($node->list)) { // without preview or attach. See notes in upload_prepare.
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $key, $node->vid, $node->list[$key], $node->description[$key]); if ($_SESSION['file_submitted'] == $file->fid) {
} $file->list = variable_get('upload_list_default',1);
// copy of old revision
else {
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $key, $node->vid, $file->list, $file->description);
}
} }
// Update existing revision $file->fid = db_next_id('{files}_fid');
else { db_query("INSERT INTO {files} (fid, nid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", $file->fid, $node->nid, $file->filename, $file->filepath, $file->filemime, $file->filesize);
db_query("UPDATE {file_revisions} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $node->list[$key], $node->description[$key], $key, $node->vid); db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
}
} }
unset($_SESSION['file_previews'][$fid]);
}
// Create a new revision, as needed
elseif ($node->old_vid && is_numeric($fid)) {
db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file->fid, $node->vid, $file->list, $file->description);
}
// Update existing revision
else {
db_query("UPDATE {file_revisions} SET list = %d, description = '%s' WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->fid, $node->vid);
} }
} }
...@@ -446,20 +491,18 @@ function upload_delete($node) { ...@@ -446,20 +491,18 @@ function upload_delete($node) {
} }
foreach ($files as $fid => $file) { foreach ($files as $fid => $file) {
// delete all file revision information associated with the node // Delete all file revision information associated with the node
db_query('DELETE FROM {file_revisions} WHERE fid = %d', $fid); db_query('DELETE FROM {file_revisions} WHERE fid = %d', $fid);
file_delete($file->filepath); file_delete($file->filepath);
} }
// delete all files associated with the node // Delete all files associated with the node
db_query('DELETE FROM {files} WHERE nid = %d', $node->nid); db_query('DELETE FROM {files} WHERE nid = %d', $node->nid);
} }
function upload_delete_revision($node) { function upload_delete_revision($node) {
$files = upload_load($node); foreach ($node->files as $file) {
// Check if the file will be used after this revision is deleted
foreach ($files as $file) {
// check if the file will be used after this revision is deleted
$count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $file->fid)); $count = db_result(db_query('SELECT COUNT(fid) FROM {file_revisions} WHERE fid = %d', $file->fid));
// if the file won't be used, delete it // if the file won't be used, delete it
...@@ -473,6 +516,7 @@ function upload_delete_revision($node) { ...@@ -473,6 +516,7 @@ function upload_delete_revision($node) {
db_query('DELETE FROM {file_revisions} WHERE vid = %d', $node->vid); db_query('DELETE FROM {file_revisions} WHERE vid = %d', $node->vid);
} }
function _upload_form($node) { function _upload_form($node) {
$header = array(t('Delete'), t('List'), t('Description'), t('Size')); $header = array(t('Delete'), t('List'), t('Description'), t('Size'));
$rows = array(); $rows = array();
...@@ -480,27 +524,23 @@ function _upload_form($node) { ...@@ -480,27 +524,23 @@ function _upload_form($node) {
$form['#theme'] = 'upload_form_new'; $form['#theme'] = 'upload_form_new';
if (is_array($node->files) && count($node->files)) { if (is_array($node->files) && count($node->files)) {
$form['current']['#theme'] = 'upload_form_current'; $form['files']['#theme'] = 'upload_form_current';
$form['current']['description']['#tree'] = TRUE; $form['files']['#tree'] = TRUE;
foreach ($node->files as $key => $file) { foreach ($node->files as $key => $file) {
$options[$key] = ''; $description = "<small>". file_create_url((strpos($file->fid,'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))) ."</small>";
if ($file->remove || $node->remove[$key]) { $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
$remove[] = $key; $form['files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize));
} $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove);
if ($file->list || $node->list[$key]) { $form['files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list);
$list[] = $key; $form['files'][$key]['filename'] = array('#type' => 'value', '#value' => $file->filename);
} $form['files'][$key]['filepath'] = array('#type' => 'value', '#value' => $file->filepath);
$description = "<small>". file_create_url(($file->fid ? $file->filepath : file_create_filename($file->filename, file_create_path()))) ."</small>"; $form['files'][$key]['filemime'] = array('#type' => 'value', '#value' => $file->filemime);
$form['current']['description'][$key] = array('#type' => 'textfield', '#default_value' => $file->description ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); $form['files'][$key]['filesize'] = array('#type' => 'value', '#value' => $file->filesize);
$form['current']['size'][$key] = array('#type' => 'markup', '#value' => format_size($file->filesize)); $form['files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid);
} }
$form['current']['remove'] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $remove);
$form['current']['list'] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $list);
$form['files'][$key] = array('#type' => 'hidden', '#value' => 1);
} }
if (user_access('upload files')) { if (user_access('upload files')) {
$form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40); $form['new']['upload'] = array('#type' => 'file', '#title' => t('Attach new file'), '#size' => 40);
$form['new']['fileop'] = array('#type' => 'button', '#value' => t('Attach'), '#name'=> 'fileop', '#attributes' => array('id' => 'fileop')); $form['new']['fileop'] = array('#type' => 'button', '#value' => t('Attach'), '#name'=> 'fileop', '#attributes' => array('id' => 'fileop'));
// The class triggers the js upload behaviour. // The class triggers the js upload behaviour.
...@@ -523,12 +563,13 @@ function theme_upload_form_new($form) { ...@@ -523,12 +563,13 @@ function theme_upload_form_new($form) {
function theme_upload_form_current(&$form) { function theme_upload_form_current(&$form) {
$header = array(t('Delete'), t('List'), t('Description'), t('Size')); $header = array(t('Delete'), t('List'), t('Description'), t('Size'));
foreach (element_children($form['description']) as $key) {
foreach (element_children($form) as $key) {
$row = array(); $row = array();
$row[] = form_render($form['remove'][$key]); $row[] = form_render($form[$key]['remove']);
$row[] = form_render($form['list'][$key]); $row[] = form_render($form[$key]['list']);
$row[] = form_render($form['description'][$key]); $row[] = form_render($form[$key]['description']);
$row[] = form_render($form['size'][$key]); $row[] = form_render($form[$key]['size']);
$rows[] = $row; $rows[] = $row;
} }
$output = theme('table', $header, $rows); $output = theme('table', $header, $rows);
...@@ -576,7 +617,14 @@ function _upload_image($file) { ...@@ -576,7 +617,14 @@ function _upload_image($file) {
function upload_js() { function upload_js() {
// We only do the upload.module part of the node validation process. // We only do the upload.module part of the node validation process.
$node = (object)$_POST['edit']; $node = (object)$_POST['edit'];
upload_nodeapi($node, 'validate', NULL);
// Load existing node files.
$node->files = upload_load($node);
// Handle new uploads, and merge tmp files into node-files.
_upload_prepare($node);
_upload_validate($node);
$form = _upload_form($node); $form = _upload_form($node);
$form = form_builder('upload_js', $form); $form = form_builder('upload_js', $form);
$output = theme('status_messages') . form_render($form); $output = theme('status_messages') . form_render($form);
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment