summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/common.inc48
-rw-r--r--modules/book/book.module26
-rw-r--r--modules/comment/comment.module105
-rw-r--r--modules/field/field.attach.inc3
-rw-r--r--modules/field/field.default.inc23
-rw-r--r--modules/field/field.form.inc15
-rw-r--r--modules/field/tests/field_test.entity.inc29
-rw-r--r--modules/menu/menu.module9
-rw-r--r--modules/node/node.api.php28
-rw-r--r--modules/node/node.pages.inc77
-rw-r--r--modules/poll/poll.module11
-rw-r--r--modules/simpletest/tests/form_test.module2
-rw-r--r--modules/taxonomy/taxonomy.admin.inc152
-rw-r--r--modules/user/user.pages.inc25
14 files changed, 347 insertions, 206 deletions
diff --git a/includes/common.inc b/includes/common.inc
index 13d9862b8..89266d26c 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -6635,6 +6635,54 @@ function entity_invoke($op, $entity_type, $entity) {
}
/**
+ * Helper function for attaching field API validation to entity forms.
+ */
+function entity_form_field_validate($entity_type, $form, &$form_state) {
+ // All field attach API functions act on an entity object, but during form
+ // validation, we don't have one. $form_state contains the entity as it was
+ // prior to processing the current form submission, and we must not update it
+ // until we have fully validated the submitted input. Therefore, for
+ // validation, act on a pseudo entity created out of the form values.
+ $pseudo_entity = (object) $form_state['values'];
+ field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
+}
+
+/**
+ * Helper function for copying submitted values to entity properties for simple entity forms.
+ *
+ * During the submission handling of an entity form's "Save", "Preview", and
+ * possibly other buttons, the form state's entity needs to be updated with the
+ * submitted form values. Each entity form implements its own
+ * $form['#builder_function'] for doing this, appropriate for the particular
+ * entity and form. Many of these entity builder functions can call this helper
+ * function to re-use its logic of copying $form_state['values'][PROPERTY]
+ * values to $entity->PROPERTY for all entries in $form_state['values'] that are
+ * not field data, and calling field_attach_submit() to copy field data.
+ *
+ * For some entity forms (e.g., forms with complex non-field data and forms that
+ * simultaneously edit multiple entities), this behavior may be inappropriate,
+ * so the #builder_function for such forms needs to implement the required
+ * functionality instead of calling this function.
+ */
+function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
+ $info = entity_get_info($entity_type);
+ list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Copy top-level form values that are not for fields to entity properties,
+ // without changing existing entity properties that are not being edited by
+ // this form. Copying field values must be done using field_attach_submit().
+ $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values'];
+ foreach ($values_excluding_fields as $key => $value) {
+ $entity->$key = $value;
+ }
+
+ // Copy field values to the entity.
+ if ($info['fieldable']) {
+ field_attach_submit($entity_type, $entity, $form, $form_state);
+ }
+}
+
+/**
* Performs one or more XML-RPC request(s).
*
* @param $url
diff --git a/modules/book/book.module b/modules/book/book.module
index bea4a077f..9d3284bc8 100644
--- a/modules/book/book.module
+++ b/modules/book/book.module
@@ -416,15 +416,13 @@ function book_form_alter(&$form, &$form_state, $form_id) {
if ($access) {
_book_add_form_elements($form, $form_state, $node);
+ // Since the "Book" dropdown can't trigger a form submission when
+ // JavaScript is disabled, add a submit button to do that. book.css hides
+ // this button when JavaScript is enabled.
$form['book']['pick-book'] = array(
'#type' => 'submit',
'#value' => t('Change book (update list of parents)'),
- // Submit the node form so the parent select options get updated.
- // This is typically only used when JS is disabled. Since the parent options
- // won't be changed via AJAX, a button is provided in the node form to submit
- // the form and generate options in the parent select corresponding to the
- // selected book. This is similar to what happens during a node preview.
- '#submit' => array('node_form_submit_build_node'),
+ '#submit' => array('book_pick_book_nojs_submit'),
'#weight' => 20,
);
}
@@ -432,6 +430,22 @@ function book_form_alter(&$form, &$form_state, $form_id) {
}
/**
+ * Submit handler to change a node's book.
+ *
+ * This handler is run when JavaScript is disabled. It triggers the form to
+ * rebuild so that the "Parent item" options are changed to reflect the newly
+ * selected book. When JavaScript is enabled, the submit button that triggers
+ * this handler is hidden, and the "Book" dropdown directly triggers the
+ * book_form_update() AJAX callback instead.
+ *
+ * @see book_form_update()
+ */
+function book_pick_book_nojs_submit($form, &$form_state) {
+ $form_state['node']->book = $form_state['values']['book'];
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
* Build the parent selection form element for the node form or outline tab.
*
* This function is also called when generating a new set of options during the
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 03cbb5460..d54a33a9c 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -1762,6 +1762,32 @@ function comment_edit_page($comment) {
function comment_form($form, &$form_state, $comment) {
global $user;
+ // During initial form build, add the comment entity to the form state for
+ // use during form building and processing. During a rebuild, use what is in
+ // the form state.
+ if (!isset($form_state['comment'])) {
+ $defaults = array(
+ 'name' => '',
+ 'mail' => '',
+ 'homepage' => '',
+ 'subject' => '',
+ 'comment' => '',
+ 'cid' => NULL,
+ 'pid' => NULL,
+ 'language' => LANGUAGE_NONE,
+ 'uid' => 0,
+ );
+ foreach ($defaults as $key => $value) {
+ if (!isset($comment->$key)) {
+ $comment->$key = $value;
+ }
+ }
+ $form_state['comment'] = $comment;
+ }
+ else {
+ $comment = $form_state['comment'];
+ }
+
$node = node_load($comment->nid);
$form['#node'] = $node;
@@ -1773,24 +1799,6 @@ function comment_form($form, &$form_state, $comment) {
$form['#attributes']['class'][] = 'user-info-from-cookie';
}
- $comment = (array) $comment;
- // Take into account multi-step rebuilding.
- if (isset($form_state['comment'])) {
- $comment = $form_state['comment'] + (array) $comment;
- }
- $comment += array(
- 'name' => '',
- 'mail' => '',
- 'homepage' => '',
- 'subject' => '',
- 'comment' => '',
- 'cid' => NULL,
- 'pid' => NULL,
- 'language' => LANGUAGE_NONE,
- 'uid' => 0,
- );
- $comment = (object) $comment;
-
// If not replying to a comment, use our dedicated page callback for new
// comments on nodes.
if (empty($comment->cid) && empty($comment->pid)) {
@@ -1966,8 +1974,9 @@ function comment_form($form, &$form_state, $comment) {
* Build a preview from submitted form values.
*/
function comment_form_build_preview($form, &$form_state) {
- $comment = comment_form_submit_build_comment($form, $form_state);
+ $comment = $form['#builder_function']($form, $form_state);
$form_state['comment_preview'] = comment_preview($comment);
+ $form_state['rebuild'] = TRUE;
}
/**
@@ -2033,8 +2042,8 @@ function comment_preview($comment) {
*/
function comment_form_validate($form, &$form_state) {
global $user;
- $comment = (object) $form_state['values'];
- field_attach_form_validate('comment', $comment, $form, $form_state);
+
+ entity_form_field_validate('comment', $form, $form_state);
if (!empty($form_state['values']['cid'])) {
if ($form_state['values']['date'] && strtotime($form_state['values']['date']) === FALSE) {
@@ -2085,49 +2094,56 @@ function comment_form_validate($form, &$form_state) {
/**
* Prepare a comment for submission.
- *
- * @param $comment
- * An associative array containing the comment data.
*/
function comment_submit($comment) {
- $comment += array('subject' => '');
- if (empty($comment['date'])) {
- $comment['date'] = 'now';
+ // @todo Legacy support. Remove in Drupal 8.
+ if (is_array($comment)) {
+ $comment += array('subject' => '');
+ $comment = (object) $comment;
+ }
+
+ if (empty($comment->date)) {
+ $comment->date = 'now';
}
- $comment['created'] = strtotime($comment['date']);
- $comment['changed'] = REQUEST_TIME;
+ $comment->created = strtotime($comment->date);
+ $comment->changed = REQUEST_TIME;
- if (!empty($comment['name']) && ($account = user_load_by_name($comment['name']))) {
- $comment['uid'] = $account->uid;
+ if (!empty($comment->name) && ($account = user_load_by_name($comment->name))) {
+ $comment->uid = $account->uid;
}
// Validate the comment's subject. If not specified, extract from comment body.
- if (trim($comment['subject']) == '') {
+ if (trim($comment->subject) == '') {
// The body may be in any format, so:
// 1) Filter it into HTML
// 2) Strip out all HTML tags
// 3) Convert entities back to plain-text.
- $comment['subject'] = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment['comment_body'][LANGUAGE_NONE][0]['value'], $comment['comment_body'][LANGUAGE_NONE][0]['format'])))), 29, TRUE);
+ $comment->subject = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment->comment_body[LANGUAGE_NONE][0]['value'], $comment->comment_body[LANGUAGE_NONE][0]['format'])))), 29, TRUE);
// Edge cases where the comment body is populated only by HTML tags will
// require a default subject.
- if ($comment['subject'] == '') {
- $comment['subject'] = t('(No subject)');
+ if ($comment->subject == '') {
+ $comment->subject = t('(No subject)');
}
}
- return (object) $comment;
+ return $comment;
}
/**
- * Build a comment by processing form values and prepare for a form rebuild.
+ * Updates the form state's comment entity by processing this submission's values.
+ *
+ * This is the default #builder_function for the comment form. It is called
+ * during the "Save" and "Preview" submit handlers to retrieve the entity to
+ * save or preview. This function can also be called by a "Next" button of a
+ * wizard to update the form state's entity with the current step's values
+ * before proceeding to the next step.
+ *
+ * @see comment_form()
*/
function comment_form_submit_build_comment($form, &$form_state) {
- $comment = comment_submit($form_state['values']);
-
- field_attach_submit('comment', $comment, $form, $form_state);
-
- $form_state['comment'] = (array) $comment;
- $form_state['rebuild'] = TRUE;
+ $comment = $form_state['comment'];
+ entity_form_submit_build_entity('comment', $comment, $form, $form_state);
+ comment_submit($comment);
return $comment;
}
@@ -2136,7 +2152,7 @@ function comment_form_submit_build_comment($form, &$form_state) {
*/
function comment_form_submit($form, &$form_state) {
$node = node_load($form_state['values']['nid']);
- $comment = comment_form_submit_build_comment($form, $form_state);
+ $comment = $form['#builder_function']($form, $form_state);
if (user_access('post comments') && (user_access('administer comments') || $node->comment == COMMENT_NODE_OPEN)) {
// Save the anonymous user information to a cookie for reuse.
if (!$comment->uid) {
@@ -2173,7 +2189,6 @@ function comment_form_submit($form, &$form_state) {
// Redirect the user to the node they are commenting on.
$redirect = 'node/' . $node->nid;
}
- unset($form_state['rebuild']);
$form_state['redirect'] = $redirect;
// Clear the block and page caches so that anonymous users see the comment
// they have posted.
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index d461eca46..3f4a641b3 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -547,9 +547,6 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
$form['#entity_type'] = $entity_type;
$form['#bundle'] = $bundle;
- // Save the original entity to allow later re-use.
- $form_state['entity'] = $entity;
-
// Let other modules make changes to the form.
// Avoid module_invoke_all() to let parameters be taken by reference.
foreach (module_implements('field_attach_form') as $module) {
diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc
index 4cd512392..060ad990e 100644
--- a/modules/field/field.default.inc
+++ b/modules/field/field.default.inc
@@ -65,25 +65,12 @@ function field_default_validate($entity_type, $entity, $field, $instance, $langc
}
function field_default_submit($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
- $field_name = $field['field_name'];
-
- if (isset($form_state['values'][$field_name][$langcode])) {
- // Reorder items to account for drag-n-drop reordering.
- if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
- $items = _field_sort_items($field, $items);
- }
- // Filter out empty values.
- $items = _field_filter_items($field, $items);
- }
- elseif (!empty($entity->revision) && isset($form_state['entity']->{$field_name}[$langcode])) {
- // To ensure new revisions are created with all field values in all
- // languages, populate values not included in the form with the ones from
- // the original object. This covers:
- // - partial forms including only a subset of the fields,
- // - fields for which the user has no edit access,
- // - languages not involved in the form.
- $items = $form_state['entity']->{$field_name}[$langcode];
+ // Reorder items to account for drag-n-drop reordering.
+ if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+ $items = _field_sort_items($field, $items);
}
+ // Filter out empty values.
+ $items = _field_filter_items($field, $items);
}
/**
diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc
index 32c4d3a9a..e042fd728 100644
--- a/modules/field/field.form.inc
+++ b/modules/field/field.form.inc
@@ -359,17 +359,12 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la
* to return just the changed part of the form.
*/
function field_add_more_submit($form, &$form_state) {
- // Set the form to rebuild and run submit handlers.
- if (isset($form['#builder_function']) && function_exists($form['#builder_function'])) {
- $entity = $form['#builder_function']($form, $form_state);
-
- // Make the changes we want to the form state.
- $field_name = $form_state['clicked_button']['#field_name'];
- $langcode = $form_state['clicked_button']['#language'];
- if ($form_state['values'][$field_name . '_add_more']) {
- $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
- }
+ $field_name = $form_state['clicked_button']['#field_name'];
+ $langcode = $form_state['clicked_button']['#language'];
+ if ($form_state['values'][$field_name . '_add_more']) {
+ $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
}
+ $form_state['rebuild'] = TRUE;
}
/**
diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc
index b078714b9..b7887c812 100644
--- a/modules/field/tests/field_test.entity.inc
+++ b/modules/field/tests/field_test.entity.inc
@@ -289,10 +289,15 @@ function field_test_entity_edit($entity) {
* Test_entity form.
*/
function field_test_entity_form($form, &$form_state, $entity, $add = FALSE) {
- if (isset($form_state['test_entity'])) {
- $entity = $form_state['test_entity'] + (array) $entity;
+ // During initial form build, add the entity to the form state for use during
+ // form building and processing. During a rebuild, use what is in the form
+ // state.
+ if (!isset($form_state['test_entity'])) {
+ $form_state['test_entity'] = $entity;
+ }
+ else {
+ $entity = $form_state['test_entity'];
}
- $entity = (object) $entity;
foreach (array('ftid', 'ftvid', 'fttype') as $key) {
$form[$key] = array(
@@ -327,15 +332,14 @@ function field_test_entity_form($form, &$form_state, $entity, $add = FALSE) {
* Validate handler for field_test_entity_form().
*/
function field_test_entity_form_validate($form, &$form_state) {
- $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']);
- field_attach_form_validate('test_entity', $entity, $form, $form_state);
+ entity_form_field_validate('test_entity', $form, $form_state);
}
/**
* Submit handler for field_test_entity_form().
*/
function field_test_entity_form_submit($form, &$form_state) {
- $entity = field_test_entity_form_submit_builder($form, $form_state);
+ $entity = $form['#builder_function']($form, $form_state);
$insert = empty($entity->ftid);
field_test_entity_save($entity);
@@ -343,25 +347,20 @@ function field_test_entity_form_submit($form, &$form_state) {
drupal_set_message($message);
if ($entity->ftid) {
- unset($form_state['rebuild']);
$form_state['redirect'] = 'test-entity/' . $entity->ftid . '/edit';
}
else {
// Error on save.
drupal_set_message(t('The entity could not be saved.'), 'error');
+ $form_state['rebuild'] = TRUE;
}
}
/**
- * Builds a test_entity from submitted form values.
+ * Updates the form state's entity by processing this submission's values.
*/
function field_test_entity_form_submit_builder($form, &$form_state) {
- $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']);
- $entity->revision = !empty($form_state['values']['revision']);
- field_attach_submit('test_entity', $entity, $form, $form_state);
-
- $form_state['test_entity'] = (array) $entity;
- $form_state['rebuild'] = TRUE;
-
+ $entity = $form_state['test_entity'];
+ entity_form_submit_build_entity('test_entity', $entity, $form, $form_state);
return $entity;
}
diff --git a/modules/menu/menu.module b/modules/menu/menu.module
index 12381543c..9686a57bf 100644
--- a/modules/menu/menu.module
+++ b/modules/menu/menu.module
@@ -596,7 +596,6 @@ function menu_form_alter(&$form, $form_state, $form_id) {
return;
}
$link = $form['#node']->menu;
- $form['#submit'][] = 'menu_node_form_submit';
$form['menu'] = array(
'#type' => 'fieldset',
@@ -661,15 +660,13 @@ function menu_form_alter(&$form, $form_state, $form_id) {
}
/**
- * Submit handler for node form.
- *
- * @see menu_form_alter()
+ * Implements hook_node_submit().
*/
-function menu_node_form_submit($form, &$form_state) {
+function menu_node_submit($node, $form, $form_state) {
// Decompose the selected menu parent option into 'menu_name' and 'plid', if
// the form used the default parent selection widget.
if (!empty($form_state['values']['menu']['parent'])) {
- list($form_state['values']['menu']['menu_name'], $form_state['values']['menu']['plid']) = explode(':', $form_state['values']['menu']['parent']);
+ list($node->menu['menu_name'], $node->menu['plid']) = explode(':', $form_state['values']['menu']['parent']);
}
}
diff --git a/modules/node/node.api.php b/modules/node/node.api.php
index 95dc3b2b1..690fb9e39 100644
--- a/modules/node/node.api.php
+++ b/modules/node/node.api.php
@@ -665,6 +665,34 @@ function hook_node_validate($node, $form) {
}
/**
+ * Act on a node after validated form values have been copied to it.
+ *
+ * This hook is invoked when a node form is submitted with either the "Save" or
+ * "Preview" button, after form values have been copied to the form state's node
+ * object, but before the node is saved or previewed. It is a chance for modules
+ * to adjust the node's properties from what they are simply after a copy from
+ * $form_state['values']. This hook is intended for adjusting non-field-related
+ * properties. See hook_field_attach_submit() for customizing field-related
+ * properties.
+ *
+ * @param $node
+ * The node being updated in response to a form submission.
+ * @param $form
+ * The form being used to edit the node.
+ * @param $form_state
+ * The form state array.
+ *
+ * @ingroup node_api_hooks
+ */
+function hook_node_submit($node, $form, &$form_state) {
+ // Decompose the selected menu parent option into 'menu_name' and 'plid', if
+ // the form used the default parent selection widget.
+ if (!empty($form_state['values']['menu']['parent'])) {
+ list($node->menu['menu_name'], $node->menu['plid']) = explode(':', $form_state['values']['menu']['parent']);
+ }
+}
+
+/**
* Act on a node that is being assembled before rendering.
*
* The module may add elements to $node->content prior to rendering. This hook
diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc
index 6b9544f17..96a4ebfcb 100644
--- a/modules/node/node.pages.inc
+++ b/modules/node/node.pages.inc
@@ -76,12 +76,15 @@ function node_add($type) {
}
function node_form_validate($form, &$form_state) {
+ // $form_state['node'] contains the actual entity being edited, but we must
+ // not update it with form values that have not yet been validated, so we
+ // create a pseudo-entity to use during validation.
$node = (object) $form_state['values'];
node_validate($node, $form);
// Field validation. Requires access to $form_state, so this cannot be
// done in node_validate() as it currently exists.
- field_attach_form_validate('node', $node, $form, $form_state);
+ entity_form_field_validate('node', $form, $form_state);
}
/**
@@ -89,28 +92,29 @@ function node_form_validate($form, &$form_state) {
*/
function node_form($form, &$form_state, $node) {
global $user;
- // This form has its own multistep persistence.
- if ($form_state['rebuild']) {
- $form_state['input'] = array();
- }
- if (isset($form_state['node'])) {
- $node = (object) ($form_state['node'] + (array) $node);
- }
- if (isset($form_state['node_preview'])) {
- $form['#prefix'] = $form_state['node_preview'];
- }
- foreach (array('title') as $key) {
- if (!isset($node->$key)) {
- $node->$key = NULL;
+ // During initial form build, add the node entity to the form state for use
+ // during form building and processing. During a rebuild, use what is in the
+ // form state.
+ if (!isset($form_state['node'])) {
+ if (!isset($node->title)) {
+ $node->title = NULL;
}
- }
- if (!isset($form_state['node_preview'])) {
node_object_prepare($node);
+ $form_state['node'] = $node;
}
else {
+ $node = $form_state['node'];
+ }
+
+ // Some special stuff when previewing a node.
+ if (isset($form_state['node_preview'])) {
+ $form['#prefix'] = $form_state['node_preview'];
$node->in_preview = TRUE;
}
+ else {
+ unset($node->in_preview);
+ }
// Identify this as a node edit form.
$form['#node_edit_form'] = TRUE;
@@ -140,7 +144,9 @@ function node_form($form, &$form_state, $node) {
if (!isset($form['title']['#weight'])) {
$form['title']['#weight'] = -5;
}
-
+ // @todo Legacy support. Modules adding form building and processing functions
+ // to the node form are encouraged to access the node using
+ // $form_state['node']. Remove in Drupal 8.
$form['#node'] = $node;
$form['additional_settings'] = array(
@@ -299,8 +305,9 @@ function node_form_delete_submit($form, &$form_state) {
function node_form_build_preview($form, &$form_state) {
- $node = node_form_submit_build_node($form, $form_state);
+ $node = $form['#builder_function']($form, $form_state);
$form_state['node_preview'] = node_preview($node);
+ $form_state['rebuild'] = TRUE;
}
/**
@@ -382,7 +389,7 @@ function theme_node_preview($variables) {
}
function node_form_submit($form, &$form_state) {
- $node = node_form_submit_build_node($form, $form_state);
+ $node = $form['#builder_function']($form, $form_state);
$insert = empty($node->nid);
node_save($node);
$node_link = l(t('view'), 'node/' . $node->nid);
@@ -398,7 +405,6 @@ function node_form_submit($form, &$form_state) {
drupal_set_message(t('@type %title has been updated.', $t_args));
}
if ($node->nid) {
- unset($form_state['rebuild']);
$form_state['values']['nid'] = $node->nid;
$form_state['nid'] = $node->nid;
$form_state['redirect'] = 'node/' . $node->nid;
@@ -407,25 +413,42 @@ function node_form_submit($form, &$form_state) {
// In the unlikely case something went wrong on save, the node will be
// rebuilt and node form redisplayed the same way as in preview.
drupal_set_message(t('The post could not be saved.'), 'error');
+ $form_state['rebuild'] = TRUE;
}
// Clear the page and block caches.
cache_clear_all();
}
/**
- * Build a node by processing submitted form values and prepare for a form rebuild.
+ * Updates the form state's node entity by processing this submission's values.
+ *
+ * This is the default #builder_function for the node form. It is called
+ * during the "Save" and "Preview" submit handlers to retrieve the entity to
+ * save or preview. This function can also be called by a "Next" button of a
+ * wizard to update the form state's entity with the current step's values
+ * before proceeding to the next step.
+ *
+ * @see node_form()
*/
function node_form_submit_build_node($form, &$form_state) {
- // Unset any button-level handlers, execute all the form-level submit
- // functions to process the form values into an updated node.
+ // @todo Legacy support for modules that extend the node form with form-level
+ // submit handlers that adjust $form_state['values'] prior to those values
+ // being used to update the entity. Module authors are encouraged to instead
+ // adjust the node directly within a hook_node_submit() implementation. For
+ // Drupal 8, evaluate whether the pattern of triggering form-level submit
+ // handlers during button-level submit processing is worth supporting
+ // properly, and if so, add a Form API function for doing so.
unset($form_state['submit_handlers']);
form_execute_handlers('submit', $form, $form_state);
- $node = node_submit((object) $form_state['values']);
- field_attach_submit('node', $node, $form, $form_state);
+ $node = $form_state['node'];
+ entity_form_submit_build_entity('node', $node, $form, $form_state);
- $form_state['node'] = (array) $node;
- $form_state['rebuild'] = TRUE;
+ node_submit($node);
+ foreach (module_implements('node_submit') as $module) {
+ $function = $module . '_node_submit';
+ $function($node, $form, $form_state);
+ }
return $node;
}
diff --git a/modules/poll/poll.module b/modules/poll/poll.module
index f273953a4..d698e0301 100644
--- a/modules/poll/poll.module
+++ b/modules/poll/poll.module
@@ -366,15 +366,18 @@ function poll_form($node, &$form_state) {
* return just the changed part of the form.
*/
function poll_more_choices_submit($form, &$form_state) {
- include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'node') . '/node.pages.inc';
- // Set the form to rebuild and run submit handlers.
- node_form_submit_build_node($form, $form_state);
-
// Make the changes we want to the form state.
if ($form_state['values']['poll_more']) {
$n = $_GET['q'] == 'system/ajax' ? 1 : 5;
$form_state['choice_count'] = count($form_state['values']['choice']) + $n;
}
+ // Renumber the choices. This invalidates the corresponding key/value
+ // associations in $form_state['input'], so clear that out. This requires
+ // poll_form() to rebuild the choices with the values in
+ // $form_state['node']->choice, which it does.
+ $form_state['node']->choice = array_values($form_state['values']['choice']);
+ unset($form_state['input']['choice']);
+ $form_state['rebuild'] = TRUE;
}
function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight = 0, $size = 10) {
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 6a1cb3258..0b9284adb 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -1143,8 +1143,6 @@ function form_test_form_user_register_form_alter(&$form, &$form_state) {
if (!empty($_REQUEST['field'])) {
$node = (object)array('type' => 'page');
field_attach_form('node', $node, $form, $form_state);
- // The form API requires the builder function to set rebuilding, so do so.
- $form['#builder_function'] = 'form_test_user_register_form_rebuild';
}
}
diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc
index 59defe82c..1080e3818 100644
--- a/modules/taxonomy/taxonomy.admin.inc
+++ b/modules/taxonomy/taxonomy.admin.inc
@@ -100,17 +100,33 @@ function theme_taxonomy_overview_vocabularies($variables) {
* @see taxonomy_form_vocabulary_submit()
*/
function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
- if (!is_array($edit)) {
- $edit = (array) $edit;
- }
- $edit += array(
- 'name' => '',
- 'machine_name' => '',
- 'description' => '',
- 'hierarchy' => 0,
- 'weight' => 0,
- );
- $form['#vocabulary'] = (object) $edit;
+ // During initial form build, add the entity to the form state for use
+ // during form building and processing. During a rebuild, use what is in the
+ // form state.
+ if (!isset($form_state['vocabulary'])) {
+ $vocabulary = is_object($edit) ? $edit : (object) $edit;
+ $defaults = array(
+ 'name' => '',
+ 'machine_name' => '',
+ 'description' => '',
+ 'hierarchy' => 0,
+ 'weight' => 0,
+ );
+ foreach ($defaults as $key => $value) {
+ if (!isset($vocabulary->$key)) {
+ $vocabulary->$key = $value;
+ }
+ }
+ $form_state['vocabulary'] = $vocabulary;
+ }
+ else {
+ $vocabulary = $form_state['vocabulary'];
+ }
+
+ // @todo Legacy support. Modules are encouraged to access the entity using
+ // $form_state. Remove in Drupal 8.
+ $form['#vocabulary'] = $form_state['vocabulary'];
+
// Check whether we need a deletion confirmation form.
if (isset($form_state['confirm_delete']) && isset($form_state['values']['vid'])) {
return taxonomy_vocabulary_confirm_delete($form, $form_state, $form_state['values']['vid']);
@@ -118,7 +134,7 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
- '#default_value' => $edit['name'],
+ '#default_value' => $vocabulary->name,
'#maxlength' => 255,
'#required' => TRUE,
'#field_suffix' => ' <small id="edit-name-suffix">&nbsp;</small>',
@@ -139,7 +155,7 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
$form['machine_name'] = array(
'#type' => 'textfield',
'#title' => t('Machine-readable name'),
- '#default_value' => $edit['machine_name'],
+ '#default_value' => $vocabulary->machine_name,
'#maxlength' => 255,
'#description' => t('The unique machine-readable name for this vocabulary, used for theme templates. Can only contain lowercase letters, numbers, and underscores.'),
'#required' => TRUE,
@@ -150,7 +166,7 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
$form['description'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
- '#default_value' => $edit['description'],
+ '#default_value' => $vocabulary->description,
);
// Set the hierarchy to "multiple parents" by default. This simplifies the
// vocabulary form and standardizes the term form.
@@ -161,10 +177,10 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
- if (isset($edit['vid'])) {
+ if (isset($vocabulary->vid)) {
$form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
- $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
- $form['module'] = array('#type' => 'value', '#value' => $edit['module']);
+ $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
+ $form['module'] = array('#type' => 'value', '#value' => $vocabulary->module);
}
return $form;
}
@@ -201,16 +217,17 @@ function taxonomy_form_vocabulary_validate($form, &$form_state) {
* Accept the form submission for a vocabulary and save the results.
*/
function taxonomy_form_vocabulary_submit($form, &$form_state) {
- $old_vocabulary = $form['#vocabulary'];
if ($form_state['clicked_button']['#value'] == t('Delete')) {
// Rebuild the form to confirm vocabulary deletion.
$form_state['rebuild'] = TRUE;
$form_state['confirm_delete'] = TRUE;
return;
}
- $vocabulary = (object) $form_state['values'];
- if ($vocabulary->machine_name != $old_vocabulary->machine_name) {
- field_attach_rename_bundle('taxonomy_term', $old_vocabulary->machine_name, $vocabulary->machine_name);
+ $old_machine_name = $form_state['vocabulary']->machine_name;
+ $vocabulary = $form_state['vocabulary'];
+ entity_form_submit_build_entity('taxonomy_vocabulary', $vocabulary, $form, $form_state);
+ if ($vocabulary->machine_name != $old_machine_name) {
+ field_attach_rename_bundle('taxonomy_term', $old_machine_name, $vocabulary->machine_name);
}
switch (taxonomy_vocabulary_save($vocabulary)) {
case SAVED_NEW:
@@ -617,33 +634,45 @@ function theme_taxonomy_overview_terms($variables) {
* @see taxonomy_form_term_submit()
*/
function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = NULL) {
- if (!isset($vocabulary) && is_object($edit)) {
- $vocabulary = taxonomy_vocabulary_load($edit->vid);
- $edit = (array) $edit;
- }
- $edit += array(
- 'name' => '',
- 'description' => '',
- 'format' => filter_default_format(),
- 'vocabulary_machine_name' => $vocabulary->machine_name,
- 'tid' => NULL,
- 'weight' => 0,
- );
-
- // Take into account multi-step rebuilding.
- if (isset($form_state['term'])) {
- $edit = $form_state['term'] + $edit;
+ // During initial form build, add the term entity to the form state for use
+ // during form building and processing. During a rebuild, use what is in the
+ // form state.
+ if (!isset($form_state['term'])) {
+ $term = is_object($edit) ? $edit : (object) $edit;
+ if (!isset($vocabulary) && isset($term->vid)) {
+ $vocabulary = taxonomy_vocabulary_load($term->vid);
+ }
+ $defaults = array(
+ 'name' => '',
+ 'description' => '',
+ 'format' => filter_default_format(),
+ 'vocabulary_machine_name' => isset($vocabulary) ? $vocabulary->machine_name : NULL,
+ 'tid' => NULL,
+ 'weight' => 0,
+ );
+ foreach ($defaults as $key => $value) {
+ if (!isset($term->$key)) {
+ $term->$key = $value;
+ }
+ }
+ $form_state['term'] = $term;
+ }
+ else {
+ $term = $form_state['term'];
+ if (!isset($vocabulary) && isset($term->vid)) {
+ $vocabulary = taxonomy_vocabulary_load($term->vid);
+ }
}
- $parent = array_keys(taxonomy_get_parents($edit['tid']));
- $form['#term'] = $edit;
+ $parent = array_keys(taxonomy_get_parents($term->tid));
+ $form['#term'] = (array) $term;
$form['#term']['parent'] = $parent;
$form['#vocabulary'] = $vocabulary;
$form['#builder_function'] = 'taxonomy_form_term_submit_builder';
// Check for confirmation forms.
if (isset($form_state['confirm_delete'])) {
- return array_merge($form, taxonomy_term_confirm_delete($form, $form_state, $edit['tid']));
+ return array_merge($form, taxonomy_term_confirm_delete($form, $form_state, $term->tid));
}
elseif (isset($form_state['confirm_parents'])) {
return array_merge($form, taxonomy_term_confirm_parents($form, $form_state, $vocabulary));
@@ -652,7 +681,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
- '#default_value' => $edit['name'],
+ '#default_value' => $term->name,
'#maxlength' => 255,
'#required' => TRUE,
'#weight' => -5,
@@ -660,18 +689,18 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
$form['description'] = array(
'#type' => 'text_format',
'#title' => t('Description'),
- '#default_value' => $edit['description'],
- '#format' => $edit['format'],
+ '#default_value' => $term->description,
+ '#format' => $term->format,
'#weight' => 0,
);
$form['vocabulary_machine_name'] = array(
'#type' => 'textfield',
'#access' => FALSE,
- '#value' => isset($edit['vocabulary_machine_name']) ? $edit['vocabulary_machine_name'] : $vocabulary->name,
+ '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name,
);
- field_attach_form('taxonomy_term', (object) $edit, $form, $form_state);
+ field_attach_form('taxonomy_term', $term, $form, $form_state);
$form['relations'] = array(
'#type' => 'fieldset',
@@ -686,23 +715,23 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
// full vocabulary. Contrib modules can then intercept before
// hook_form_alter to provide scalable alternatives.
if (!variable_get('taxonomy_override_selector', FALSE)) {
- $parent = array_keys(taxonomy_get_parents($edit['tid']));
- $children = taxonomy_get_tree($vocabulary->vid, $edit['tid']);
+ $parent = array_keys(taxonomy_get_parents($term->tid));
+ $children = taxonomy_get_tree($vocabulary->vid, $term->tid);
// A term can't be the child of itself, nor of its children.
foreach ($children as $child) {
$exclude[] = $child->tid;
}
- $exclude[] = $edit['tid'];
+ $exclude[] = $term->tid;
$tree = taxonomy_get_tree($vocabulary->vid);
$options = array('<' . t('root') . '>');
if (empty($parent)) {
$parent = array(0);
}
- foreach ($tree as $term) {
- if (!in_array($term->tid, $exclude)) {
- $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
+ foreach ($tree as $item) {
+ if (!in_array($item->tid, $exclude)) {
+ $options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
}
}
$form['relations']['parent'] = array(
@@ -718,7 +747,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
'#type' => 'textfield',
'#title' => t('Weight'),
'#size' => 6,
- '#default_value' => $edit['weight'],
+ '#default_value' => $term->weight,
'#description' => t('Terms are displayed in ascending order by weight.'),
'#required' => TRUE,
);
@@ -728,7 +757,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
);
$form['tid'] = array(
'#type' => 'value',
- '#value' => $edit['tid'],
+ '#value' => $term->tid,
);
$form['actions'] = array('#type' => 'actions');
@@ -738,7 +767,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
'#weight' => 5,
);
- if ($edit['tid']) {
+ if ($term->tid) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
@@ -759,7 +788,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary =
* @see taxonomy_form_term()
*/
function taxonomy_form_term_validate($form, &$form_state) {
- field_attach_form_validate('taxonomy_term', (object) $form_state['values'], $form, $form_state);
+ entity_form_field_validate('taxonomy_term', $form, $form_state);
// Ensure numeric values.
if (isset($form_state['values']['weight']) && !is_numeric($form_state['values']['weight'])) {
@@ -790,7 +819,7 @@ function taxonomy_form_term_submit($form, &$form_state) {
return;
}
- $term = taxonomy_form_term_submit_builder($form, $form_state);
+ $term = $form['#builder_function']($form, $form_state);
$status = taxonomy_term_save($term);
switch ($status) {
@@ -833,21 +862,16 @@ function taxonomy_form_term_submit($form, &$form_state) {
}
/**
- * Build a term by processing form values and prepare for a form rebuild.
+ * Updates the form state's term entity by processing this submission's values.
*/
function taxonomy_form_term_submit_builder($form, &$form_state) {
- $term = (object) $form_state['values'];
+ $term = $form_state['term'];
+ entity_form_submit_build_entity('taxonomy_term', $term, $form, $form_state);
// Convert text_format field into values expected by taxonomy_term_save().
$description = $form_state['values']['description'];
$term->description = $description['value'];
$term->format = $description['format'];
-
- field_attach_submit('taxonomy_term', $term, $form, $form_state);
-
- $form_state['term'] = (array) $term;
- $form_state['rebuild'] = TRUE;
-
return $term;
}
diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc
index 32c20a4d7..24b9a2b72 100644
--- a/modules/user/user.pages.inc
+++ b/modules/user/user.pages.inc
@@ -243,6 +243,18 @@ function template_preprocess_user_profile_category(&$variables) {
function user_profile_form($form, &$form_state, $account, $category = 'account') {
global $user;
+ // During initial form build, add the entity to the form state for use during
+ // form building and processing. During a rebuild, use what is in the form
+ // state.
+ if (!isset($form_state['user'])) {
+ $form_state['user'] = $account;
+ }
+ else {
+ $account = $form_state['user'];
+ }
+
+ // @todo Legacy support. Modules are encouraged to access the entity using
+ // $form_state. Remove in Drupal 8.
$form['#user'] = $account;
$form['#user_category'] = $category;
@@ -278,22 +290,23 @@ function user_profile_form($form, &$form_state, $account, $category = 'account')
* Validation function for the user account and profile editing form.
*/
function user_profile_form_validate($form, &$form_state) {
- $edit = (object) $form_state['values'];
- field_attach_form_validate('user', $edit, $form, $form_state);
+ entity_form_field_validate('user', $form, $form_state);
}
/**
* Submit function for the user account and profile editing form.
*/
function user_profile_form_submit($form, &$form_state) {
- $account = $form['#user'];
+ $account = $form_state['user'];
$category = $form['#user_category'];
// Remove unneeded values.
form_state_values_clean($form_state);
- $edit = (object) $form_state['values'];
- field_attach_submit('user', $edit, $form, $form_state);
- $edit = (array) $edit;
+ entity_form_submit_build_entity('user', $account, $form, $form_state);
+
+ // Populate $edit with the properties of $account, which have been edited on
+ // this form by taking over all values, which appear in the form values too.
+ $edit = array_intersect_key((array) $account, $form_state['values']);
user_save($account, $edit, $category);
$form_state['values']['uid'] = $account->uid;