diff --git includes/common.inc includes/common.inc index 701c32c..d2d45ff 100644 --- includes/common.inc +++ includes/common.inc @@ -5468,6 +5468,24 @@ function entity_get_controller($entity_type) { } /** + * Properly handle multistep behavior on entity forms. + * + * This sets takes the current form values, builds a proper entity object out + * of them and stores this entity object in $form_state['ENTITY_TYPE']. + * + * Note that the #entity_type property is required to be set to a valid entity + * type. + */ +function entity_submit_multistep_form($form, &$form_state) { + $entity_type = $form['#entity_type']; + $entity = entity_get_controller($entity_type)->submitFormValues($form, $form_state); + + $form_state[$entity_type] = $entity; + $form_state['rebuild'] = TRUE; + return $entity; +} + +/** * Performs one or more XML-RPC request(s). * * @param $url diff --git includes/entity.inc includes/entity.inc index 66b1eb4..2596ae5 100644 --- includes/entity.inc +++ includes/entity.inc @@ -38,6 +38,25 @@ interface DrupalEntityControllerInterface { * An array of entity objects indexed by their ids. */ public function load($ids = array(), $conditions = array()); + + /** + * Build an entity by processing a submitted entity form. + * + * This also stores a copy of the built entity in $form_state['ENTITY_TYPE'], + * which is required for multi-step forms. + * + * @return + * An entity that is ready to be saved. + */ + public function submitFormValues($form, &$form_state); + + /** + * Build an entity by processing an array of values. + * + * @return + * An entity that is ready to be saved. + */ + public function submit($values); } /** @@ -291,4 +310,18 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface { protected function cacheSet($entities) { $this->entityCache += $entities; } + + public function submitFormValues($form, &$form_state) { + // Actually submit the form values. + $entity = $this->submit($form_state['values']); + + field_attach_submit($this->entityType, $entity, $form, $form_state); + + return $entity; + } + + public function submit($values) { + $entity = (object) $values; + return $entity; + } } diff --git modules/comment/comment.module modules/comment/comment.module index 9513ca8..066c84a 100644 --- modules/comment/comment.module +++ modules/comment/comment.module @@ -1492,6 +1492,35 @@ class CommentController extends DrupalDefaultEntityController { $comments[$key] = $comment; } } + + public function submit($comment) { + $comment += array('subject' => ''); + if (!isset($comment['date'])) { + $comment['date'] = 'now'; + } + + $comment['timestamp'] = strtotime($comment['date']); + if (isset($comment['author'])) { + $account = user_load_by_name($comment['author']); + $comment['uid'] = $account->uid; + $comment['name'] = $comment['author']; + } + + // Validate the comment's subject. If not specified, extract from comment body. + 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'], $comment['comment_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)'); + } + } + return (object)$comment; + } } /** @@ -1629,11 +1658,13 @@ function comment_form($form, &$form_state, $comment) { $comment = (array) $comment; // Take into account multi-step rebuilding. if (isset($form_state['comment'])) { - $comment = $form_state['comment'] + (array) $comment; + $comment = (array) $form_state['comment'] + (array) $comment; } $comment += array('name' => '', 'mail' => '', 'homepage' => ''); $comment = (object) $comment; + $form['#entity_type'] = 'comment'; + if (isset($form_state['comment_preview'])) { $form += $form_state['comment_preview']; } @@ -1863,7 +1894,6 @@ function comment_form($form, &$form_state, $comment) { } $comment->node_type = 'comment_node_' . $node->type; - $form['#builder_function'] = 'comment_form_submit_build_comment'; field_attach_form('comment', $comment, $form, $form_state); return $form; @@ -1873,8 +1903,8 @@ 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); - $form_state['comment_preview'] = comment_preview($comment); + entity_submit_multistep_form($form, $form_state); + $form_state['comment_preview'] = comment_preview($form_state['comment']); } /** @@ -2000,51 +2030,11 @@ 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 (!isset($comment['date'])) { - $comment['date'] = 'now'; - } - - $comment['timestamp'] = strtotime($comment['date']); - if (isset($comment['author'])) { - $account = user_load_by_name($comment['author']); - $comment['uid'] = $account->uid; - $comment['name'] = $comment['author']; - } - - // Validate the comment's subject. If not specified, extract from comment body. - 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'], $comment['comment_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)'); - } - } - return (object)$comment; -} - -/** - * Build a comment by processing form values and prepare for a form rebuild. + * Build a comment by processing submitted form values and prepare for a form rebuild. */ -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; - return $comment; +function comment_submit_form_values($form, &$form_state) { + $node = entity_get_controller('comment')->submitFormValues($form, $form_state); + return $node; } /** @@ -2052,7 +2042,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 = comment_submit_form_values($form, $form_state); if (user_access('post comments') && (user_access('administer comments') || $node->comment == COMMENT_NODE_OPEN)) { comment_save($comment); // Explain the approval queue if necessary. diff --git modules/field/field.form.inc modules/field/field.form.inc index f068ed5..8839830 100644 --- modules/field/field.form.inc +++ modules/field/field.form.inc @@ -327,15 +327,12 @@ function field_default_form_errors($obj_type, $object, $field, $instance, $langc */ 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]); - } + entity_submit_multistep_form($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]); } } diff --git modules/node/node.module modules/node/node.module index a0c215e..218b02d 100644 --- modules/node/node.module +++ modules/node/node.module @@ -822,24 +822,8 @@ function node_validate($node, $form = array()) { /** * Prepare node for save and allow modules to make changes. */ -function node_submit($node) { - global $user; - - // Convert the node to an object, if necessary. - $node = (object)$node; - - if (user_access('administer nodes')) { - // Populate the "authored by" field. - if ($account = user_load_by_name($node->name)) { - $node->uid = $account->uid; - } - else { - $node->uid = 0; - } - } - $node->created = !empty($node->date) ? strtotime($node->date) : REQUEST_TIME; - $node->validated = TRUE; - +function node_submit($values) { + $node = entity_get_controller('node')->submit($values); return $node; } @@ -3073,4 +3057,38 @@ class NodeController extends DrupalDefaultEntityController { $this->hookLoadArguments[] = array_keys($typed_nodes); parent::attachLoad($nodes); } + + public function submitFormValues($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: This is ugly to have here. To be able to remove these two lines, + // we have to standaridze the various entity types to all either use + // form-level or button-level submitting to actually save the entity. + // Currently, only node.module uses button level submitting to save the + // entity while form-level submit handlers are used by other modules to + // expand their values in $form_state. The latter could be moved to a + // to-be-created hook_ENTITY_TYPE_submit() that is invoked from here. + // Alternatively, all other entity types could adopt node.module's way and + // use button-level submit handlers to save the entity. + unset($form_state['submit_handlers']); + form_execute_handlers('submit', $form, $form_state); + return parent::submitFormValues($form, $form_state); + } + + public function submit($values) { + $node = parent::submit($values); + if (user_access('administer nodes')) { + // Populate the "authored by" field. + if ($account = user_load_by_name($node->name)) { + $node->uid = $account->uid; + } + else { + $node->uid = 0; + } + } + $node->created = !empty($node->date) ? strtotime($node->date) : REQUEST_TIME; + $node->validated = TRUE; + + return $node; + } } diff --git modules/node/node.pages.inc modules/node/node.pages.inc index 5b19fe8..3be2f29 100644 --- modules/node/node.pages.inc +++ modules/node/node.pages.inc @@ -111,10 +111,7 @@ function node_form($form, &$form_state, $node) { global $user; if (isset($form_state['node'])) { - $node = $form_state['node'] + (array)$node; - } - if (isset($form_state['node_preview'])) { - $form['#prefix'] = $form_state['node_preview']; + $node = (array)$form_state['node'] + (array)$node; } $node = (object)$node; foreach (array('title') as $key) { @@ -129,6 +126,13 @@ function node_form($form, &$form_state, $node) { $node->in_preview = TRUE; } + if (isset($form_state['node_preview'])) { + $form['#prefix'] = $form_state['node_preview']; + } + + // Set the entity type for field and entity APIs. + $form['#entity_type'] = 'node'; + // Set the id and identify this as a node edit form. $form['#id'] = 'node-form'; $form['#node_edit_form'] = TRUE; @@ -290,7 +294,6 @@ function node_form($form, &$form_state, $node) { $form['#validate'][] = 'node_form_validate'; $form['#theme'] = array($node->type . '_node_form', 'node_form'); - $form['#builder_function'] = 'node_form_submit_build_node'; field_attach_form('node', $node, $form, $form_state, $node->language); return $form; @@ -311,8 +314,8 @@ 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); - $form_state['node_preview'] = node_preview($node); + entity_submit_multistep_form($form, $form_state); + $form_state['node_preview'] = node_preview($form_state['node']); } /** @@ -404,9 +407,7 @@ function theme_node_preview($node) { } function node_form_submit($form, &$form_state) { - global $user; - - $node = node_form_submit_build_node($form, $form_state); + $node = node_submit_form_values($form, $form_state); $insert = empty($node->nid); node_save($node); $node_link = l(t('view'), 'node/' . $node->nid); @@ -436,17 +437,8 @@ function node_form_submit($form, &$form_state) { /** * Build a node by processing submitted form values and prepare for a form rebuild. */ -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. - unset($form_state['submit_handlers']); - form_execute_handlers('submit', $form, $form_state); - $node = node_submit($form_state['values']); - - field_attach_submit('node', $node, $form, $form_state); - - $form_state['node'] = (array)$node; - $form_state['rebuild'] = TRUE; +function node_submit_form_values($form, &$form_state) { + $node = entity_get_controller('node')->submitFormValues($form, $form_state); return $node; } diff --git modules/taxonomy/taxonomy.admin.inc modules/taxonomy/taxonomy.admin.inc index 4507f83..fb1fbd0 100644 --- modules/taxonomy/taxonomy.admin.inc +++ modules/taxonomy/taxonomy.admin.inc @@ -682,16 +682,16 @@ function taxonomy_form_term($form, &$form_state, $vocabulary, $edit = array()) { ); // Take into account multi-step rebuilding. - if (isset($form_state['term'])) { - $edit = $form_state['term'] + $edit; + if (isset($form_state['taxonomy_term'])) { + $edit = (array)$form_state['taxonomy_term'] + $edit; } $parent = array_keys(taxonomy_get_parents($edit['tid'])); + $form['#entity_type'] = 'taxonomy_term'; $form['#term'] = $edit; $form['#term']['parent'] = $parent; $form['#vocabulary'] = $vocabulary; $form['#vocabulary']->nodes = drupal_map_assoc($vocabulary->nodes); - $form['#builder_function'] = 'taxonomy_form_term_submit_builder'; // Check for confirmation forms. if (isset($form_state['confirm_delete'])) { @@ -820,7 +820,7 @@ function taxonomy_form_term_submit($form, &$form_state) { return; } - $term = taxonomy_form_term_submit_builder($form, $form_state); + $term = taxonomy_term_submit_form_values($form, $form_state); $status = taxonomy_term_save($term); switch ($status) { @@ -864,13 +864,8 @@ function taxonomy_form_term_submit($form, &$form_state) { /** * Build a term by processing form values and prepare for a form rebuild. */ -function taxonomy_form_term_submit_builder($form, &$form_state) { - $term = (object) $form_state['values']; - field_attach_submit('taxonomy_term', $term, $form, $form_state); - - $form_state['term'] = (array)$term; - $form_state['rebuild'] = TRUE; - +function taxonomy_term_submit_form_values($form, &$form_state) { + $term = entity_get_controller('taxonomy_term')->submitFormValues($form, $form_state); return $term; }