diff --git a/core/includes/form.inc b/core/includes/form.inc index 395cca7..0dd261d 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -745,9 +745,13 @@ function drupal_retrieve_form($form_id, &$form_state) { // the constructor function itself. $args = $form_state['build_info']['args']; - // We first check to see if there's a function named after the $form_id. + // If an explicit form builder callback is defined we just use it, otherwise + // we look for a function named after the $form_id. + $callback = !empty($form_state['build_info']['callback']) ? $form_state['build_info']['callback'] : $form_id; + + // We first check to see if there is a valid form builder callback defined. // If there is, we simply pass the arguments on to it to get the form. - if (!function_exists($form_id)) { + if (!is_callable($callback)) { // In cases where many form_ids need to share a central constructor function, // such as the node editing form, modules can implement hook_forms(). It // maps one or more form_ids to the correct constructor functions. @@ -806,7 +810,7 @@ function drupal_retrieve_form($form_id, &$form_state) { // If $callback was returned by a hook_forms() implementation, call it. // Otherwise, call the function named after the form id. - $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args); + $form = call_user_func_array($callback, $args); $form['#form_id'] = $form_id; return $form; @@ -1467,7 +1471,7 @@ function form_execute_handlers($type, &$form, &$form_state) { $batch['has_form_submits'] = TRUE; } else { - $function($form, $form_state); + call_user_func_array($function, array(&$form, &$form_state)); } $return = TRUE; } diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index a6230f2..4eb546b 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -9,6 +9,8 @@ * book page, etc. */ +use Drupal\entity\EntityFormController; + use Drupal\node\Node; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -103,8 +105,11 @@ function comment_entity_info() { 'base table' => 'comment', 'uri callback' => 'comment_uri', 'fieldable' => TRUE, - 'controller class' => 'Drupal\comment\CommentStorageController', 'entity class' => 'Drupal\comment\Comment', + 'controller class' => 'Drupal\comment\CommentStorageController', + 'form controller class' => array( + 'default' => 'Drupal\comment\CommentFormController', + ), 'entity keys' => array( 'id' => 'cid', 'bundle' => 'node_type', @@ -765,8 +770,7 @@ function comment_node_page_additions(Node $node) { // Append comment form if needed. if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) { - $comment = entity_create('comment', array('nid' => $node->nid)); - $additions['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment); + $additions['comment_form'] = comment_edit($node); } if ($additions) { @@ -782,6 +786,14 @@ function comment_node_page_additions(Node $node) { } /** + * Returns a rendered form to comment the given node. + */ +function comment_edit($node, $pid = NULL) { + $values = array('nid' => $node->nid, 'pid' => $pid, 'node_type' => 'comment_node_' . $node->type); + return entity_create('comment', $values)->form(); +} + +/** * Retrieves comments for a thread. * * @param Drupal\node\Node $node @@ -1650,19 +1662,7 @@ function comment_get_display_page($cid, $node_type) { */ function comment_edit_page(Comment $comment) { drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject)), PASS_THROUGH); - $node = node_load($comment->nid); - return drupal_get_form("comment_node_{$node->type}_form", $comment); -} - -/** - * Implements hook_forms(). - */ -function comment_forms() { - $forms = array(); - foreach (node_type_get_types() as $type) { - $forms["comment_node_{$type->type}_form"]['callback'] = 'comment_form'; - } - return $forms; + return $comment->form(); } /** @@ -1677,18 +1677,10 @@ function comment_form($form, &$form_state, Comment $comment) { global $user; $language_content = drupal_container()->get(LANGUAGE_TYPE_CONTENT); - // 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'])) { - $form_state['comment'] = $comment; - } - else { - $comment = $form_state['comment']; - } - $node = node_load($comment->nid); + // @todo Remove all the usages of $form['#node']. $form['#node'] = $node; + $form_state['node'] = $node; // Use #comment-form as unique jump target, regardless of node type. $form['#id'] = drupal_html_id('comment_form'); @@ -1848,24 +1840,8 @@ function comment_form($form, &$form_state, Comment $comment) { '#value' => $comment_langcode, ); - // Only show the save button if comment previews are optional or if we are - // already previewing the submission. - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#access' => ($comment->cid && user_access('administer comments')) || variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || isset($form_state['comment_preview']), - ); - $form['actions']['preview'] = array( - '#type' => 'submit', - '#value' => t('Preview'), - '#access' => (variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_DISABLED), - '#submit' => array('comment_form_build_preview'), - ); - // Attach fields. $comment->node_type = 'comment_node_' . $node->type; - field_attach_form('comment', $comment, $form, $form_state); return $form; } @@ -1948,8 +1924,6 @@ function comment_preview(Comment $comment) { function comment_form_validate($form, &$form_state) { global $user; - entity_form_field_validate('comment', $form, $form_state); - if (!empty($form_state['values']['cid'])) { // Verify the name in case it is being changed from being anonymous. $account = user_load_by_name($form_state['values']['name']); @@ -2029,26 +2003,6 @@ function comment_submit(Comment $comment) { } /** - * Updates the comment entity by processing the 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() - * @see comment_form_preview() - * @see comment_form_submit() - */ -function comment_form_submit_build_comment($form, &$form_state) { - $comment = $form_state['comment']; - entity_form_submit_build_entity('comment', $comment, $form, $form_state); - comment_submit($comment); - return $comment; -} - -/** * Form submission handler for comment_form(). * * @see comment_form_validate() @@ -2056,7 +2010,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 = EntityFormController::getFormInstance($form_state)->getEntity(); 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 (user_is_anonymous()) { diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc index 5176846..7ee3e04 100644 --- a/core/modules/comment/comment.pages.inc +++ b/core/modules/comment/comment.pages.inc @@ -43,8 +43,7 @@ function comment_reply(Node $node, $pid = NULL) { // The user is previewing a comment prior to submitting it. if ($op == t('Preview')) { if (user_access('post comments')) { - $comment = entity_create('comment', array('nid' => $node->nid, 'pid' => $pid)); - $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment); + $build['comment_form'] = comment_edit($node, $pid); } else { drupal_set_message(t('You are not authorized to post comments.'), 'error'); @@ -92,8 +91,7 @@ function comment_reply(Node $node, $pid = NULL) { drupal_goto("node/$node->nid"); } elseif (user_access('post comments')) { - $comment = entity_create('comment', array('nid' => $node->nid, 'pid' => $pid)); - $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment); + $build['comment_form'] = comment_edit($node, $pid); } else { drupal_set_message(t('You are not authorized to post comments.'), 'error'); diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php new file mode 100644 index 0000000..f11575f --- /dev/null +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -0,0 +1,76 @@ +entity); + return parent::form($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::actions() + */ + protected function actions(array $form, array &$form_state) { + $element = parent::actions($form, $form_state); + $comment = $this->entity; + $node = $form_state['node']; + $preview_mode = variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL); + + // No delete action on the comment form. + unset($element['delete']); + + // Only show the save button if comment previews are optional or if we are + // already previewing the submission. + $element['submit']['#access'] = ($comment->cid && user_access('administer comments')) || $preview_mode != DRUPAL_REQUIRED || isset($form_state['comment_preview']); + + $element['preview'] = array( + '#type' => 'submit', + '#value' => t('Preview'), + '#access' => $preview_mode != DRUPAL_DISABLED, + '#submit' => array('comment_form_build_preview'), + ); + + $element['#weight'] = $form['comment_body']['#weight'] + 0.01; + + return $element; + } + + /** + * @see Drupal\entity\EntityFormController::validate() + */ + public function validate(array $form, array &$form_state) { + parent::validate($form, $form_state); + comment_form_validate($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::submit() + */ + public function submit(array $form, array &$form_state) { + parent::submit($form, $form_state); + comment_submit($this->entity); + } + + /** + * @see Drupal\entity\EntityFormController::save() + */ + public function save(array $form, array &$form_state) { + comment_form_submit($form, $form_state); + } +} diff --git a/core/modules/entity/entity.api.php b/core/modules/entity/entity.api.php index 02d8754..4d4830b 100644 --- a/core/modules/entity/entity.api.php +++ b/core/modules/entity/entity.api.php @@ -27,6 +27,12 @@ * The class has to implement the * Drupal\entity\EntityStorageControllerInterface interface. Leave blank * to use the Drupal\entity\DatabaseStorageController implementation. + * - form controller class: An array of form controller class names keyed by + * context name. Each class handle the edit form for the entity being + * defined, alongside with its handlers, for the related context. The + * context is also passed to the class costructor hence if only small tweaks + * are needed to adapt the edit form to the various contexts a unique class + * may be provided. * - base table: (used by Drupal\entity\DatabaseStorageController) The * name of the entity type's base table. * - static cache: (used by Drupal\entity\DatabaseStorageController) @@ -136,6 +142,9 @@ function hook_entity_info() { 'label' => t('Node'), 'entity class' => 'Drupal\node\Node', 'controller class' => 'Drupal\node\NodeStorageController', + 'form controller class' => array( + 'default' => 'Drupal\node\NodeFormController', + ), 'base table' => 'node', 'revision table' => 'node_revision', 'uri callback' => 'node_uri', diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module index a66b1e8..dae931d 100644 --- a/core/modules/entity/entity.module +++ b/core/modules/entity/entity.module @@ -71,6 +71,9 @@ function entity_get_info($entity_type = NULL) { 'fieldable' => FALSE, 'entity class' => 'Drupal\entity\Entity', 'controller class' => 'Drupal\entity\DatabaseStorageController', + 'form controller class' => array( + 'default' => 'Drupal\entity\EntityFormController', + ), 'static cache' => TRUE, 'field cache' => TRUE, 'bundles' => array(), @@ -447,6 +450,90 @@ function entity_form_field_validate($entity_type, $form, &$form_state) { } /** + * Returns a form controller for the given context. + * + * Since there might be different contexts in which an entity or parts of it are + * edited, multiple form controllers suitable to the different contexts may be + * defined. If no valid controller is found for the given context the default + * one will be used. + * + * @param $entity_type + * The type of the entity. + * @param + * (optional) The bundle of the entity being edited. + * @param $context + * (optional) The name of the context identifying the form controller. + * + * @return Drupal\entity\EntityFormController + * An instance of the Drupal\Core\Entity\FormController class. + */ +function entity_get_form_controller($entity_type, $bundle = NULL, $context = 'default') { + $info = entity_get_info($entity_type); + + // If no controller is specified default to the base implementation. + if (!empty($info) && empty($info['form controller class'])) { + $class = 'Drupal\entity\EntityFormController'; + } + + // Check whether a bundle-specific form controller exists. + if (!empty($bundle) && !empty($info['bundle form controller class'][$bundle][$context])) { + $class = $info['bundle form controller class'][$bundle][$context]; + } + // Fall back to the per-entity-type form controller. + elseif (!empty($info['form controller class'][$context])) { + $class = $info['form controller class'][$context]; + } + // If a non-existing context has been specified stop. + else { + throw new EntityMalformedException("Missing form controller for '$entity_type' bundle '$bundle' in context '$context'"); + } + + return new $class($context); +} + +/** + * Helper function. Returns the form id for the given entity. + */ +function _entity_form_id(EntityInterface $entity, $context = 'default') { + $entity_type = $entity->entityType(); + $bundle = $entity->bundle(); + $form_id = $entity_type; + if ($bundle != $entity_type) { + $form_id = $bundle . '_' . $form_id; + } + if ($context != 'default') { + $form_id = $form_id . '_' . $context; + } + return $form_id . '_form'; +} + +/** + * Returns a rendered edit form for the given entity and context. + * + * @param EntityInterface|string $entity + * The entity being edited or the entity type to be created. + * @param $context + * (optional) The context for the form to be returned. + * + * @return + * A rendered edit form for the given entity. + */ +function entity_get_form($entity, $context = 'default') { + if (is_string($entity)) { + $entity = entity_create($entity, array()); + } + + $controller = entity_get_form_controller($entity->entityType(), $entity->bundle(), $context); + + $form_state['build_info']['callback'] = array($controller, 'build'); + $form_state['build_info']['base_form_id'] = $entity->entityType() . '_form'; + $form_state['build_info']['args'] = array($entity); + + $form_id = _entity_form_id($entity, $context); + return drupal_build_form($form_id, $form_state); +} + +/** * Copies submitted values to entity properties for simple entity forms. * * During the submission handling of an entity form's "Save", "Preview", and diff --git a/core/modules/entity/lib/Drupal/entity/Entity.php b/core/modules/entity/lib/Drupal/entity/Entity.php index eeaeee8..5b47228 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity.php +++ b/core/modules/entity/lib/Drupal/entity/Entity.php @@ -270,4 +270,13 @@ class Entity implements EntityInterface { return $this->isCurrentRevision; } + /** + * Implements EntityInterface::form(). + * + * @param $context + * (optional) The context for the form to be returned. + */ + public function form($context = 'default') { + return entity_get_form($this, $context); + } } diff --git a/core/modules/entity/lib/Drupal/entity/EntityFormController.php b/core/modules/entity/lib/Drupal/entity/EntityFormController.php new file mode 100644 index 0000000..5773f00 --- /dev/null +++ b/core/modules/entity/lib/Drupal/entity/EntityFormController.php @@ -0,0 +1,272 @@ +context = $context; + } + + /** + * Builds an entity edit form. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * A reference to a keyed array containing the current state of the form. + * @param $entity_type + * The type of the entity being edited. + * @param $entity + * The entity being edited. + * + * @return + * The array containing the complete form. + */ + public final function build(array $form, array &$form_state, EntityInterface $entity) { + // During initial form build, add the entity to the form controller for use + // during form building and processing. During a rebuild, use what is in the + // form state. + $controller = EntityFormController::getFormInstance($form_state); + $this->entity = !empty($controller) ? $controller->getEntity() : $entity; + + // Set the entity form controller in the form state. + $this->setFormInstance($form_state); + + // Retrieve the form array. + $form = $this->form($form, $form_state); + + // Retrieve the form actions. + $actions = $this->actionsElement($form, $form_state); + if (!empty($actions)) { + $form['actions'] = $actions; + } + + return $form; + } + + /** + * Returns the actual form array to be built. + * + * @see FormController::build(). + */ + protected function form(array $form, array &$form_state) { + // @todo Exploit the Property API to generate the default widgets for the + // entity properties. + $info = $this->entity->entityInfo(); + if (!empty($info['fieldable'])) { + field_attach_form($entity->entityType(), $this->entity, $form, $form_state, $this->getFormLangcode($form_state)); + } + return $form; + } + + /** + * Returns the action form element for the current entity form. + */ + protected final function actionsElement(array $form, array &$form_state) { + $element = $this->actions($form, $form_state); + + // We cannot delete an entity that has not been created yet. + if (!$this->entity->id()) { + unset($element['delete']); + } + elseif (isset($element['delete'])) { + // Move the delete action as last one, unless weights are explictly + // provided. + $delete = $element['delete']; + unset($element['delete']); + $element['delete'] = $delete; + } + + $count = 0; + $submit = array($this, 'submit'); + + foreach (element_children($element) as $action) { + $element[$action] += array( + '#type' => 'submit', + '#weight' => ++$count * 5, + '#validate' => array(), + '#submit' => array(), + ); + // Ensure we always preprocess submitted data before calling the actual + // submission handlers. + array_unshift($element[$action]['#submit'], $submit); + } + + if (!empty($element)) { + $element['#type'] = 'actions'; + } + + return $element; + } + + /** + * Returns an array of supported actions for the current entity form. + */ + protected function actions(array $form, array &$form_state) { + return array( + 'submit' => array( + '#value' => t('Save'), + '#validate' => array(array($this, 'validate')), + '#submit' => array(array($this, 'save')), + ), + 'delete' => array( + '#value' => t('Delete'), + // No need to validate the form when deleting the entity. + '#submit' => array(array($this, 'delete')), + ), + ); + } + + /** + * Validates the submitted form values. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function validate(array $form, array &$form_state) { + // @todo Exploit the Property API to validate the values submitted for the + // entity properties. + $info = $this->entity->entityInfo(); + if (!empty($info['fieldable'])) { + entity_form_field_validate($this->entity->entityType(), $form, $form_state); + } + + // @todo Remove this. + // Execute legacy global validation handlers. + unset($form_state['validate_handlers']); + form_execute_handlers('validate', $form, $form_state); + } + + /** + * Processes the submitted form values and updates the entity object. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function submit(array $form, array &$form_state) { + // @todo Exploit the Property API to process the submitted entity property + // values. + // @todo Do we really need this? + $this->prebuildEntity($form, $form_state); + // @todo Make entity_form_submit_build_entity() an entity form controller + // method. + entity_form_submit_build_entity($this->entity->entityType(), $this->$entity, $form, $form_state); + } + + /** + * Gives a chance to manipulate submitted data before building the entity. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * A reference to a keyed array containing the current state of the form. + */ + protected function prebuildEntity(array $form, array &$form_state) { + if (isset($form['#entity_prebuilders'])) { + foreach ($form['#entity_prebuilders'] as $function) { + $function($form, $form_state); + } + } + } + + /** + * Submit handler for the 'save' action. + * + * @param $form + * An associative array containing the structure of the form. + * @param $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function save(array $form, array &$form_state) {} + + /** + * Submit handler for the 'delete' action. + * + * @param array $form + * @param array $form_state + */ + public function delete(array $form, array &$form_state) {} + + /** + * Returns the code identifying the active form language. + */ + public function getFormLangcode($form_state) { + // @todo Introduce a new language type to use as the default language. + $language = $this->entity->language(); + return !empty($language->langcode) ? $language->langcode : NULL; + } + + /** + * Returns the entity being edited. + * + * @param $form_state + * The current form state. + * + * @return Drupal\entity\Entity + * The entity being edited in the current form. + */ + public function getEntity() { + return $this->entity; + } + + /** + * Returns an instance of the entity form controller. + * + * @param $form_state + * The current form state. + * @return Drupal\entity\EntityFormController + * An instance of the entity form controller associated with the current + * form. + */ + public static final function getFormInstance($form_state) { + return isset($form_state['entity_form_controller']) ? $form_state['entity_form_controller'] : FALSE; + } + + /** + * Associates the current form controller to the form being edited. + * + * @param $form_state + * The current form state. + * @param EntityFormController $controller + * The current entity form controller. + */ + protected final function setFormInstance(&$form_state) { + $form_state['entity_form_controller'] = $this; + } +} diff --git a/core/modules/entity/lib/Drupal/entity/EntityInterface.php b/core/modules/entity/lib/Drupal/entity/EntityInterface.php index 699c424..2479c28 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityInterface.php +++ b/core/modules/entity/lib/Drupal/entity/EntityInterface.php @@ -205,4 +205,10 @@ interface EntityInterface { */ public function isCurrentRevision(); + /** + * Returns a rendered entity edit form. + * + * @param $context + */ + public function form($context = 'default'); } diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index ca9bf7e..54ed906 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -592,7 +592,7 @@ function forum_field_storage_pre_update($entity_type, $entity, &$skip_fields) { /** * Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary(). */ -function forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) { +function forum_form_taxonomy_vocabulary_form_alter(&$form, &$form_state, $form_id) { $vid = variable_get('forum_nav_vocabulary', 0); if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) { $form['help_forum_vocab'] = array( @@ -609,9 +609,9 @@ function forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_i } /** - * Implements hook_form_FORM_ID_alter() for taxonomy_form_term(). + * Implements hook_form_FORM_ID_alter() for taxonomy_term_form(). */ -function forum_form_taxonomy_form_term_alter(&$form, &$form_state, $form_id) { +function forum_form_taxonomy_term_form_alter(&$form, &$form_state, $form_id) { $vid = variable_get('forum_nav_vocabulary', 0); if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) { // Hide multiple parents select from forum terms. diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php new file mode 100644 index 0000000..776e962 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -0,0 +1,77 @@ +entity); + return parent::form($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::actions() + */ + protected function actions(array $form, array &$form_state) { + $element = parent::actions($form, $form_state); + $node = $this->entity; + $preview_mode = variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL); + + $element['preview'] = array( + '#access' => $preview_mode != DRUPAL_DISABLED, + '#value' => t('Preview'), + '#submit' => array('node_form_build_preview'), + ); + + $element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])); + $element['delete']['#access'] = node_access('delete', $node); + + return $element; + } + + /** + * @see Drupal\entity\EntityFormController::validate() + */ + public function validate(array $form, array &$form_state) { + node_form_validate($form, $form_state); + parent::validate($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::submit() + */ + public function submit(array $form, array &$form_state) { + // Handle possible field translations first and then build the node from the + // submitted values. + node_field_language_form_submit($form, $form_state); + parent::submit($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::save() + */ + public function save(array $form, array &$form_state) { + node_form_submit($form, $form_state); + } + + /** + * @see Drupal\entity.EntityFormController::delete() + */ + public function delete(array $form, array &$form_state) { + node_form_delete_submit($form, $form_state); + } +} diff --git a/core/modules/node/node.module b/core/modules/node/node.module index b0c7126..93ef5bf 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -193,8 +193,11 @@ function node_entity_info() { $return = array( 'node' => array( 'label' => t('Node'), - 'controller class' => 'Drupal\node\NodeStorageController', 'entity class' => 'Drupal\node\Node', + 'controller class' => 'Drupal\node\NodeStorageController', + 'form controller class' => array( + 'default' => 'Drupal\node\NodeFormController', + ), 'base table' => 'node', 'revision table' => 'node_revision', 'uri callback' => 'node_uri', @@ -3641,21 +3644,6 @@ function node_content_form(Node $node, $form_state) { */ /** - * Implements hook_forms(). - * - * All node forms share the same form handler. - */ -function node_forms() { - $forms = array(); - if ($types = node_type_get_types()) { - foreach (array_keys($types) as $type) { - $forms[$type . '_node_form']['callback'] = 'node_form'; - } - } - return $forms; -} - -/** * Implements hook_action_info(). */ function node_action_info() { diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc index 5d60f7c..05d65e5 100644 --- a/core/modules/node/node.pages.inc +++ b/core/modules/node/node.pages.inc @@ -9,6 +9,8 @@ * @see node_menu() */ +use Drupal\entity\EntityFormController; + use Drupal\node\Node; /** @@ -19,7 +21,7 @@ use Drupal\node\Node; function node_page_edit($node) { $type_name = node_type_get_name($node); drupal_set_title(t('Edit @type @title', array('@type' => $type_name, '@title' => $node->title)), PASS_THROUGH); - return drupal_get_form($node->type . '_node_form', $node); + return $node->form(); } /** @@ -96,7 +98,7 @@ function node_add($type) { 'langcode' => node_type_get_default_langcode($type) )); drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), PASS_THROUGH); - $output = drupal_get_form($type . '_node_form', $node); + $output = $node->form(); return $output; } @@ -110,15 +112,14 @@ function node_add($type) { * @see node_form_submit_build_node() */ 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 + // The entity form controller 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 = clone $form_state['node']; + $node = clone EntityFormController::getFormInstance($form_state)->getEntity(); foreach ($form_state['values'] as $key => $value) { $node->{$key} = $value; } node_validate($node, $form, $form_state); - entity_form_field_validate('node', $form, $form_state); } /** @@ -328,43 +329,13 @@ function node_form($form, &$form_state, Node $node) { '#default_value' => $node->sticky, ); - // Add the buttons. - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])), - '#value' => t('Save'), - '#weight' => 5, - '#submit' => array('node_form_submit'), - ); - $form['actions']['preview'] = array( - '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_DISABLED, - '#type' => 'submit', - '#value' => t('Preview'), - '#weight' => 10, - '#submit' => array('node_form_build_preview'), - ); - if (!empty($node->nid) && node_access('delete', $node)) { - $form['actions']['delete'] = array( - '#type' => 'submit', - '#value' => t('Delete'), - '#weight' => 15, - '#submit' => array('node_form_delete_submit'), - ); - } // This form uses a button-level #submit handler for the form's main submit // action. node_form_submit() manually invokes all form-level #submit handlers // of the form. Without explicitly setting #submit, Form API would auto-detect // node_form_submit() as submit handler, but that is the button-level #submit - // handler for the 'Save' action. To maintain backwards compatibility, a - // #submit handler is auto-suggested for custom node type modules. - $form['#validate'][] = 'node_form_validate'; - if (!isset($form['#submit']) && function_exists($node->type . '_node_form_submit')) { - $form['#submit'][] = $node->type . '_node_form_submit'; - } + // handler for the 'Save' action. $form += array('#submit' => array()); - field_attach_form('node', $node, $form, $form_state, $node->langcode); return $form; } @@ -382,7 +353,7 @@ function node_form_delete_submit($form, &$form_state) { $destination = drupal_get_destination(); unset($_GET['destination']); } - $node = $form['#node']; + $node = EntityFormController::getFormInstance($form_state)->getEntity(); $form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination)); } @@ -494,9 +465,6 @@ function theme_node_preview($variables) { * @see node_form_submit_build_node() */ function node_form_submit($form, &$form_state) { - // Handle possible field translations first and then build the node from the - // submitted values. - node_field_language_form_submit($form, $form_state); $node = node_form_submit_build_node($form, $form_state); $insert = empty($node->nid); $node->save(); @@ -567,19 +535,7 @@ function node_field_language_form_submit($form, &$form_state) { * @see node_form_submit() */ function node_form_submit_build_node($form, &$form_state) { - // @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 = $form_state['node']; - entity_form_submit_build_entity('node', $node, $form, $form_state); - + $node = EntityFormController::getFormInstance($form_state)->getEntity(); node_submit($node); foreach (module_implements('node_submit') as $module) { $function = $module . '_node_submit'; diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module index 034c7c2..82973e4 100644 --- a/core/modules/openid/openid.module +++ b/core/modules/openid/openid.module @@ -732,6 +732,10 @@ function openid_authentication($response) { $form_state['values'] = array(); $form_state['values']['op'] = t('Create new account'); + $controller = entity_get_form_controller('user', NULL, 'register'); + $form_state['build_info']['callback'] = array($controller, 'build'); + $form_state['build_info']['base_form_id'] = 'user_form'; + $form_state['build_info']['args'] = array(entity_create('user', array()), 'register'); drupal_form_submit('user_register_form', $form_state); if (empty($form_state['user'])) { diff --git a/core/modules/path/path.module b/core/modules/path/path.module index d01c623..7b73e20 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -232,9 +232,9 @@ function path_node_predelete(Node $node) { } /** - * Implements hook_form_FORM_ID_alter() for taxonomy_form_term(). + * Implements hook_form_FORM_ID_alter() for taxonomy_term_form(). */ -function path_form_taxonomy_form_term_alter(&$form, $form_state) { +function path_form_taxonomy_term_form_alter(&$form, $form_state) { // Make sure this does not show up on the delete confirmation form. if (empty($form_state['confirm_delete'])) { $path = (isset($form['#term']['tid']) ? path_load('taxonomy/term/' . $form['#term']['tid']) : array()); diff --git a/core/modules/poll/lib/Drupal/poll/PollFormController.php b/core/modules/poll/lib/Drupal/poll/PollFormController.php new file mode 100644 index 0000000..0ab5a2f --- /dev/null +++ b/core/modules/poll/lib/Drupal/poll/PollFormController.php @@ -0,0 +1,18 @@ + LANGUAGE_NOT_SPECIFIED, )); $node2 = clone($node1); - $return['node_form_1'] = drupal_get_form('page_node_form', $node1); - $return['node_form_2'] = drupal_get_form('page_node_form', $node2); + $return['node_form_1'] = entity_get_form($node1); + $return['node_form_2'] = entity_get_form($node2); return $return; } diff --git a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module index 0ec6da6..00e6637 100644 --- a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module +++ b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module @@ -59,16 +59,14 @@ function taxonomy_test_taxonomy_term_delete(Term $term) { /** * Implements hook_form_alter(). */ -function taxonomy_test_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'taxonomy_form_term') { - $antonym = taxonomy_test_get_antonym($form['#term']['tid']); - $form['advanced']['antonym'] = array( - '#type' => 'textfield', - '#title' => t('Antonym'), - '#default_value' => !empty($antonym) ? $antonym : '', - '#description' => t('Antonym of this term.') - ); - } +function taxonomy_test_form_taxonomy_term_form_alter(&$form, $form_state, $form_id) { + $antonym = taxonomy_test_get_antonym($form['#term']['tid']); + $form['advanced']['antonym'] = array( + '#type' => 'textfield', + '#title' => t('Antonym'), + '#default_value' => !empty($antonym) ? $antonym : '', + '#description' => t('Antonym of this term.') + ); } /** diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php new file mode 100644 index 0000000..a7ff69a --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php @@ -0,0 +1,55 @@ +entity); + return parent::form($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::validate() + */ + public function validate(array $form, array &$form_state) { + parent::validate($form, $form_state); + taxonomy_term_form_validate($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::submit() + */ + public function submit(array $form, array &$form_state) { + parent::submit($form, $form_state); + taxonomy_term_form_submit_build_taxonomy_term($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::save() + */ + public function save(array $form, array &$form_state) { + taxonomy_term_form_submit($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::delete() + */ + public function delete(array $form, array &$form_state) { + taxonomy_term_form_delete_submit($form, $form_state); + } +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index d041d16..a14cff8 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -278,7 +278,7 @@ class TermTest extends TaxonomyTestBase { 'description[value]' => $this->randomName(100), ); // Explicitly set the parents field to 'root', to ensure that - // taxonomy_form_term_submit() handles the invalid term ID correctly. + // taxonomy_term_form_submit() handles the invalid term ID correctly. $edit['parent[]'] = array(0); // Create the term to edit. @@ -329,7 +329,7 @@ class TermTest extends TaxonomyTestBase { $this->drupalGet('taxonomy/term/' . $term->tid . '/feed'); // Check that the term edit page does not try to interpret additional path - // components as arguments for taxonomy_form_term(). + // components as arguments for taxonomy_term_form(). $this->drupalGet('taxonomy/term/' . $term->tid . '/edit/' . $this->randomName()); // Delete the term. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php new file mode 100644 index 0000000..7a7536b --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php @@ -0,0 +1,62 @@ +entity); + return parent::form($form, $form_state); + } + + /** + * Returns an array of supported actions for the current entity form. + */ + protected function actions(array $form, array &$form_state) { + // If we are displaying the delete confirmation skip the regular actions. + return empty($form_state['confirm_delete']) ? parent::actions($form, $form_state) : array(); + } + + /** + * @see Drupal\entity\EntityFormController::validate() + */ + public function validate(array $form, array &$form_state) { + parent::validate($form, $form_state); + taxonomy_form_vocabulary_validate($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::submit() + */ + public function submit(array $form, array &$form_state) { + if ($form_state['triggering_element']['#value'] == t('Delete')) { + // Rebuild the form to confirm vocabulary deletion. + $form_state['rebuild'] = TRUE; + $form_state['confirm_delete'] = TRUE; + } + else { + parent::submit($form, $form_state); + } + } + + /** + * @see Drupal\entity\EntityFormController::save() + */ + public function save(array $form, array &$form_state) { + taxonomy_form_vocabulary_submit($form, $form_state); + } +} diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index be020a6..6d44036 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -7,6 +7,7 @@ use Drupal\taxonomy\Term; use Drupal\taxonomy\Vocabulary; +use Drupal\entity\EntityFormController; /** * Form builder to list and manage vocabularies. @@ -167,13 +168,9 @@ function taxonomy_form_vocabulary($form, &$form_state, Vocabulary $vocabulary = '#value' => '0', ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save')); if (isset($vocabulary->vid)) { - $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete')); $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid); } - $form['#validate'][] = 'taxonomy_form_vocabulary_validate'; return $form; } @@ -207,15 +204,7 @@ function taxonomy_form_vocabulary_validate($form, &$form_state) { * @see taxonomy_form_vocabulary_validate() */ function taxonomy_form_vocabulary_submit($form, &$form_state) { - if ($form_state['triggering_element']['#value'] == t('Delete')) { - // Rebuild the form to confirm vocabulary deletion. - $form_state['rebuild'] = TRUE; - $form_state['confirm_delete'] = TRUE; - return; - } - - $vocabulary = $form_state['vocabulary']; - entity_form_submit_build_entity('taxonomy_vocabulary', $vocabulary, $form, $form_state); + $vocabulary = EntityFormController::getFormInstance($form_state)->getEntity(); // Prevent leading and trailing spaces in vocabulary names. $vocabulary->name = trim($vocabulary->name); @@ -643,6 +632,14 @@ function theme_taxonomy_overview_terms($variables) { } /** + * Returns a rendered edit form to create a new term associated to the given vocabulary. + */ +function taxonomy_term_add($vocabulary) { + $term = entity_create('taxonomy_term', array('vid' => $vocabulary->vid, 'vocabulary_machine_name' => $vocabulary->machine_name)); + return $term->form(); +} + +/** * Form function for the term edit form. * * @param Drupal\taxonomy\Term|null $term @@ -653,10 +650,10 @@ function theme_taxonomy_overview_terms($variables) { * the term is omitted. * * @ingroup forms - * @see taxonomy_form_term_validate() - * @see taxonomy_form_term_submit() + * @see taxonomy_term_form_validate() + * @see taxonomy_term_form_submit() */ -function taxonomy_form_term($form, &$form_state, Term $term = NULL, Vocabulary $vocabulary = NULL) { +function taxonomy_term_form($form, &$form_state, Term $term = NULL, Vocabulary $vocabulary = NULL) { // 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. @@ -710,8 +707,6 @@ function taxonomy_form_term($form, &$form_state, Term $term = NULL, Vocabulary $ '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name, ); - field_attach_form('taxonomy_term', $term, $form, $form_state); - $form['relations'] = array( '#type' => 'fieldset', '#title' => t('Relations'), @@ -770,23 +765,7 @@ function taxonomy_form_term($form, &$form_state, Term $term = NULL, Vocabulary $ '#value' => $term->tid, ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#weight' => 5, - ); - - if ($term->tid) { - $form['actions']['delete'] = array( - '#type' => 'submit', - '#value' => t('Delete'), - '#access' => taxonomy_term_access('delete', $term), - '#weight' => 10, - '#submit' => array('taxonomy_form_term_delete_submit'), - ); - } - else { + if (empty($term->tid)) { $form_state['redirect'] = current_path(); } @@ -796,11 +775,9 @@ function taxonomy_form_term($form, &$form_state, Term $term = NULL, Vocabulary $ /** * Validation handler for the term form. * - * @see taxonomy_form_term() + * @see taxonomy_term_form() */ -function taxonomy_form_term_validate($form, &$form_state) { - entity_form_field_validate('taxonomy_term', $form, $form_state); - +function taxonomy_term_form_validate($form, &$form_state) { // Ensure numeric values. if (isset($form_state['values']['weight']) && !is_numeric($form_state['values']['weight'])) { form_set_error('weight', t('Weight value must be numeric.')); @@ -810,10 +787,10 @@ function taxonomy_form_term_validate($form, &$form_state) { /** * Submit handler to insert or update a term. * - * @see taxonomy_form_term() + * @see taxonomy_term_form() */ -function taxonomy_form_term_submit($form, &$form_state) { - $term = taxonomy_form_term_submit_build_taxonomy_term($form, $form_state); +function taxonomy_term_form_submit($form, &$form_state) { + $term = EntityFormController::getFormInstance($form_state)->getEntity(); $status = taxonomy_term_save($term); switch ($status) { @@ -856,9 +833,8 @@ function taxonomy_form_term_submit($form, &$form_state) { /** * Updates the form state's term entity by processing this submission's values. */ -function taxonomy_form_term_submit_build_taxonomy_term($form, &$form_state) { - $term = $form_state['term']; - entity_form_submit_build_entity('taxonomy_term', $term, $form, $form_state); +function taxonomy_term_form_submit_build_taxonomy_term($form, &$form_state) { + $term = EntityFormController::getFormInstance($form_state)->getEntity(); // Prevent leading and trailing spaces in term names. $term->name = trim($term->name); @@ -871,18 +847,18 @@ function taxonomy_form_term_submit_build_taxonomy_term($form, &$form_state) { } /** - * Form submission handler for the 'Delete' button for taxonomy_form_term(). + * Form submission handler for the 'Delete' button for taxonomy_term_form(). * - * @see taxonomy_form_term_validate() - * @see taxonomy_form_term_submit() + * @see taxonomy_term_form_validate() + * @see taxonomy_term_form_submit() */ -function taxonomy_form_term_delete_submit($form, &$form_state) { +function taxonomy_term_form_delete_submit($form, &$form_state) { $destination = array(); if (isset($_GET['destination'])) { $destination = drupal_get_destination(); unset($_GET['destination']); } - $term = $form['#term']; + $term = EntityFormController::getFormInstance($form_state)->getEntity(); $form_state['redirect'] = array('taxonomy/term/' . $term['tid'] . '/delete', array('query' => $destination)); } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 6697a0a..85c1a83 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -113,6 +113,9 @@ function taxonomy_entity_info() { 'label' => t('Taxonomy term'), 'entity class' => 'Drupal\taxonomy\Term', 'controller class' => 'Drupal\taxonomy\TermStorageController', + 'form controller class' => array( + 'default' => 'Drupal\taxonomy\TermFormController', + ), 'base table' => 'taxonomy_term_data', 'uri callback' => 'taxonomy_term_uri', 'fieldable' => TRUE, @@ -149,6 +152,9 @@ function taxonomy_entity_info() { 'label' => t('Taxonomy vocabulary'), 'entity class' => 'Drupal\taxonomy\Vocabulary', 'controller class' => 'Drupal\taxonomy\VocabularyStorageController', + 'form controller class' => array( + 'default' => 'Drupal\taxonomy\VocabularyFormController', + ), 'base table' => 'taxonomy_vocabulary', 'entity keys' => array( 'id' => 'vid', @@ -293,8 +299,8 @@ function taxonomy_menu() { ); $items['admin/structure/taxonomy/add'] = array( 'title' => 'Add vocabulary', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('taxonomy_form_vocabulary'), + 'page callback' => 'entity_get_form', + 'page arguments' => array('taxonomy_vocabulary'), 'access arguments' => array('administer taxonomy'), 'type' => MENU_LOCAL_ACTION, 'file' => 'taxonomy.admin.inc', @@ -315,10 +321,10 @@ function taxonomy_menu() { ); $items['taxonomy/term/%taxonomy_term/edit'] = array( 'title' => 'Edit', - 'page callback' => 'drupal_get_form', + 'page callback' => 'entity_get_form', // Pass a NULL argument to ensure that additional path components are not - // passed to taxonomy_form_term() as the vocabulary machine name argument. - 'page arguments' => array('taxonomy_form_term', 2, NULL), + // passed to taxonomy_term_form() as the vocabulary machine name argument. + 'page arguments' => array(2), 'access callback' => 'taxonomy_term_access', 'access arguments' => array('edit', 2), 'type' => MENU_LOCAL_TASK, @@ -368,8 +374,8 @@ function taxonomy_menu() { ); $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/edit'] = array( 'title' => 'Edit', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('taxonomy_form_vocabulary', 3), + 'page callback' => 'entity_get_form', + 'page arguments' => array(3), 'access arguments' => array('administer taxonomy'), 'type' => MENU_LOCAL_TASK, 'weight' => -10, @@ -378,8 +384,8 @@ function taxonomy_menu() { $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add'] = array( 'title' => 'Add term', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('taxonomy_form_term', NULL, 3), + 'page callback' => 'taxonomy_term_add', + 'page arguments' => array(3), 'access arguments' => array('administer taxonomy'), 'type' => MENU_LOCAL_ACTION, 'file' => 'taxonomy.admin.inc', diff --git a/core/modules/user/lib/Drupal/user/ProfileFormController.php b/core/modules/user/lib/Drupal/user/ProfileFormController.php new file mode 100644 index 0000000..9d20354 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php @@ -0,0 +1,76 @@ +entity; + + // @todo Actually the cancel action can be assimilated to the delete one: we + // should alter it instead of providing a new one. + unset($element['delete']); + + $element['cancel'] = array( + '#type' => 'submit', + '#value' => t('Cancel account'), + '#submit' => array('user_edit_cancel_submit'), + '#access' => $account->uid > 1 && (($account->uid == $GLOBALS['user']->uid && user_access('cancel account')) || user_access('administer users')), + ); + + return $element; + } + + /** + * @see Drupal\entity\EntityFormController::submit() + */ + public function submit(array $form, array &$form_state) { + // @todo Consider moving this into the parent method. + // Remove unneeded values. + form_state_values_clean($form_state); + parent::submit($form, $form_state); + } + + /** + * @see Drupal\entity\EntityFormController::save() + */ + public function save(array $form, array &$form_state) { + $account = $this->entity; + $account->save(); + $form_state['values']['uid'] = $account->id(); + + // Clear the page cache because pages can contain usernames and/or profile + // information: + cache_invalidate(array('content' => TRUE)); + + drupal_set_message(t('The changes have been saved.')); + } +} diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php new file mode 100644 index 0000000..5205a04 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php @@ -0,0 +1,62 @@ +uid; } + + /** + * Implements Drupal\entity\Entity::form() + */ + public function form($context = 'profile') { + return parent::form($context); + } } diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc index 58b0218..f044c64 100644 --- a/core/modules/user/user.admin.inc +++ b/core/modules/user/user.admin.inc @@ -11,7 +11,7 @@ function user_admin($callback_arg = '') { switch ($op) { case t('Create new account'): case 'create': - $build['user_register'] = drupal_get_form('user_register_form'); + $build['user_register'] = entity_get_form('user', 'register'); break; default: if (!empty($_POST['accounts']) && isset($_POST['operation']) && ($_POST['operation'] == 'cancel')) { diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 2250110..980ee11 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1,5 +1,7 @@ array( 'label' => t('User'), + 'entity class' => 'Drupal\user\User', 'controller class' => 'Drupal\user\UserStorageController', + 'form controller class' => array( + 'profile' => 'Drupal\user\ProfileFormController', + 'register' => 'Drupal\user\RegisterFormController', + ), 'base table' => 'users', 'uri callback' => 'user_uri', 'label callback' => 'user_label', 'fieldable' => TRUE, - 'entity class' => 'Drupal\user\User', 'entity keys' => array( 'id' => 'uid', ), @@ -711,8 +717,8 @@ function user_account_form(&$form, &$form_state) { global $user; $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE); - $account = $form['#user']; - $register = ($form['#user']->uid > 0 ? FALSE : TRUE); + $account = EntityFormController::getFormInstance($form_state)->getEntity(); + $register = ($account->uid > 0 ? FALSE : TRUE); $admin = user_access('administer users'); @@ -1497,8 +1503,8 @@ function user_menu() { $items['user/register'] = array( 'title' => 'Create new account', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('user_register_form'), + 'page callback' => 'entity_get_form', + 'page arguments' => array('user', 'register'), 'access callback' => 'user_register_access', 'type' => MENU_LOCAL_TASK, ); @@ -1657,8 +1663,8 @@ function user_menu() { $items['user/%user/edit'] = array( 'title' => 'Edit', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('user_profile_form', 1), + 'page callback' => 'entity_get_form', + 'page arguments' => array(1, 'profile'), 'access callback' => 'user_edit_access', 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, @@ -3497,7 +3503,7 @@ function user_form_field_ui_field_edit_form_submit($form, &$form_state) { * @see user_account_form_validate() * @see user_register_submit() */ -function user_register_form($form, &$form_state) { +function _user_register_form($form, &$form_state) { global $user; $admin = user_access('administer users'); @@ -3515,7 +3521,7 @@ function user_register_form($form, &$form_state) { drupal_goto('user/' . $user->uid); } - $form['#user'] = entity_create('user', array()); + $form['#user'] = EntityFormController::getFormInstance($form_state)->getEntity(); $form['#attached']['library'][] = array('system', 'jquery.cookie'); $form['#attributes']['class'][] = 'user-info-from-cookie'; @@ -3538,54 +3544,21 @@ function user_register_form($form, &$form_state) { $form_state['redirect'] = current_path(); } - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Create new account'), - ); - - $form['#validate'][] = 'user_register_validate'; - // Add the final user registration form submit handler. - $form['#submit'][] = 'user_register_submit'; - return $form; } /** - * Validation function for the user registration form. - */ -function user_register_validate($form, &$form_state) { - entity_form_field_validate('user', $form, $form_state); -} - -/** * Submit handler for the user registration form. * - * This function is shared by the installation form and the normal registration form, - * which is why it can't be in the user.pages.inc file. - * - * @see user_register_form() + * This function is shared by the installation form and the normal registration + * form, which is why it can't be in the user.pages.inc file. */ function user_register_submit($form, &$form_state) { + $account = EntityFormController::getFormInstance($form_state)->getEntity(); + $pass = $account->pass; $admin = $form_state['values']['administer_users']; - - if (!variable_get('user_email_verification', TRUE) || $admin) { - $pass = $form_state['values']['pass']; - } - else { - $pass = user_password(); - } $notify = !empty($form_state['values']['notify']); - // Remove unneeded values. - form_state_values_clean($form_state); - - $form_state['values']['pass'] = $pass; - $form_state['values']['init'] = $form_state['values']['mail']; - - $account = $form['#user']; - - entity_form_submit_build_entity('user', $account, $form, $form_state); $status = $account->save(); // Terminate if an error occurred while saving the account. diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc index 6cdfd25..ff36675 100644 --- a/core/modules/user/user.pages.inc +++ b/core/modules/user/user.pages.inc @@ -5,6 +5,8 @@ * User page callback file for the user module. */ +use Drupal\entity\EntityFormController; + use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -202,86 +204,6 @@ function template_preprocess_user_profile(&$variables) { } /** - * Form builder; edit a user account. - * - * @ingroup forms - * @see user_account_form() - * @see user_account_form_validate() - * @see user_profile_form_validate() - * @see user_profile_form_submit() - * @see user_cancel_confirm_form_submit() - */ -function user_profile_form($form, &$form_state, $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; - - - user_account_form($form, $form_state); - // Attach field widgets. - field_attach_form('user', $account, $form, $form_state); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - $form['actions']['cancel'] = array( - '#type' => 'submit', - '#value' => t('Cancel account'), - '#submit' => array('user_edit_cancel_submit'), - '#access' => $account->uid > 1 && (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')), - ); - - $form['#validate'][] = 'user_profile_form_validate'; - // Add the final user profile form submit handler. - $form['#submit'][] = 'user_profile_form_submit'; - - return $form; -} - -/** - * Validation function for the user account and profile editing form. - */ -function user_profile_form_validate($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_state['user']; - // Remove unneeded values. - form_state_values_clean($form_state); - - entity_form_submit_build_entity('user', $account, $form, $form_state); - $account->save(); - $form_state['values']['uid'] = $account->uid; - - if (!empty($edit['pass'])) { - // Remove the password reset tag since a new password was saved. - unset($_SESSION['pass_reset_'. $account->uid]); - } - // Clear the page cache because pages can contain usernames and/or profile information: - cache_invalidate(array('content' => TRUE)); - - drupal_set_message(t('The changes have been saved.')); -} - -/** * Submit function for the 'Cancel account' button on the user edit form. */ function user_edit_cancel_submit($form, &$form_state) {