Commit b5156952 authored by Dries's avatar Dries

- Patch #745590 by justinrandell, effulgentsia, yched, quicksketch,...

- Patch #745590 by justinrandell, effulgentsia, yched, quicksketch, eojthebrave: #managed_file() element does not work when #extended not TRUE, or when ancestor element doesn't have #tree=TRUE.
parent 6344f037
......@@ -5987,14 +5987,22 @@ function element_set_attributes(array &$element, array $map) {
* An array of parent keys, starting with the outermost key.
* @param $value
* The value to set.
* @param $force
* (Optional) If TRUE, the value is forced into the structure even if it
* requires the deletion of an already existing non-array parent value. If
* FALSE, PHP throws an error if trying to add into a value that is not an
* array. Defaults to FALSE.
*
* @see drupal_array_get_nested_value()
*/
function drupal_array_set_nested_value(array &$array, array $parents, $value) {
function drupal_array_set_nested_value(array &$array, array $parents, $value, $force = FALSE) {
$ref = &$array;
foreach ($parents as $parent) {
// Note that PHP is fine with referencing a not existing array key - in this
// case it just creates an entry with NULL as value.
// PHP auto-creates container arrays and NULL entries without error if $ref
// is NULL, but throws an error if $ref is set, but not an array.
if ($force && isset($ref) && !is_array($ref)) {
$ref = array();
}
$ref = &$ref[$parent];
}
$ref = $value;
......@@ -6058,10 +6066,7 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value) {
function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
// array_key_exists() is slower than isset() and triggers notices if the
// second argument is not an array, so only call it when absolutely
// necessary.
if (isset($ref[$parent]) || (is_array($ref) && array_key_exists($parent, $ref))) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
$ref = &$ref[$parent];
}
else {
......
......@@ -1031,10 +1031,29 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
drupal_array_set_nested_value($values, $section, $value);
}
}
// For convenience we always make the value of the pressed button available.
// A button's #value does not require validation, so for convenience we
// allow the value of the clicked button to be retained in its normal
// $form_state['values'] locations, even if these locations are not included
// in #limit_validation_errors.
if (isset($form_state['triggering_element']['#button_type'])) {
$values[$form_state['triggering_element']['#name']] = $form_state['triggering_element']['#value'];
drupal_array_set_nested_value($values, $form_state['triggering_element']['#parents'], $form_state['triggering_element']['#value']);
$button_value = $form_state['triggering_element']['#value'];
// Like all input controls, the button value may be in the location
// dictated by #parents. If it is, copy it to $values, but do not override
// what may already be in $values.
$parents = $form_state['triggering_element']['#parents'];
if (!drupal_array_nested_key_exists($values, $parents) && drupal_array_get_nested_value($form_state['values'], $parents) === $button_value) {
drupal_array_set_nested_value($values, $parents, $button_value);
}
// Additionally, form_builder() places the button value in
// $form_state['values'][BUTTON_NAME]. If it's still there, after
// validation handlers have run, copy it to $values, but do not override
// what may already be in $values.
$name = $form_state['triggering_element']['#name'];
if (!isset($values[$name]) && isset($form_state['values'][$name]) && $form_state['values'][$name] === $button_value) {
$values[$name] = $button_value;
}
}
$form_state['values'] = $values;
}
......@@ -2301,7 +2320,7 @@ function form_type_token_value($element, $input = FALSE) {
* Form state array where the value change should be recorded.
*/
function form_set_value($element, $value, &$form_state) {
drupal_array_set_nested_value($form_state['values'], $element['#parents'], $value);
drupal_array_set_nested_value($form_state['values'], $element['#parents'], $value, TRUE);
}
/**
......
......@@ -648,8 +648,9 @@ function file_field_widget_process($element, &$form_state, $form) {
// Adjust the AJAX settings so that on upload and remove of any individual
// file, the entire group of file fields is updated together.
if ($field['cardinality'] != 1) {
$new_path = preg_replace('/\/\d+\//', '/', $element['remove_button']['#ajax']['path'], 1);
$field_element = drupal_array_get_nested_value($form, array_slice($element['#array_parents'], 0, -1));
$parents = array_slice($element['#array_parents'], 0, -1);
$new_path = 'file/ajax/' . implode('/', $parents) . '/' . $form['form_build_id']['#value'];
$field_element = drupal_array_get_nested_value($form, $parents);
$new_wrapper = $field_element['#id'] . '-ajax-wrapper';
foreach (element_children($element) as $key) {
if (isset($element[$key]['#ajax'])) {
......
......@@ -360,7 +360,7 @@ function file_managed_file_process($element, &$form_state, $form) {
$element['#tree'] = TRUE;
$ajax_settings = array(
'path' => 'file/ajax/' . implode('/', $element['#parents']) . '/' . $form['form_build_id']['#value'],
'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'],
'wrapper' => $element['#id'] . '-ajax-wrapper',
'effect' => 'fade',
'progress' => array(
......
......@@ -13,7 +13,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
protected $admin_user;
function setUp() {
parent::setUp('file');
parent::setUp('file', 'file_module_test');
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
$this->drupalLogin($this->admin_user);
}
......@@ -31,6 +31,13 @@ class FileFieldTestCase extends DrupalWebTestCase {
return $file;
}
/**
* Get the fid of the last inserted file.
*/
function getLastFileId() {
return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
}
/**
* Create a new file field.
*
......@@ -201,6 +208,95 @@ class FileFieldTestCase extends DrupalWebTestCase {
}
}
/**
* Test class for testing the 'managed_file' element type on its own, not as part of a file field.
*
* @todo Create a FileTestCase base class and move FileFieldTestCase methods
* that aren't related to fields into it.
*/
class FileManagedFileElementTestCase extends FileFieldTestCase {
public static function getInfo() {
return array(
'name' => 'Managed file element test',
'description' => 'Tests the managed_file element type.',
'group' => 'File',
);
}
/**
* Tests the managed_file element type.
*/
function testManagedFile() {
// Perform the tests with all permutations of $form['#tree'] and
// $element['#extended'].
foreach (array(0, 1) as $tree) {
foreach (array(0, 1) as $extended) {
$test_file = $this->getTestFile('text');
$path = 'file/test/' . $tree . '/' . $extended;
$input_base_name = $tree ? 'nested_file' : 'file';
// Submit without a file.
$this->drupalPost($path, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.'));
// Submit a new file, without using the Upload button.
$last_fid_prior = $this->getLastFileId();
$edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
$this->drupalPost($path, $edit, t('Save'));
$last_fid = $this->getLastFileId();
$this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
// Submit no new input, but with a default file.
$this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.'));
// Now, test the Upload and Remove buttons, with and without AJAX.
foreach (array(FALSE, TRUE) as $ajax) {
// Upload, then Submit.
$last_fid_prior = $this->getLastFileId();
$this->drupalGet($path);
$edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
if ($ajax) {
$this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
}
else {
$this->drupalPost(NULL, $edit, t('Upload'));
}
$last_fid = $this->getLastFileId();
$this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.'));
$this->drupalPost(NULL, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
// Remove, then Submit.
$this->drupalGet($path . '/' . $last_fid);
if ($ajax) {
$this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
}
else {
$this->drupalPost(NULL, array(), t('Remove'));
}
$this->drupalPost(NULL, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.'));
// Upload, then Remove, then Submit.
$this->drupalGet($path);
$edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
if ($ajax) {
$this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
$this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
}
else {
$this->drupalPost(NULL, $edit, t('Upload'));
$this->drupalPost(NULL, array(), t('Remove'));
}
$this->drupalPost(NULL, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.'));
}
}
}
}
}
/**
* Test class to test file field widget, single and multi-valued, with and without AJAX, with public and private files.
......
......@@ -22,15 +22,22 @@ function file_module_test_menu() {
return $items;
}
function file_module_test_form($form, $form_state) {
$form['#tree'] = TRUE;
/**
* Form builder for testing a 'managed_file' element.
*/
function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FALSE, $default_fid = NULL) {
$form['#tree'] = (bool) $tree;
$form['file'] = array(
$form['nested']['file'] = array(
'#type' => 'managed_file',
'#title' => t('Managed file'),
'#upload_location' => 'public://test',
'#progress_message' => t('Please wait...'),
'#extended' => (bool) $extended,
);
if ($default_fid) {
$form['nested']['file']['#default_value'] = $extended ? array('fid' => $default_fid) : $default_fid;
}
$form['textfield'] = array(
'#type' => 'textfield',
......@@ -44,3 +51,16 @@ function file_module_test_form($form, $form_state) {
return $form;
}
/**
* Form submission handler for file_module_test_form().
*/
function file_module_test_form_submit($form, &$form_state) {
if ($form['#tree']) {
$fid = $form['nested']['file']['#extended'] ? $form_state['values']['nested']['file']['fid'] : $form_state['values']['nested']['file'];
}
else {
$fid = $form['nested']['file']['#extended'] ? $form_state['values']['file']['fid'] : $form_state['values']['file'];
}
drupal_set_message(t('The file id is %fid.', array('%fid' => $fid)));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment