diff --git a/core/includes/form.inc b/core/includes/form.inc
index 7498ea5..47c14ab 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -159,6 +159,8 @@ function drupal_get_form($form_id) {
* - build_info: Internal. An associative array of information stored by Form
* API that is necessary to build and rebuild the form from cache when the
* original context may no longer be available:
+ * - callback: The actual callback to be used to retrieve the form array. If
+ * none is provided $form_id is used instead. Can be any callable type.
* - args: A list of arguments to pass to the form constructor.
* - files: An optional array defining include files that need to be loaded
* for building the form. Each array entry may be the path to a file or
@@ -266,11 +268,11 @@ function drupal_get_form($form_id) {
* ones used by Form API internals) for this kind of storage. The
* recommended way to ensure that the chosen key doesn't conflict with ones
* used by the Form API or other modules is to use the module name as the
- * key name or a prefix for the key name. For example, the Node module uses
- * $form_state['node'] in node editing forms to store information about the
- * node being edited, and this information stays available across successive
- * clicks of the "Preview" button as well as when the "Save" button is
- * finally clicked.
+ * key name or a prefix for the key name. For example, the entity form
+ * controller classes use $form_state['entity'] in entity forms to store
+ * information about the entity being edited, and this information stays
+ * available across successive clicks of the "Preview" button (if available)
+ * as well as when the "Save" button is finally clicked.
* - buttons: A list containing copies of all submit and button elements in
* the form.
* - complete_form: A reference to the $form variable containing the complete
@@ -747,9 +749,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.
@@ -808,7 +814,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;
@@ -1469,7 +1475,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;
}
@@ -1812,7 +1818,7 @@ function form_builder($form_id, &$element, &$form_state) {
// checkboxes and files.
if (isset($element['#process']) && !$element['#processed']) {
foreach ($element['#process'] as $process) {
- $element = $process($element, $form_state, $form_state['complete_form']);
+ $element = call_user_func_array($process, array(&$element, &$form_state, &$form_state['complete_form']));
}
$element['#processed'] = TRUE;
}
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 4a3d6b9..2592137 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -596,7 +596,7 @@ function block_custom_block_save($edit, $delta) {
* Implements hook_form_FORM_ID_alter() for user_profile_form().
*/
function block_form_user_profile_form_alter(&$form, &$form_state) {
- $account = $form['#user'];
+ $account = $form_state['controller']->getEntity($form_state);
$rids = array_keys($account->roles);
$result = db_query("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.weight, b.module", array(':rids' => $rids));
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 86a5d10..9231082 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -423,7 +423,7 @@ function book_get_books() {
* @see book_pick_book_nojs_submit()
*/
function book_form_node_form_alter(&$form, &$form_state, $form_id) {
- $node = $form['#node'];
+ $node = $form_state['controller']->getEntity($form_state);
$access = user_access('administer book outlines');
if (!$access) {
if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) {
@@ -462,7 +462,8 @@ function book_form_node_form_alter(&$form, &$form_state, $form_id) {
* @see book_form_node_form_alter()
*/
function book_pick_book_nojs_submit($form, &$form_state) {
- $form_state['node']->book = $form_state['values']['book'];
+ $node = $form_state['controller']->getEntity($form_state);
+ $node->book = $form_state['values']['book'];
$form_state['rebuild'] = TRUE;
}
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index 0a59897..6210cc5 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -278,7 +278,7 @@ function comment_confirm_delete_page($cid) {
* @see confirm_form()
*/
function comment_confirm_delete($form, &$form_state, Comment $comment) {
- $form['#comment'] = $comment;
+ $form_state['comment'] = $comment;
// Always provide entity id in the same form key as in the entity edit form.
$form['cid'] = array('#type' => 'value', '#value' => $comment->cid);
return confirm_form(
@@ -295,7 +295,7 @@ function comment_confirm_delete($form, &$form_state, Comment $comment) {
* Form submission handler for comment_confirm_delete().
*/
function comment_confirm_delete_submit($form, &$form_state) {
- $comment = $form['#comment'];
+ $comment = $form_state['comment'];
// Delete the comment and its replies.
comment_delete($comment->cid);
drupal_set_message(t('The comment and all its replies have been deleted.'));
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 39faf90..6c37cfe 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -10,6 +10,7 @@
*/
use Drupal\node\Node;
+
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -106,6 +107,9 @@ function comment_entity_info() {
'uri callback' => 'comment_uri',
'fieldable' => TRUE,
'controller class' => 'Drupal\comment\CommentStorageController',
+ 'form controller class' => array(
+ 'default' => 'Drupal\comment\CommentFormController',
+ ),
'entity class' => 'Drupal\comment\Comment',
'entity keys' => array(
'id' => 'cid',
@@ -771,8 +775,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_add($node);
}
if ($additions) {
@@ -788,6 +791,21 @@ function comment_node_page_additions(Node $node) {
}
/**
+ * Returns a rendered form to comment the given node.
+ *
+ * @param Drupal\node\Node $node
+ * The node entity to be commented.
+ *
+ * @return array
+ * The renderable array for the comment addition form.
+ */
+function comment_add(Node $node, $pid = NULL) {
+ $values = array('nid' => $node->nid, 'pid' => $pid, 'node_type' => 'comment_node_' . $node->type);
+ $comment = entity_create('comment', $values);
+ return entity_get_form($comment);
+}
+
+/**
* Retrieves comments for a thread.
*
* @param Drupal\node\Node $node
@@ -1231,7 +1249,7 @@ function comment_form_node_type_form_alter(&$form, $form_state) {
* Implements hook_form_BASE_FORM_ID_alter().
*/
function comment_form_node_form_alter(&$form, $form_state) {
- $node = $form['#node'];
+ $node = $form_state['controller']->getEntity($form_state);
$form['comment_settings'] = array(
'#type' => 'fieldset',
'#access' => user_access('administer comments'),
@@ -1658,247 +1676,17 @@ 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;
-}
-
-/**
- * Form constructor for the basic commenting form.
- *
- * @see comment_form_validate()
- * @see comment_form_submit()
- * @see comment_form_build_preview()
- * @ingroup forms
- */
-function comment_form($form, &$form_state, Comment $comment) {
- global $user;
- $language_content = language(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);
- $form['#node'] = $node;
-
- // Use #comment-form as unique jump target, regardless of node type.
- $form['#id'] = drupal_html_id('comment_form');
- $form['#theme'] = array('comment_form__node_' . $node->type, 'comment_form');
-
- $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
- $is_admin = (!empty($comment->cid) && user_access('administer comments'));
-
- if (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
- $form['#attached']['library'][] = array('system', 'jquery.cookie');
- $form['#attributes']['class'][] = 'user-info-from-cookie';
- }
-
- // If not replying to a comment, use our dedicated page callback for new
- // comments on nodes.
- if (empty($comment->cid) && empty($comment->pid)) {
- $form['#action'] = url('comment/reply/' . $comment->nid);
- }
-
- if (isset($form_state['comment_preview'])) {
- $form += $form_state['comment_preview'];
- }
-
- $form['author'] = array(
- '#weight' => 10,
- );
- // Display author information in a fieldset for comment moderators.
- if ($is_admin) {
- $form['author'] += array(
- '#type' => 'fieldset',
- '#title' => t('Administration'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- );
- }
-
- // Prepare default values for form elements.
- if ($is_admin) {
- $author = (!$comment->uid && $comment->name ? $comment->name : $comment->registered_name);
- $status = (isset($comment->status) ? $comment->status : COMMENT_NOT_PUBLISHED);
- $date = (!empty($comment->date) ? $comment->date : format_date($comment->created, 'custom', 'Y-m-d H:i O'));
- }
- else {
- if ($user->uid) {
- $author = $user->name;
- }
- else {
- $author = ($comment->name ? $comment->name : '');
- }
- $status = (user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED);
- $date = '';
- }
-
- // Add the author name field depending on the current user.
- if ($is_admin) {
- $form['author']['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored by'),
- '#default_value' => $author,
- '#maxlength' => 60,
- '#size' => 30,
- '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
- '#autocomplete_path' => 'user/autocomplete',
- );
- }
- elseif ($user->uid) {
- $form['author']['_author'] = array(
- '#type' => 'item',
- '#title' => t('Your name'),
- '#markup' => theme('username', array('account' => $user)),
- );
- $form['author']['name'] = array(
- '#type' => 'value',
- '#value' => $author,
- );
- }
- else {
- $form['author']['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Your name'),
- '#default_value' => $author,
- '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
- '#maxlength' => 60,
- '#size' => 30,
- );
- }
-
- // Add author e-mail and homepage fields depending on the current user.
- $form['author']['mail'] = array(
- '#type' => 'email',
- '#title' => t('E-mail'),
- '#default_value' => $comment->mail,
- '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
- '#maxlength' => 64,
- '#size' => 30,
- '#description' => t('The content of this field is kept private and will not be shown publicly.'),
- '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
- );
- $form['author']['homepage'] = array(
- '#type' => 'url',
- '#title' => t('Homepage'),
- '#default_value' => $comment->homepage,
- '#maxlength' => 255,
- '#size' => 30,
- '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
- );
-
- // Add administrative comment publishing options.
- $form['author']['date'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored on'),
- '#default_value' => $date,
- '#maxlength' => 25,
- '#size' => 20,
- '#access' => $is_admin,
- );
- $form['author']['status'] = array(
- '#type' => 'radios',
- '#title' => t('Status'),
- '#default_value' => $status,
- '#options' => array(
- COMMENT_PUBLISHED => t('Published'),
- COMMENT_NOT_PUBLISHED => t('Not published'),
- ),
- '#access' => $is_admin,
- );
-
- $form['subject'] = array(
- '#type' => 'textfield',
- '#title' => t('Subject'),
- '#maxlength' => 64,
- '#default_value' => $comment->subject,
- '#access' => variable_get('comment_subject_field_' . $node->type, 1) == 1,
- );
-
- // Used for conditional validation of author fields.
- $form['is_anonymous'] = array(
- '#type' => 'value',
- '#value' => ($comment->cid ? !$comment->uid : !$user->uid),
- );
-
- // Add internal comment properties.
- foreach (array('cid', 'pid', 'nid', 'uid') as $key) {
- $form[$key] = array('#type' => 'value', '#value' => $comment->$key);
- }
- $form['node_type'] = array('#type' => 'value', '#value' => 'comment_node_' . $node->type);
-
- // Make the comment inherit the node language unless specifically set.
- $comment_langcode = $comment->langcode;
- if ($comment_langcode == LANGUAGE_NOT_SPECIFIED) {
- $comment_langcode = $language_content->langcode;
- }
-
- // Uses the language of the content as comment language.
- $form['langcode'] = array(
- '#type' => 'value',
- '#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;
-}
-
-/**
- * Form submission handler for the 'preview' button in comment_form().
- */
-function comment_form_build_preview($form, &$form_state) {
- $comment = comment_form_submit_build_comment($form, $form_state);
- $form_state['comment_preview'] = comment_preview($comment);
- $form_state['rebuild'] = TRUE;
+ return entity_get_form($comment);
}
/**
* Generates a comment preview.
*
* @param Drupal\comment\Comment $comment
- *
- * @see comment_form_build_preview()
*/
function comment_preview(Comment $comment) {
global $user;
-
- drupal_set_title(t('Preview comment'), PASS_THROUGH);
-
+ $preview_build = array();
$node = node_load($comment->nid);
if (!form_get_errors()) {
@@ -1928,7 +1716,7 @@ function comment_preview(Comment $comment) {
$comment_build = comment_view($comment, $node);
$comment_build['#weight'] = -100;
- $form['comment_preview'] = $comment_build;
+ $preview_build['comment_preview'] = $comment_build;
}
if ($comment->pid) {
@@ -1942,169 +1730,10 @@ function comment_preview(Comment $comment) {
$build = node_view($node);
}
- $form['comment_output_below'] = $build;
- $form['comment_output_below']['#weight'] = 100;
-
- return $form;
-}
-
-/**
- * Form validation handler for comment_form().
- *
- * @see comment_form_submit()
- */
-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']);
- $form_state['values']['uid'] = $account ? $account->uid : 0;
-
- if ($form_state['values']['date'] && strtotime($form_state['values']['date']) === FALSE) {
- form_set_error('date', t('You have to specify a valid date.'));
- }
- if ($form_state['values']['name'] && !$form_state['values']['is_anonymous'] && !$account) {
- form_set_error('name', t('You have to specify a valid author.'));
- }
- }
- elseif ($form_state['values']['is_anonymous']) {
- // Validate anonymous comment author fields (if given). If the (original)
- // author of this comment was an anonymous user, verify that no registered
- // user with this name exists.
- if ($form_state['values']['name']) {
- $query = db_select('users', 'u');
- $query->addField('u', 'uid', 'uid');
- $taken = $query
- ->condition('name', db_like($form_state['values']['name']), 'LIKE')
- ->countQuery()
- ->execute()
- ->fetchField();
- if ($taken) {
- form_set_error('name', t('The name you used belongs to a registered user.'));
- }
- }
- }
-}
-
-/**
- * Prepare a comment for submission.
- *
- * @param Drupal\comment\Comment $comment
- *
- */
-function comment_submit(Comment $comment) {
- if (empty($comment->date)) {
- $comment->date = 'now';
- }
- $comment->created = strtotime($comment->date);
- $comment->changed = REQUEST_TIME;
-
- // If the comment was posted by a registered user, assign the author's ID.
- // @todo Too fragile. Should be prepared and stored in comment_form() already.
- if (!$comment->is_anonymous && !empty($comment->name) && ($account = user_load_by_name($comment->name))) {
- $comment->uid = $account->uid;
- }
- // If the comment was posted by an anonymous user and no author name was
- // required, use "Anonymous" by default.
- if ($comment->is_anonymous && (!isset($comment->name) || $comment->name === '')) {
- $comment->name = variable_get('anonymous', t('Anonymous'));
- }
+ $preview_build['comment_output_below'] = $build;
+ $preview_build['comment_output_below']['#weight'] = 100;
- // 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_body = $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0];
- if (isset($comment_body['format'])) {
- $comment_text = check_markup($comment_body['value'], $comment_body['format']);
- }
- else {
- $comment_text = check_plain($comment_body['value']);
- }
- $comment->subject = truncate_utf8(trim(decode_entities(strip_tags($comment_text))), 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 $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()
- * @see comment_form_submit_build_comment()
- */
-function comment_form_submit($form, &$form_state) {
- $node = node_load($form_state['values']['nid']);
- $comment = comment_form_submit_build_comment($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 (user_is_anonymous()) {
- user_cookie_save(array_intersect_key($form_state['values'], array_flip(array('name', 'mail', 'homepage'))));
- }
-
- comment_save($comment);
- $form_state['values']['cid'] = $comment->cid;
-
- // Add an entry to the watchdog log.
- watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject), WATCHDOG_NOTICE, l(t('view'), 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)));
-
- // Explain the approval queue if necessary.
- if ($comment->status == COMMENT_NOT_PUBLISHED) {
- if (!user_access('administer comments')) {
- drupal_set_message(t('Your comment has been queued for review by site administrators and will be published after approval.'));
- }
- }
- else {
- drupal_set_message(t('Your comment has been posted.'));
- }
- $query = array();
- // Find the current display page for this comment.
- $page = comment_get_display_page($comment->cid, $node->type);
- if ($page > 0) {
- $query['page'] = $page;
- }
- // Redirect to the newly posted comment.
- $redirect = array('node/' . $node->nid, array('query' => $query, 'fragment' => 'comment-' . $comment->cid));
- }
- else {
- watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject), WATCHDOG_WARNING);
- drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject)), 'error');
- // Redirect the user to the node they are commenting on.
- $redirect = 'node/' . $node->nid;
- }
- $form_state['redirect'] = $redirect;
- // Clear the block and page caches so that anonymous users see the comment
- // they have posted.
- cache_invalidate(array('content' => TRUE));
+ return $preview_build;
}
/**
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index ae50379..ed91d23 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_add($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_add($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..9b3094e
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -0,0 +1,378 @@
+nid);
+ $form_state['comment']['node'] = $node;
+
+ // Use #comment-form as unique jump target, regardless of node type.
+ $form['#id'] = drupal_html_id('comment_form');
+ $form['#theme'] = array('comment_form__node_' . $node->type, 'comment_form');
+
+ $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
+ $is_admin = (!empty($comment->cid) && user_access('administer comments'));
+
+ if (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
+ $form['#attached']['library'][] = array('system', 'jquery.cookie');
+ $form['#attributes']['class'][] = 'user-info-from-cookie';
+ }
+
+ // If not replying to a comment, use our dedicated page callback for new
+ // comments on nodes.
+ if (empty($comment->cid) && empty($comment->pid)) {
+ $form['#action'] = url('comment/reply/' . $comment->nid);
+ }
+
+ if (isset($form_state['comment_preview'])) {
+ $form += $form_state['comment_preview'];
+ }
+
+ $form['author'] = array(
+ '#weight' => 10,
+ );
+ // Display author information in a fieldset for comment moderators.
+ if ($is_admin) {
+ $form['author'] += array(
+ '#type' => 'fieldset',
+ '#title' => t('Administration'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+ }
+
+ // Prepare default values for form elements.
+ if ($is_admin) {
+ $author = (!$comment->uid && $comment->name ? $comment->name : $comment->registered_name);
+ $status = (isset($comment->status) ? $comment->status : COMMENT_NOT_PUBLISHED);
+ $date = (!empty($comment->date) ? $comment->date : format_date($comment->created, 'custom', 'Y-m-d H:i O'));
+ }
+ else {
+ if ($user->uid) {
+ $author = $user->name;
+ }
+ else {
+ $author = ($comment->name ? $comment->name : '');
+ }
+ $status = (user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED);
+ $date = '';
+ }
+
+ // Add the author name field depending on the current user.
+ if ($is_admin) {
+ $form['author']['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Authored by'),
+ '#default_value' => $author,
+ '#maxlength' => 60,
+ '#size' => 30,
+ '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
+ '#autocomplete_path' => 'user/autocomplete',
+ );
+ }
+ elseif ($user->uid) {
+ $form['author']['_author'] = array(
+ '#type' => 'item',
+ '#title' => t('Your name'),
+ '#markup' => theme('username', array('account' => $user)),
+ );
+
+ $form['author']['name'] = array(
+ '#type' => 'value',
+ '#value' => $author,
+ );
+ }
+ else {
+ $form['author']['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Your name'),
+ '#default_value' => $author,
+ '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
+ '#maxlength' => 60,
+ '#size' => 30,
+ );
+ }
+
+ // Add author e-mail and homepage fields depending on the current user.
+ $form['author']['mail'] = array(
+ '#type' => 'email',
+ '#title' => t('E-mail'),
+ '#default_value' => $comment->mail,
+ '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
+ '#maxlength' => 64,
+ '#size' => 30,
+ '#description' => t('The content of this field is kept private and will not be shown publicly.'),
+ '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
+ );
+
+ $form['author']['homepage'] = array(
+ '#type' => 'url',
+ '#title' => t('Homepage'),
+ '#default_value' => $comment->homepage,
+ '#maxlength' => 255,
+ '#size' => 30,
+ '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
+ );
+
+ // Add administrative comment publishing options.
+ $form['author']['date'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Authored on'),
+ '#default_value' => $date,
+ '#maxlength' => 25,
+ '#size' => 20,
+ '#access' => $is_admin,
+ );
+
+ $form['author']['status'] = array(
+ '#type' => 'radios',
+ '#title' => t('Status'),
+ '#default_value' => $status,
+ '#options' => array(
+ COMMENT_PUBLISHED => t('Published'),
+ COMMENT_NOT_PUBLISHED => t('Not published'),
+ ),
+ '#access' => $is_admin,
+ );
+
+ $form['subject'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Subject'),
+ '#maxlength' => 64,
+ '#default_value' => $comment->subject,
+ '#access' => variable_get('comment_subject_field_' . $node->type, 1) == 1,
+ );
+
+ // Used for conditional validation of author fields.
+ $form['is_anonymous'] = array(
+ '#type' => 'value',
+ '#value' => ($comment->cid ? !$comment->uid : !$user->uid),
+ );
+
+ // Add internal comment properties.
+ foreach (array('cid', 'pid', 'nid', 'uid') as $key) {
+ $form[$key] = array('#type' => 'value', '#value' => $comment->$key);
+ }
+ $form['node_type'] = array('#type' => 'value', '#value' => 'comment_node_' . $node->type);
+
+ // Make the comment inherit the node language unless specifically set.
+ $comment_langcode = $comment->langcode;
+ if ($comment_langcode == LANGUAGE_NOT_SPECIFIED) {
+ $comment_langcode = $language_content->langcode;
+ }
+
+ // Uses the language of the content as comment language.
+ $form['langcode'] = array(
+ '#type' => 'value',
+ '#value' => $comment_langcode,
+ );
+
+ // Attach fields.
+ $comment->node_type = 'comment_node_' . $node->type;
+
+ return parent::form($form, $form_state, $comment);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::actions().
+ */
+ protected function actions(array $form, array &$form_state) {
+ $element = parent::actions($form, $form_state);
+ $comment = $this->getEntity($form_state);
+ $node = $form_state['comment']['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,
+ '#validate' => array(
+ array($this, 'validate'),
+ ),
+ '#submit' => array(
+ array($this, 'submit'),
+ array($this, 'preview'),
+ ),
+ );
+
+ $element['#weight'] = $form['comment_body']['#weight'] + 0.01;
+
+ return $element;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::validate().
+ */
+ public function validate(array $form, array &$form_state) {
+ parent::validate($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']);
+ $form_state['values']['uid'] = $account ? $account->uid : 0;
+
+ if ($form_state['values']['date'] && strtotime($form_state['values']['date']) === FALSE) {
+ form_set_error('date', t('You have to specify a valid date.'));
+ }
+ if ($form_state['values']['name'] && !$form_state['values']['is_anonymous'] && !$account) {
+ form_set_error('name', t('You have to specify a valid author.'));
+ }
+ }
+ elseif ($form_state['values']['is_anonymous']) {
+ // Validate anonymous comment author fields (if given). If the (original)
+ // author of this comment was an anonymous user, verify that no registered
+ // user with this name exists.
+ if ($form_state['values']['name']) {
+ $query = db_select('users', 'u');
+ $query->addField('u', 'uid', 'uid');
+ $taken = $query
+ ->condition('name', db_like($form_state['values']['name']), 'LIKE')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ if ($taken) {
+ form_set_error('name', t('The name you used belongs to a registered user.'));
+ }
+ }
+ }
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ $comment = parent::submit($form, $form_state);
+
+ if (empty($comment->date)) {
+ $comment->date = 'now';
+ }
+ $comment->created = strtotime($comment->date);
+ $comment->changed = REQUEST_TIME;
+
+ // If the comment was posted by a registered user, assign the author's ID.
+ // @todo Too fragile. Should be prepared and stored in comment_form()
+ // already.
+ if (!$comment->is_anonymous && !empty($comment->name) && ($account = user_load_by_name($comment->name))) {
+ $comment->uid = $account->uid;
+ }
+ // If the comment was posted by an anonymous user and no author name was
+ // required, use "Anonymous" by default.
+ if ($comment->is_anonymous && (!isset($comment->name) || $comment->name === '')) {
+ $comment->name = variable_get('anonymous', t('Anonymous'));
+ }
+
+ // 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_body = $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0];
+ if (isset($comment_body['format'])) {
+ $comment_text = check_markup($comment_body['value'], $comment_body['format']);
+ }
+ else {
+ $comment_text = check_plain($comment_body['value']);
+ }
+ $comment->subject = truncate_utf8(trim(decode_entities(strip_tags($comment_text))), 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 $comment;
+ }
+
+ /**
+ * Form submission handler for the 'preview' 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 preview(array $form, array &$form_state) {
+ $comment = $this->getEntity($form_state);
+ drupal_set_title(t('Preview comment'), PASS_THROUGH);
+ $form_state['comment_preview'] = comment_preview($comment);
+ $form_state['rebuild'] = TRUE;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $node = node_load($form_state['values']['nid']);
+ $comment = $this->getEntity($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 (user_is_anonymous()) {
+ user_cookie_save(array_intersect_key($form_state['values'], array_flip(array('name', 'mail', 'homepage'))));
+ }
+
+ comment_save($comment);
+ $form_state['values']['cid'] = $comment->cid;
+
+ // Add an entry to the watchdog log.
+ watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject), WATCHDOG_NOTICE, l(t('view'), 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)));
+
+ // Explain the approval queue if necessary.
+ if ($comment->status == COMMENT_NOT_PUBLISHED) {
+ if (!user_access('administer comments')) {
+ drupal_set_message(t('Your comment has been queued for review by site administrators and will be published after approval.'));
+ }
+ }
+ else {
+ drupal_set_message(t('Your comment has been posted.'));
+ }
+ $query = array();
+ // Find the current display page for this comment.
+ $page = comment_get_display_page($comment->cid, $node->type);
+ if ($page > 0) {
+ $query['page'] = $page;
+ }
+ // Redirect to the newly posted comment.
+ $redirect = array('node/' . $node->nid, array('query' => $query, 'fragment' => 'comment-' . $comment->cid));
+ }
+ else {
+ watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject), WATCHDOG_WARNING);
+ drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject)), 'error');
+ // Redirect the user to the node they are commenting on.
+ $redirect = 'node/' . $node->nid;
+ }
+ $form_state['redirect'] = $redirect;
+ // Clear the block and page caches so that anonymous users see the comment
+ // they have posted.
+ cache_invalidate(array('content' => TRUE));
+ }
+}
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 245c125..1ba70b9 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -221,7 +221,7 @@ function contact_form_user_profile_form_alter(&$form, &$form_state) {
'#weight' => 5,
'#collapsible' => TRUE,
);
- $account = $form['#user'];
+ $account = $form_state['controller']->getEntity($form_state);
$form['contact']['contact'] = array(
'#type' => 'checkbox',
'#title' => t('Personal contact form'),
diff --git a/core/modules/entity/entity.api.php b/core/modules/entity/entity.api.php
index 5359376..98e32b8 100644
--- a/core/modules/entity/entity.api.php
+++ b/core/modules/entity/entity.api.php
@@ -27,6 +27,13 @@
* The class has to implement the
* Drupal\entity\EntityStorageControllerInterface interface. Leave blank
* to use the Drupal\entity\DatabaseStorageController implementation.
+ * - form controller class: An associative array where the keys are the names
+ * of the different form operations (such as creation, editing or deletion)
+ * and the values are the names of the controller classes. To facilitate
+ * supporting the case where an entity form varies only slightly between
+ * different operations, the name of the operation is passed also to the
+ * constructor of the form controller class. This way, one class can be used
+ * for multiple entity forms.
* - base table: (used by Drupal\entity\DatabaseStorageController) The
* name of the entity type's base table.
* - static cache: (used by Drupal\entity\DatabaseStorageController)
@@ -139,6 +146,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 3d37a2a..2f4714f 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -5,6 +5,8 @@
* Entity API for handling entities like nodes or users.
*/
+use \InvalidArgumentException;
+
use Drupal\entity\EntityFieldQuery;
use Drupal\entity\EntityMalformedException;
use Drupal\entity\EntityStorageException;
@@ -73,6 +75,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(),
@@ -418,16 +423,123 @@ function entity_page_label(EntityInterface $entity, $langcode = NULL) {
}
/**
- * Attaches field API validation to entity forms.
+ * Returns an entity form controller for the given operation.
+ *
+ * Since there might be different scenarios in which an entity is edited,
+ * multiple form controllers suitable to the different operations may be defined.
+ * If no controller is found for the default operation, the base class will be
+ * used. If a non-existing non-default operation is specified an exception will
+ * be thrown.
+ *
+ * @see hook_entity_info()
+ *
+ * @param $entity_type
+ * The type of the entity.
+ * @param $operation
+ * (optional) The name of an operation, such as creation, editing or deletion,
+ * identifying the controlled form. Defaults to 'default' which is the usual
+ * create/edit form.
+ *
+ * @return Drupal\entity\EntityFormControllerInterface
+ * An entity form controller instance.
+ */
+function entity_form_controller($entity_type, $operation = 'default') {
+ $info = entity_get_info($entity_type);
+
+ // Check whether there is a form controller class for the specified operation.
+ if (!empty($info['form controller class'][$operation])) {
+ $class = $info['form controller class'][$operation];
+ }
+ // If no controller is specified default to the base implementation.
+ elseif (empty($info['form controller class']) && $operation == 'default') {
+ $class = 'Drupal\entity\EntityFormController';
+ }
+ // If a non-existing operation has been specified stop.
+ else {
+ throw new InvalidArgumentException("Missing form controller for '$entity_type', operation '$operation'");
+ }
+
+ return new $class($operation);
+}
+
+/**
+ * Returns the form id for the given entity and operation.
+ *
+ * @param EntityInterface $entity
+ * The entity to be created or edited.
+ * @param $operation
+ * (optional) The operation for the form to be processed.
+ *
+ * @return
+ * A string representing the entity form id.
+ */
+function entity_form_id(EntityInterface $entity, $operation = 'default') {
+ $entity_type = $entity->entityType();
+ $bundle = $entity->bundle();
+ $form_id = $entity_type;
+ if ($bundle != $entity_type) {
+ $form_id = $bundle . '_' . $form_id;
+ }
+ if ($operation != 'default') {
+ $form_id = $form_id . '_' . $operation;
+ }
+ return $form_id . '_form';
+}
+
+/**
+ * Returns the default form state for the given entity and operation.
+ *
+ * @param EntityInterface $entity
+ * The entity to be created or edited.
+ * @param $operation
+ * (optional) The operation identifying the form to be processed.
+ *
+ * @return
+ * A $form_state array already filled the entity form controller.
+ */
+function entity_form_state_defaults(EntityInterface $entity, $operation = 'default') {
+ $form_state = array();
+ $controller = entity_form_controller($entity->entityType(), $operation);
+ $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);
+ return $form_state;
+}
+
+/**
+ * Retrieves, populates, and processes an entity form.
+ *
+ * @param EntityInterface $entity
+ * The entity to be created or edited.
+ * @param $operation
+ * (optional) The operation identifying the form to be submitted.
+ * @param $form_state
+ * (optional) A keyed array containing the current state of the form.
+ *
+ * @return
+ * A $form_state array already filled with the entity form controller.
+ */
+function entity_form_submit(EntityInterface $entity, $operation = 'default', &$form_state = array()) {
+ $form_state += entity_form_state_defaults($entity, $operation);
+ $form_id = entity_form_id($entity, $operation);
+ drupal_form_submit($form_id, $form_state);
+}
+
+/**
+ * Returns the built and processed entity form for the given entity.
+ *
+ * @param EntityInterface $entity
+ * The entity to be created or edited.
+ * @param $operation
+ * (optional) The operation identifying the form variation to be returned.
+ *
+ * @return
+ * The processed form for the given entity and operation.
*/
-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 = entity_create($entity_type, $form_state['values']);
- field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
+function entity_get_form(EntityInterface $entity, $operation = 'default') {
+ $form_state = entity_form_state_defaults($entity, $operation);
+ $form_id = entity_form_id($entity, $operation);
+ return drupal_build_form($form_id, $form_state);
}
/**
diff --git a/core/modules/entity/lib/Drupal/entity/Entity.php b/core/modules/entity/lib/Drupal/entity/Entity.php
index 14009b6..7e73024 100644
--- a/core/modules/entity/lib/Drupal/entity/Entity.php
+++ b/core/modules/entity/lib/Drupal/entity/Entity.php
@@ -278,5 +278,4 @@ class Entity implements EntityInterface {
public function isCurrentRevision() {
return $this->isCurrentRevision;
}
-
}
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..4f1263c
--- /dev/null
+++ b/core/modules/entity/lib/Drupal/entity/EntityFormController.php
@@ -0,0 +1,256 @@
+operation = $operation;
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::build().
+ */
+ public function build(array $form, array &$form_state, EntityInterface $entity) {
+
+ // During the 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 (!$this->getEntity($form_state)) {
+ $this->init($form_state, $entity);
+ }
+
+ // Retrieve the form array using the possibly updated entity in form state.
+ $entity = $this->getEntity($form_state);
+ $form = $this->form($form, $form_state, $entity);
+
+ // Retrieve and add the form actions array.
+ $actions = $this->actionsElement($form, $form_state);
+ if (!empty($actions)) {
+ $form['actions'] = $actions;
+ }
+
+ return $form;
+ }
+
+ /**
+ * Initialize the form state and the entity before the first form build.
+ */
+ protected function init(array &$form_state, EntityInterface $entity) {
+ // Add the controller to the form state so it can be easily accessed by
+ // module-provided form handlers there.
+ $form_state['controller'] = $this;
+ $this->setEntity($entity, $form_state);
+ $this->prepareEntity($entity);
+ }
+
+ /**
+ * Returns the actual form array to be built.
+ *
+ * @see Drupal\entity\EntityFormController::build()
+ */
+ public function form(array $form, array &$form_state, EntityInterface $entity) {
+ // @todo Exploit the Property API to generate the default widgets for the
+ // entity properties.
+ $info = $entity->entityInfo();
+ if (!empty($info['fieldable'])) {
+ field_attach_form($entity->entityType(), $entity, $form, $form_state, $this->getFormLangcode($form_state));
+ }
+ return $form;
+ }
+
+ /**
+ * Returns the action form element for the current entity form.
+ */
+ protected 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->getEntity($form_state)->isNew()) {
+ unset($element['delete']);
+ }
+ elseif (isset($element['delete'])) {
+ // Move the delete action as last one, unless weights are explicitly
+ // provided.
+ $delete = $element['delete'];
+ unset($element['delete']);
+ $element['delete'] = $delete;
+ }
+
+ $count = 0;
+ foreach (element_children($element) as $action) {
+ $element[$action] += array(
+ '#type' => 'submit',
+ '#weight' => ++$count * 5,
+ );
+ }
+
+ 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(
+ // @todo Rename the action key from submit to save.
+ 'submit' => array(
+ '#value' => t('Save'),
+ '#validate' => array(
+ array($this, 'validate'),
+ ),
+ '#submit' => array(
+ array($this, 'submit'),
+ array($this, 'save'),
+ ),
+ ),
+ 'delete' => array(
+ '#value' => t('Delete'),
+ // No need to validate the form when deleting the entity.
+ '#submit' => array(
+ array($this, 'delete'),
+ ),
+ ),
+ // @todo Consider introducing a 'preview' action here, since it is used by
+ // many entity types.
+ );
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::validate().
+ */
+ public function validate(array $form, array &$form_state) {
+ // @todo Exploit the Property API to validate the values submitted for the
+ // entity properties.
+ $entity = $this->buildEntity($form, $form_state);
+ $info = $entity->entityInfo();
+
+ if (!empty($info['fieldable'])) {
+ field_attach_form_validate($entity->entityType(), $entity, $form, $form_state);
+ }
+
+ // @todo Remove this.
+ // Execute legacy global validation handlers.
+ unset($form_state['validate_handlers']);
+ form_execute_handlers('validate', $form, $form_state);
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::submit().
+ *
+ * This is the default entity object builder function. It is called before any
+ * other submit handler to build the new entity object to be passed to the
+ * following submit handlers. At this point of the form workflow the entity is
+ * validated and the form state can be updated, this way the subsequently
+ * invoked handlers can retrieve a regular entity object to act on.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param array $form_state
+ * A reference to a keyed array containing the current state of the form.
+ */
+ public function submit(array $form, array &$form_state) {
+ $entity = $this->buildEntity($form, $form_state);
+ $this->setEntity($entity, $form_state);
+ return $entity;
+ }
+
+ /**
+ * Form submission handler for the 'save' action.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param array $form_state
+ * A reference to a keyed array containing the current state of the form.
+ */
+ public function save(array $form, array &$form_state) {
+ // @todo Perform common save operations.
+ }
+
+ /**
+ * Form submission handler for the 'delete' action.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param array $form_state
+ * A reference to a keyed array containing the current state of the form.
+ */
+ public function delete(array $form, array &$form_state) {
+ // @todo Perform common delete operations.
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::getFormLangcode().
+ */
+ public function getFormLangcode($form_state) {
+ // @todo Introduce a new form language type (see hook_language_types_info())
+ // to be used as the default active form language, should it be missing, so
+ // that entity forms can be used to submit multilingual values.
+ $language = $this->getEntity($form_state)->language();
+ return !empty($language->langcode) ? $language->langcode : NULL;
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::buildEntity().
+ */
+ public function buildEntity(array $form, array &$form_state) {
+ $entity = clone $this->getEntity($form_state);
+ // @todo Move entity_form_submit_build_entity() here.
+ // @todo Exploit the Property API to process the submitted entity property.
+ entity_form_submit_build_entity($entity->entityType(), $entity, $form, $form_state);
+ return $entity;
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::getEntity().
+ */
+ public function getEntity(array $form_state) {
+ return isset($form_state['entity']) ? $form_state['entity'] : NULL;
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::setEntity().
+ */
+ public function setEntity(EntityInterface $entity, array &$form_state) {
+ $form_state['entity'] = $entity;
+ }
+
+ /**
+ * Prepares the entity object before the form is built first.
+ */
+ protected function prepareEntity(EntityInterface $entity) {
+ // @todo Perform common prepare operations and add a hook.
+ }
+
+ /**
+ * Implements Drupal\entity\EntityFormControllerInterface::getOperation().
+ */
+ public function getOperation() {
+ return $this->operation;
+ }
+}
diff --git a/core/modules/entity/lib/Drupal/entity/EntityFormControllerInterface.php b/core/modules/entity/lib/Drupal/entity/EntityFormControllerInterface.php
new file mode 100644
index 0000000..6765d85
--- /dev/null
+++ b/core/modules/entity/lib/Drupal/entity/EntityFormControllerInterface.php
@@ -0,0 +1,134 @@
+get('vocabulary');
if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
$form['help_forum_vocab'] = array(
@@ -610,9 +610,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 = config('forum.settings')->get('vocabulary');
if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
// Hide multiple parents select from forum terms.
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 7984fe0..653c17f 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -12,6 +12,7 @@
*/
use Drupal\node\Node;
+
use Symfony\Component\HttpFoundation\JsonResponse;
/**
@@ -629,8 +630,9 @@ function _menu_parent_depth_limit($item) {
function menu_form_node_form_alter(&$form, $form_state) {
// Generate a list of possible parents (not including this link or descendants).
// @todo This must be handled in a #process handler.
- $link = $form['#node']->menu;
- $type = $form['#node']->type;
+ $node = $form_state['controller']->getEntity($form_state);
+ $link = $node->menu;
+ $type = $node->type;
// menu_parent_options() is goofy and can actually handle either a menu link
// or a node type both as second argument. Pick based on whether there is
// a link already (menu_node_prepare() sets mlid default to 0).
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..ccf8daa
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -0,0 +1,433 @@
+type, array('status', 'promote'));
+ // If this is a new node, fill in the default values.
+ if (!isset($node->nid) || isset($node->is_new)) {
+ foreach (array('status', 'promote', 'sticky') as $key) {
+ // Multistep node forms might have filled in something already.
+ if (!isset($node->$key)) {
+ $node->$key = (int) in_array($key, $node_options);
+ }
+ }
+ global $user;
+ $node->uid = $user->uid;
+ $node->created = REQUEST_TIME;
+ }
+ else {
+ $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
+ // Remove the log message from the original node entity.
+ $node->log = NULL;
+ }
+ // Always use the default revision setting.
+ $node->revision = in_array('revision', $node_options);
+
+ node_invoke($node, 'prepare');
+ module_invoke_all('node_prepare', $node);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::form().
+ */
+ public function form(array $form, array &$form_state, EntityInterface $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);
+ }
+
+ // Override the default CSS class name, since the user-defined node type
+ // name in 'TYPE-node-form' potentially clashes with third-party class
+ // names.
+ $form['#attributes']['class'][0] = drupal_html_class('node-' . $node->type . '-form');
+
+ // Basic node information.
+ // These elements are just values so they are not even sent to the client.
+ foreach (array('nid', 'vid', 'uid', 'created', 'type') as $key) {
+ $form[$key] = array(
+ '#type' => 'value',
+ '#value' => isset($node->$key) ? $node->$key : NULL,
+ );
+ }
+
+ // Changed must be sent to the client, for later overwrite error checking.
+ $form['changed'] = array(
+ '#type' => 'hidden',
+ '#default_value' => isset($node->changed) ? $node->changed : NULL,
+ );
+
+ // Invoke hook_form() to get the node-specific bits. Can't use node_invoke()
+ // because hook_form() needs to be able to receive $form_state by reference.
+ // @todo hook_form() implementations are unable to add #validate or #submit
+ // handlers to the form buttons below. Remove hook_form() entirely.
+ $function = node_type_get_base($node) . '_form';
+ if (function_exists($function) && ($extra = $function($node, $form_state))) {
+ $form = array_merge_recursive($form, $extra);
+ }
+ // If the node type has a title, and the node type form defined no special
+ // weight for it, we default to a weight of -5 for consistency.
+ if (isset($form['title']) && !isset($form['title']['#weight'])) {
+ $form['title']['#weight'] = -5;
+ }
+
+ if (module_exists('language')) {
+ $languages = language_list(LANGUAGE_ALL);
+ $language_options = array();
+ foreach ($languages as $langcode => $language) {
+ // Make locked languages appear special in the list.
+ $language_options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name;
+ }
+
+ $form['langcode'] = array(
+ '#type' => 'select',
+ '#title' => t('Language'),
+ '#default_value' => $node->langcode,
+ '#options' => $language_options,
+ '#access' => !variable_get('node_type_language_hidden_' . $node->type, TRUE),
+ );
+ }
+ else {
+ $form['langcode'] = array(
+ '#type' => 'value',
+ '#value' => $node->langcode,
+ );
+ }
+
+ $form['additional_settings'] = array(
+ '#type' => 'vertical_tabs',
+ '#weight' => 99,
+ );
+
+ // Add a log field if the "Create new revision" option is checked, or if the
+ // current user has the ability to check that option.
+ $form['revision_information'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Revision information'),
+ '#collapsible' => TRUE,
+ // Collapsed by default when "Create new revision" is unchecked
+ '#collapsed' => !$node->revision,
+ '#group' => 'additional_settings',
+ '#attributes' => array(
+ 'class' => array('node-form-revision-information'),
+ ),
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'node') . '/node.js'),
+ ),
+ '#weight' => 20,
+ '#access' => $node->revision || user_access('administer nodes'),
+ );
+
+ $form['revision_information']['revision'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create new revision'),
+ '#default_value' => $node->revision,
+ '#access' => user_access('administer nodes'),
+ );
+
+ // Check the revision log checkbox when the log textarea is filled in.
+ // This must not happen if "Create new revision" is enabled by default,
+ // since the state would auto-disable the checkbox otherwise.
+ if (!$node->revision) {
+ $form['revision_information']['revision']['#states'] = array(
+ 'checked' => array(
+ 'textarea[name="log"]' => array('empty' => FALSE),
+ ),
+ );
+ }
+
+ $form['revision_information']['log'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Revision log message'),
+ '#rows' => 4,
+ '#default_value' => !empty($node->log) ? $node->log : '',
+ '#description' => t('Briefly describe the changes you have made.'),
+ );
+
+ // Node author information for administrators.
+ $form['author'] = array(
+ '#type' => 'fieldset',
+ '#access' => user_access('administer nodes'),
+ '#title' => t('Authoring information'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#group' => 'additional_settings',
+ '#attributes' => array(
+ 'class' => array('node-form-author'),
+ ),
+ '#attached' => array(
+ 'js' => array(
+ drupal_get_path('module', 'node') . '/node.js',
+ array(
+ 'type' => 'setting',
+ 'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
+ ),
+ ),
+ ),
+ '#weight' => 90,
+ );
+
+ $form['author']['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Authored by'),
+ '#maxlength' => 60,
+ '#autocomplete_path' => 'user/autocomplete',
+ '#default_value' => !empty($node->name) ? $node->name : '',
+ '#weight' => -1,
+ '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
+ );
+
+ $form['author']['date'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Authored on'),
+ '#maxlength' => 25,
+ '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O'))),
+ '#default_value' => !empty($node->date) ? $node->date : '',
+ );
+
+ // Node options for administrators.
+ $form['options'] = array(
+ '#type' => 'fieldset',
+ '#access' => user_access('administer nodes'),
+ '#title' => t('Publishing options'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#group' => 'additional_settings',
+ '#attributes' => array(
+ 'class' => array('node-form-options'),
+ ),
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'node') . '/node.js'),
+ ),
+ '#weight' => 95,
+ );
+
+ $form['options']['status'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Published'),
+ '#default_value' => $node->status,
+ );
+
+ $form['options']['promote'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Promoted to front page'),
+ '#default_value' => $node->promote,
+ );
+
+ $form['options']['sticky'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Sticky at top of lists'),
+ '#default_value' => $node->sticky,
+ );
+
+ // 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.
+ $form += array('#submit' => array());
+
+ return parent::form($form, $form_state, $node);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::actions().
+ */
+ protected function actions(array $form, array &$form_state) {
+ $element = parent::actions($form, $form_state);
+ $node = $this->getEntity($form_state);
+ $preview_mode = variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL);
+
+ $element['preview'] = array(
+ '#access' => $preview_mode != DRUPAL_DISABLED,
+ '#value' => t('Preview'),
+ '#validate' => array(
+ array($this, 'validate'),
+ ),
+ '#submit' => array(
+ array($this, 'submit'),
+ array($this, '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;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::validate().
+ */
+ public function validate(array $form, array &$form_state) {
+ $node = $this->buildEntity($form, $form_state);
+
+ if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
+ form_set_error('changed', t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
+ }
+
+ // Validate the "authored by" field.
+ if (!empty($node->name) && !($account = user_load_by_name($node->name))) {
+ // The use of empty() is mandatory in the context of usernames
+ // as the empty string denotes the anonymous user. In case we
+ // are dealing with an anonymous user we set the user ID to 0.
+ form_set_error('name', t('The username %name does not exist.', array('%name' => $node->name)));
+ }
+
+ // Validate the "authored on" field.
+ if (!empty($node->date) && strtotime($node->date) === FALSE) {
+ form_set_error('date', t('You have to specify a valid date.'));
+ }
+
+ // Invoke hook_validate() for node type specific validation and
+ // hook_node_validate() for miscellaneous validation needed by modules.
+ // Can't use node_invoke() or module_invoke_all(), because $form_state must
+ // be receivable by reference.
+ $function = node_type_get_base($node) . '_validate';
+ if (function_exists($function)) {
+ $function($node, $form, $form_state);
+ }
+ foreach (module_implements('node_validate') as $module) {
+ $function = $module . '_node_validate';
+ $function($node, $form, $form_state);
+ }
+
+ parent::validate($form, $form_state);
+ }
+
+ /**
+ * Updates the node object by processing the submitted values.
+ *
+ * This function can 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.
+ *
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ $this->submitNodeLanguage($form, $form_state);
+
+ // Build the node object from the submitted values.
+ $node = parent::submit($form, $form_state);
+
+ node_submit($node);
+ foreach (module_implements('node_submit') as $module) {
+ $function = $module . '_node_submit';
+ $function($node, $form, $form_state);
+ }
+
+ return $node;
+ }
+
+ /**
+ * Handle possible node language changes.
+ */
+ protected function submitNodeLanguage(array $form, array &$form_state) {
+ if (field_has_translation_handler('node', 'node')) {
+ $bundle = $form_state['values']['type'];
+ $node_language = $form_state['values']['langcode'];
+
+ foreach (field_info_instances('node', $bundle) as $instance) {
+ $field_name = $instance['field_name'];
+ $field = field_info_field($field_name);
+ $previous_langcode = $form[$field_name]['#language'];
+
+ // Handle a possible language change: new language values are inserted,
+ // previous ones are deleted.
+ if ($field['translatable'] && $previous_langcode != $node_language) {
+ $form_state['values'][$field_name][$node_language] = $form_state['values'][$field_name][$previous_langcode];
+ $form_state['values'][$field_name][$previous_langcode] = array();
+ }
+ }
+ }
+ }
+
+ /**
+ * Form submission handler for the 'preview' 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 preview(array $form, array &$form_state) {
+ drupal_set_title(t('Preview'), PASS_THROUGH);
+ $form_state['node_preview'] = node_preview($this->getEntity($form_state));
+ $form_state['rebuild'] = TRUE;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $node = $this->getEntity($form_state);
+ $insert = empty($node->nid);
+ $node->save();
+ $node_link = l(t('view'), 'node/' . $node->nid);
+ $watchdog_args = array('@type' => $node->type, '%title' => $node->label());
+ $t_args = array('@type' => node_type_get_name($node), '%title' => $node->label());
+
+ if ($insert) {
+ watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
+ drupal_set_message(t('@type %title has been created.', $t_args));
+ }
+ else {
+ watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
+ drupal_set_message(t('@type %title has been updated.', $t_args));
+ }
+
+ if ($node->nid) {
+ $form_state['values']['nid'] = $node->nid;
+ $form_state['nid'] = $node->nid;
+ $form_state['redirect'] = 'node/' . $node->nid;
+ }
+ else {
+ // 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_invalidate(array('content' => TRUE));
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::delete().
+ */
+ public function delete(array $form, array &$form_state) {
+ $destination = array();
+ if (isset($_GET['destination'])) {
+ $destination = drupal_get_destination();
+ unset($_GET['destination']);
+ }
+ $node = $this->getEntity($form_state);
+ $form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination));
+ }
+}
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index f4fb5f5..e5a8518 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -636,8 +636,8 @@ function hook_node_access($node, $op, $account, $langcode) {
/**
* Act on a node object about to be shown on the add/edit form.
*
- * This hook is invoked from node_object_prepare() after the type-specific
- * hook_prepare() is invoked.
+ * This hook is invoked from NodeFormController::prepareEntity() after the
+ * type-specific hook_prepare() is invoked.
*
* @param Drupal\node\Node $node
* The node that is about to be shown on the add/edit form.
@@ -740,10 +740,10 @@ function hook_node_update_index(Drupal\node\Node $node) {
/**
* Perform node validation before a node is created or updated.
*
- * This hook is invoked from node_validate(), after a user has has finished
- * editing the node and is previewing or submitting it. It is invoked at the
- * end of all the standard validation steps, and after the type-specific
- * hook_validate() is invoked.
+ * This hook is invoked from NodeFormController::validate(), after a user has
+ * has finished editing the node and is previewing or submitting it. It is
+ * invoked at the end of all the standard validation steps, and after the
+ * type-specific hook_validate() is invoked.
*
* To indicate a validation error, use form_set_error().
*
@@ -1052,8 +1052,8 @@ function hook_delete(Drupal\node\Node $node) {
* This hook is invoked only on the module that defines the node's content type
* (use hook_node_prepare() to act on all node preparations).
*
- * This hook is invoked from node_object_prepare() before the general
- * hook_node_prepare() is invoked.
+ * This hook is invoked from NodeFormController::prepareEntity() before the
+ * general hook_node_prepare() is invoked.
*
* @param Drupal\node\Node $node
* The node that is about to be shown on the add/edit form.
@@ -1219,10 +1219,10 @@ function hook_update(Drupal\node\Node $node) {
* This hook is invoked only on the module that defines the node's content type
* (use hook_node_validate() to act on all node validations).
*
- * This hook is invoked from node_validate(), after a user has finished
- * editing the node and is previewing or submitting it. It is invoked at the end
- * of all the standard validation steps, and before hook_node_validate() is
- * invoked.
+ * This hook is invoked from NodeFormController::validate(), after a user has
+ * finished editing the node and is previewing or submitting it. It is invoked
+ * at the end of all the standard validation steps, and before
+ * hook_node_validate() is invoked.
*
* To indicate a validation error, use form_set_error().
*
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index be15dee..c0c5058 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',
@@ -1026,81 +1029,6 @@ function node_load($nid = NULL, $vid = NULL, $reset = FALSE) {
}
/**
- * Prepares a node entity for editing.
- *
- * Fills in a few default values, and then invokes hook_prepare() on the node
- * type module, and hook_node_prepare() on all modules.
- *
- * @param Drupal\node\Node $node
- * The node entity.
- */
-function node_object_prepare(Node $node) {
- // Set up default values, if required.
- $node_options = variable_get('node_options_' . $node->type, array('status', 'promote'));
- // If this is a new node, fill in the default values.
- if (!isset($node->nid) || isset($node->is_new)) {
- foreach (array('status', 'promote', 'sticky') as $key) {
- // Multistep node forms might have filled in something already.
- if (!isset($node->$key)) {
- $node->$key = (int) in_array($key, $node_options);
- }
- }
- global $user;
- $node->uid = $user->uid;
- $node->created = REQUEST_TIME;
- }
- else {
- $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
- // Remove the log message from the original node entity.
- $node->log = NULL;
- }
- // Always use the default revision setting.
- $node->revision = in_array('revision', $node_options);
-
- node_invoke($node, 'prepare');
- module_invoke_all('node_prepare', $node);
-}
-
-/**
- * Performs validation checks on the given node.
- *
- * @see node_form_validate()
- */
-function node_validate(Node $node, $form, &$form_state) {
- $type = node_type_load($node->type);
-
- if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
- form_set_error('changed', t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
- }
-
- // Validate the "authored by" field.
- if (!empty($node->name) && !($account = user_load_by_name($node->name))) {
- // The use of empty() is mandatory in the context of usernames
- // as the empty string denotes the anonymous user. In case we
- // are dealing with an anonymous user we set the user ID to 0.
- form_set_error('name', t('The username %name does not exist.', array('%name' => $node->name)));
- }
-
- // Validate the "authored on" field.
- if (!empty($node->date) && strtotime($node->date) === FALSE) {
- form_set_error('date', t('You have to specify a valid date.'));
- }
-
- // Invoke hook_validate() for node type specific validation and
- // hook_node_validate() for miscellaneous validation needed by modules. Can't
- // use node_invoke() or module_invoke_all(), because $form_state must be
- // receivable by reference.
- $function = node_type_get_base($node) . '_validate';
- if (function_exists($function)) {
- $function($node, $form, $form_state);
- }
- foreach (module_implements('node_validate') as $module) {
- $function = $module . '_node_validate';
- $function($node, $form, $form_state);
- }
-}
-
-/**
* Prepares a node for saving by populating the author and creation date.
*/
function node_submit($node) {
@@ -3654,21 +3582,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 76fd8b2..b2e1e4b 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -19,7 +19,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->label())), PASS_THROUGH);
- return drupal_get_form($node->type . '_node_form', $node);
+ return entity_get_form($node);
}
/**
@@ -96,311 +96,12 @@ function node_add($node_type) {
'langcode' => node_type_get_default_langcode($type)
));
drupal_set_title(t('Create @name', array('@name' => $node_type->name)), PASS_THROUGH);
- $output = drupal_get_form($type . '_node_form', $node);
+ $output = entity_get_form($node);
return $output;
}
/**
- * Form validation handler for node_form().
- *
- * @see node_form_delete_submit()
- * @see node_form_build_preview()
- * @see node_form_submit()
- * @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
- // create a pseudo-entity to use during validation.
- $node = clone $form_state['node'];
- foreach ($form_state['values'] as $key => $value) {
- $node->{$key} = $value;
- }
- node_validate($node, $form, $form_state);
- entity_form_field_validate('node', $form, $form_state);
-}
-
-/**
- * Form constructor for the node add/edit form.
- *
- * @see node_form_delete_submit()
- * @see node_form_build_preview()
- * @see node_form_validate()
- * @see node_form_submit()
- * @see node_form_submit_build_node()
- * @ingroup forms
- */
-function node_form($form, &$form_state, Node $node) {
- global $user;
-
- // 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'])) {
- 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);
- }
-
- // Override the default CSS class name, since the user-defined node type name
- // in 'TYPE-node-form' potentially clashes with third-party class names.
- $form['#attributes']['class'][0] = drupal_html_class('node-' . $node->type . '-form');
-
- // Basic node information.
- // These elements are just values so they are not even sent to the client.
- foreach (array('nid', 'vid', 'uid', 'created', 'type') as $key) {
- $form[$key] = array(
- '#type' => 'value',
- '#value' => isset($node->$key) ? $node->$key : NULL,
- );
- }
-
- // Changed must be sent to the client, for later overwrite error checking.
- $form['changed'] = array(
- '#type' => 'hidden',
- '#default_value' => isset($node->changed) ? $node->changed : NULL,
- );
- // Invoke hook_form() to get the node-specific bits. Can't use node_invoke(),
- // because hook_form() needs to be able to receive $form_state by reference.
- // @todo hook_form() implementations are unable to add #validate or #submit
- // handlers to the form buttons below. Remove hook_form() entirely.
- $function = node_type_get_base($node) . '_form';
- if (function_exists($function) && ($extra = $function($node, $form_state))) {
- $form = array_merge_recursive($form, $extra);
- }
- // If the node type has a title, and the node type form defined no special
- // weight for it, we default to a weight of -5 for consistency.
- if (isset($form['title']) && !isset($form['title']['#weight'])) {
- $form['title']['#weight'] = -5;
- }
- // @todo D8: Remove. Modules should access the node using $form_state['node'].
- $form['#node'] = $node;
-
- if (module_exists('language')) {
- $languages = language_list(LANGUAGE_ALL);
- $language_options = array();
- foreach ($languages as $langcode => $language) {
- // Make locked languages appear special in the list.
- $language_options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name;
- }
- $form['langcode'] = array(
- '#type' => 'select',
- '#title' => t('Language'),
- '#default_value' => $node->langcode,
- '#options' => $language_options,
- '#access' => !variable_get('node_type_language_hidden_' . $node->type, TRUE),
- );
- }
- else {
- $form['langcode'] = array(
- '#type' => 'value',
- '#value' => $node->langcode,
- );
- }
-
- $form['additional_settings'] = array(
- '#type' => 'vertical_tabs',
- '#weight' => 99,
- );
-
- // Add a log field if the "Create new revision" option is checked, or if the
- // current user has the ability to check that option.
- $form['revision_information'] = array(
- '#type' => 'fieldset',
- '#title' => t('Revision information'),
- '#collapsible' => TRUE,
- // Collapsed by default when "Create new revision" is unchecked
- '#collapsed' => !$node->revision,
- '#group' => 'additional_settings',
- '#attributes' => array(
- 'class' => array('node-form-revision-information'),
- ),
- '#attached' => array(
- 'js' => array(drupal_get_path('module', 'node') . '/node.js'),
- ),
- '#weight' => 20,
- '#access' => $node->revision || user_access('administer nodes'),
- );
- $form['revision_information']['revision'] = array(
- '#type' => 'checkbox',
- '#title' => t('Create new revision'),
- '#default_value' => $node->revision,
- '#access' => user_access('administer nodes'),
- );
- // Check the revision log checkbox when the log textarea is filled in.
- // This must not happen if "Create new revision" is enabled by default, since
- // the state would auto-disable the checkbox otherwise.
- if (!$node->revision) {
- $form['revision_information']['revision']['#states'] = array(
- 'checked' => array(
- 'textarea[name="log"]' => array('empty' => FALSE),
- ),
- );
- }
- $form['revision_information']['log'] = array(
- '#type' => 'textarea',
- '#title' => t('Revision log message'),
- '#rows' => 4,
- '#default_value' => !empty($node->log) ? $node->log : '',
- '#description' => t('Briefly describe the changes you have made.'),
- );
-
- // Node author information for administrators
- $form['author'] = array(
- '#type' => 'fieldset',
- '#access' => user_access('administer nodes'),
- '#title' => t('Authoring information'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'additional_settings',
- '#attributes' => array(
- 'class' => array('node-form-author'),
- ),
- '#attached' => array(
- 'js' => array(
- drupal_get_path('module', 'node') . '/node.js',
- array(
- 'type' => 'setting',
- 'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
- ),
- ),
- ),
- '#weight' => 90,
- );
- $form['author']['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored by'),
- '#maxlength' => 60,
- '#autocomplete_path' => 'user/autocomplete',
- '#default_value' => !empty($node->name) ? $node->name : '',
- '#weight' => -1,
- '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
- );
- $form['author']['date'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored on'),
- '#maxlength' => 25,
- '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O'))),
- '#default_value' => !empty($node->date) ? $node->date : '',
- );
-
- // Node options for administrators
- $form['options'] = array(
- '#type' => 'fieldset',
- '#access' => user_access('administer nodes'),
- '#title' => t('Publishing options'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#group' => 'additional_settings',
- '#attributes' => array(
- 'class' => array('node-form-options'),
- ),
- '#attached' => array(
- 'js' => array(drupal_get_path('module', 'node') . '/node.js'),
- ),
- '#weight' => 95,
- );
- $form['options']['status'] = array(
- '#type' => 'checkbox',
- '#title' => t('Published'),
- '#default_value' => $node->status,
- );
- $form['options']['promote'] = array(
- '#type' => 'checkbox',
- '#title' => t('Promoted to front page'),
- '#default_value' => $node->promote,
- );
- $form['options']['sticky'] = array(
- '#type' => 'checkbox',
- '#title' => t('Sticky at top of lists'),
- '#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';
- }
- $form += array('#submit' => array());
-
- field_attach_form('node', $node, $form, $form_state, $node->langcode);
- return $form;
-}
-
-/**
- * Form submission handler for the 'Delete' button for node_form().
- *
- * @see node_form_build_preview()
- * @see node_form_validate()
- * @see node_form_submit()
- * @see node_form_submit_build_node()
- */
-function node_form_delete_submit($form, &$form_state) {
- $destination = array();
- if (isset($_GET['destination'])) {
- $destination = drupal_get_destination();
- unset($_GET['destination']);
- }
- $node = $form['#node'];
- $form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination));
-}
-
-/**
- * Form submission handler for the 'Preview' button for node_form().
- *
- * @see node_form_delete_submit()
- * @see node_form_validate()
- * @see node_form_submit()
- * @see node_form_submit_build_node()
- */
-function node_form_build_preview($form, &$form_state) {
- $node = node_form_submit_build_node($form, $form_state);
- $form_state['node_preview'] = node_preview($node);
- $form_state['rebuild'] = TRUE;
-}
-
-/**
* Generates a node preview.
*
* @param Drupal\node\Node $node
@@ -442,7 +143,6 @@ function node_preview(Node $node) {
$output = theme('node_preview', array('node' => $node));
unset($node->in_preview);
}
- drupal_set_title(t('Preview'), PASS_THROUGH);
return $output;
}
@@ -455,7 +155,7 @@ function node_preview(Node $node) {
* An associative array containing:
* - node: The node entity which is being previewed.
*
- * @see node_preview()
+ * @see NodeFormController::preview()
* @ingroup themeable
*/
function theme_node_preview($variables) {
@@ -486,115 +186,11 @@ function theme_node_preview($variables) {
}
/**
- * Form submission handler that saves the node for node_form().
- *
- * @see node_form_delete_submit()
- * @see node_form_build_preview()
- * @see node_form_validate()
- * @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();
- $node_link = l(t('view'), 'node/' . $node->nid);
- $watchdog_args = array('@type' => $node->type, '%title' => $node->label());
- $t_args = array('@type' => node_type_get_name($node), '%title' => $node->label());
-
- if ($insert) {
- watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
- drupal_set_message(t('@type %title has been created.', $t_args));
- }
- else {
- watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
- drupal_set_message(t('@type %title has been updated.', $t_args));
- }
- if ($node->nid) {
- $form_state['values']['nid'] = $node->nid;
- $form_state['nid'] = $node->nid;
- $form_state['redirect'] = 'node/' . $node->nid;
- }
- else {
- // 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_invalidate(array('content' => TRUE));
-}
-
-/**
- * Handles possible node language changes.
- *
- */
-function node_field_language_form_submit($form, &$form_state) {
- if (field_has_translation_handler('node', 'node')) {
- $bundle = $form_state['values']['type'];
- $node_language = $form_state['values']['langcode'];
-
- foreach (field_info_instances('node', $bundle) as $instance) {
- $field_name = $instance['field_name'];
- $field = field_info_field($field_name);
- $previous_langcode = $form[$field_name]['#language'];
-
- // Handle a possible language change: New language values are inserted,
- // previous ones are deleted.
- if ($field['translatable'] && $previous_langcode != $node_language) {
- $form_state['values'][$field_name][$node_language] = $form_state['values'][$field_name][$previous_langcode];
- $form_state['values'][$field_name][$previous_langcode] = array();
- }
- }
- }
-}
-
-/**
- * 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()
- * @see node_form_delete_submit()
- * @see node_form_build_preview()
- * @see node_form_validate()
- * @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_submit($node);
- foreach (module_implements('node_submit') as $module) {
- $function = $module . '_node_submit';
- $function($node, $form, $form_state);
- }
- return $node;
-}
-
-/**
* Page callback: Form constructor for node deletion confirmation form.
*
* @see node_menu()
*/
function node_delete_confirm($form, &$form_state, $node) {
- $form['#node'] = $node;
// Always provide entity id in the same form key as in the entity edit form.
$form['nid'] = array('#type' => 'value', '#value' => $node->nid);
return confirm_form($form,
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index 5d557cc..402093f 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -7,9 +7,10 @@
* a special 'node test view' permission.
*/
-use Drupal\node\Node;
use Drupal\entity\EntityFieldQuery;
+use Drupal\node\Node;
+
/**
* Implements hook_node_grants().
*/
@@ -179,11 +180,12 @@ function node_access_entity_test_page() {
function node_access_test_form_node_form_alter(&$form, $form_state) {
// Only show this checkbox for NodeAccessBaseTableTestCase.
if (variable_get('node_access_test_private')) {
+ $node = $form_state['controller']->getEntity($form_state);
$form['private'] = array(
'#type' => 'checkbox',
'#title' => t('Private'),
'#description' => t('Check here if this content should be set private and only shown to privileged users.'),
- '#default_value' => isset($form['#node']->private) ? $form['#node']->private : FALSE,
+ '#default_value' => isset($node->private) ? $node->private : FALSE,
);
}
}
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
index 034c7c2..5098216 100644
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -231,7 +231,8 @@ function openid_form_user_register_form_alter(&$form, &$form_state) {
$timezone = current($ax_timezone_values);
}
if (in_array($timezone, timezone_identifiers_list())) {
- $form['#user']->timezone = $timezone;
+ $account = $form_state['controller']->getEntity($form_state);
+ $account->timezone = $timezone;
}
$language = FALSE;
@@ -732,7 +733,8 @@ function openid_authentication($response) {
$form_state['values'] = array();
$form_state['values']['op'] = t('Create new account');
- drupal_form_submit('user_register_form', $form_state);
+ $account = entity_create('user', array());
+ entity_form_submit($account, 'register', $form_state);
if (empty($form_state['user'])) {
module_invoke_all('openid_response', $response, NULL);
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index acfcc2c..3aaed7d 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -85,7 +85,7 @@ function overlay_theme() {
* Implements hook_form_FORM_ID_alter().
*/
function overlay_form_user_profile_form_alter(&$form, &$form_state) {
- $account = $form['#user'];
+ $account = $form_state['controller']->getEntity($form_state);
if (user_access('access overlay', $account)) {
$form['overlay_control'] = array(
'#type' => 'fieldset',
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index d01c623..2baa603 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -6,6 +6,7 @@
*/
use Drupal\node\Node;
+
use Drupal\taxonomy\Term;
/**
@@ -99,11 +100,12 @@ function path_menu() {
* @see path_form_element_validate()
*/
function path_form_node_form_alter(&$form, $form_state) {
+ $node = $form_state['controller']->getEntity($form_state);
$path = array();
- if (!empty($form['#node']->nid)) {
- $conditions = array('source' => 'node/' . $form['#node']->nid);
- if ($form['#node']->langcode != LANGUAGE_NOT_SPECIFIED) {
- $conditions['langcode'] = $form['#node']->langcode;
+ if (!empty($node->nid)) {
+ $conditions = array('source' => 'node/' . $node->nid);
+ if ($node->langcode != LANGUAGE_NOT_SPECIFIED) {
+ $conditions['langcode'] = $node->langcode;
}
$path = path_load($conditions);
if ($path === FALSE) {
@@ -112,9 +114,9 @@ function path_form_node_form_alter(&$form, $form_state) {
}
$path += array(
'pid' => NULL,
- 'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL,
+ 'source' => isset($node->nid) ? 'node/' . $node->nid : NULL,
'alias' => '',
- 'langcode' => isset($form['#node']->langcode) ? $form['#node']->langcode : LANGUAGE_NOT_SPECIFIED,
+ 'langcode' => isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED,
);
$form['path'] = array(
@@ -232,18 +234,19 @@ 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());
+ $term = $form_state['controller']->getEntity($form_state);
+ $path = (isset($term->tid) ? path_load('taxonomy/term/' . $term->tid) : array());
if ($path === FALSE) {
$path = array();
}
$path += array(
'pid' => NULL,
- 'source' => isset($form['#term']['tid']) ? 'taxonomy/term/' . $form['#term']['tid'] : NULL,
+ 'source' => isset($term->tid) ? 'taxonomy/term/' . $term->tid : NULL,
'alias' => '',
'langcode' => LANGUAGE_NOT_SPECIFIED,
);
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index 259ff14..4ab2cb8 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -336,6 +336,8 @@ function poll_form(Node $node, &$form_state) {
drupal_get_path('module', 'poll') . '/poll.admin.css',
);
+ $form['#entity_builders'][] = 'poll_node_form_submit';
+
return $form;
}
@@ -355,9 +357,10 @@ function poll_more_choices_submit($form, &$form_state) {
}
// 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']);
+ // poll_form() to rebuild the choices with the values in $node->choice, which
+ // it does.
+ $node = $form_state['controller']->getEntity($form_state);
+ $node->choice = array_values($form_state['values']['choice']);
unset($form_state['input']['choice']);
$form_state['rebuild'] = TRUE;
}
@@ -419,14 +422,14 @@ function poll_choice_js($form, $form_state) {
}
/**
- * Form submit handler for node_form().
+ * Entity builder for node_form().
*
* Upon preview and final submission, we need to renumber poll choices and
* create a teaser output.
*/
-function poll_node_form_submit(&$form, &$form_state) {
+function poll_node_form_submit($entity_type, $entity, &$form, &$form_state) {
// Renumber choices.
- $form_state['values']['choice'] = array_values($form_state['values']['choice']);
+ $entity->choice = array_values($form_state['values']['choice']);
$form_state['values']['teaser'] = poll_teaser((object) $form_state['values']);
}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 142a366..c057156 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1184,7 +1184,8 @@ function hook_page_alter(&$page) {
* Perform alterations before a form is rendered.
*
* One popular use of this hook is to add form elements to the node form. When
- * altering a node form, the node entity can be accessed at $form['#node'].
+ * altering a node form, the node entity can be retrieved by invoking
+ * $form_state['controller']->getEntity($form_state).
*
* In addition to hook_form_alter(), which is called for all forms, there are
* two more specific form hooks available. The first,
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 4cf5780..3c434ae 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -6,6 +6,7 @@
*/
use Drupal\Core\Utility\ModuleInfo;
+
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@@ -2254,7 +2255,7 @@ function system_user_login(&$edit, $account) {
function system_user_timezone(&$form, &$form_state) {
global $user;
- $account = $form['#user'];
+ $account = $form_state['controller']->getEntity($form_state);
$form['timezone'] = array(
'#type' => 'fieldset',
'#title' => t('Locale settings'),
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index b89426c..44fc0eb 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -2132,8 +2132,8 @@ function form_test_two_instances() {
'langcode' => 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..36a08af 100644
--- a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module
+++ b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module
@@ -57,18 +57,17 @@ function taxonomy_test_taxonomy_term_delete(Term $term) {
}
/**
- * Implements hook_form_alter().
+ * Implements hook_form_FORM_ID_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) {
+ $term = $form_state['controller']->getEntity($form_state);
+ $antonym = taxonomy_test_get_antonym($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..fa853b3
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php
@@ -0,0 +1,203 @@
+vid);
+
+ $parent = array_keys(taxonomy_term_load_parents($term->tid));
+ $form_state['taxonomy']['parent'] = $parent;
+ $form_state['taxonomy']['vocabulary'] = $vocabulary;
+
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Name'),
+ '#default_value' => $term->name,
+ '#maxlength' => 255,
+ '#required' => TRUE,
+ '#weight' => -5,
+ );
+
+ $form['description'] = array(
+ '#type' => 'text_format',
+ '#title' => t('Description'),
+ '#default_value' => $term->description,
+ '#format' => $term->format,
+ '#weight' => 0,
+ );
+
+ $form['vocabulary_machine_name'] = array(
+ '#type' => 'value',
+ '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name,
+ );
+
+ $form['relations'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Relations'),
+ '#collapsible' => TRUE,
+ '#collapsed' => ($vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE),
+ '#weight' => 10,
+ );
+
+ // taxonomy_get_tree and taxonomy_term_load_parents may contain large numbers of
+ // items so we check for taxonomy_override_selector before loading the
+ // 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_term_load_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[] = $term->tid;
+
+ $tree = taxonomy_get_tree($vocabulary->vid);
+ $options = array('<' . t('root') . '>');
+ if (empty($parent)) {
+ $parent = array(0);
+ }
+ foreach ($tree as $item) {
+ if (!in_array($item->tid, $exclude)) {
+ $options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
+ }
+ }
+
+ $form['relations']['parent'] = array(
+ '#type' => 'select',
+ '#title' => t('Parent terms'),
+ '#options' => $options,
+ '#default_value' => $parent,
+ '#multiple' => TRUE,
+ );
+ }
+
+ $form['relations']['weight'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Weight'),
+ '#size' => 6,
+ '#default_value' => $term->weight,
+ '#description' => t('Terms are displayed in ascending order by weight.'),
+ '#required' => TRUE,
+ );
+
+ $form['vid'] = array(
+ '#type' => 'value',
+ '#value' => $vocabulary->vid,
+ );
+
+ $form['tid'] = array(
+ '#type' => 'value',
+ '#value' => $term->tid,
+ );
+
+ if (empty($term->tid)) {
+ $form_state['redirect'] = current_path();
+ }
+
+ return parent::form($form, $form_state, $term);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::validate().
+ */
+ public function validate(array $form, array &$form_state) {
+ parent::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.'));
+ }
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ $term = parent::submit($form, $form_state);
+
+ // Prevent leading and trailing spaces in term names.
+ $term->name = trim($term->name);
+
+ // Convert text_format field into values expected by taxonomy_term_save().
+ $description = $form_state['values']['description'];
+ $term->description = $description['value'];
+ $term->format = $description['format'];
+
+ return $term;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $term = $this->getEntity($form_state);
+
+ $status = taxonomy_term_save($term);
+ switch ($status) {
+ case SAVED_NEW:
+ drupal_set_message(t('Created new term %term.', array('%term' => $term->name)));
+ watchdog('taxonomy', 'Created new term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
+ break;
+ case SAVED_UPDATED:
+ drupal_set_message(t('Updated term %term.', array('%term' => $term->name)));
+ watchdog('taxonomy', 'Updated term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
+ // Clear the page and block caches to avoid stale data.
+ cache_invalidate(array('content' => TRUE));
+ break;
+ }
+
+ $current_parent_count = count($form_state['values']['parent']);
+ $previous_parent_count = count($form_state['taxonomy']['parent']);
+ // Root doesn't count if it's the only parent.
+ if ($current_parent_count == 1 && isset($form_state['values']['parent'][0])) {
+ $current_parent_count = 0;
+ $form_state['values']['parent'] = array();
+ }
+
+ // If the number of parents has been reduced to one or none, do a check on the
+ // parents of every term in the vocabulary value.
+ if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
+ taxonomy_check_vocabulary_hierarchy($form_state['taxonomy']['vocabulary'], $form_state['values']);
+ }
+ // If we've increased the number of parents and this is a single or flat
+ // hierarchy, update the vocabulary immediately.
+ elseif ($current_parent_count > $previous_parent_count && $form_state['taxonomy']['vocabulary']->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE) {
+ $form_state['taxonomy']['vocabulary']->hierarchy = $current_parent_count == 1 ? TAXONOMY_HIERARCHY_SINGLE : TAXONOMY_HIERARCHY_MULTIPLE;
+ taxonomy_vocabulary_save($form_state['taxonomy']['vocabulary']);
+ }
+
+ $form_state['values']['tid'] = $term->tid;
+ $form_state['tid'] = $term->tid;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::delete().
+ */
+ public function delete(array $form, array &$form_state) {
+ $destination = array();
+ if (isset($_GET['destination'])) {
+ $destination = drupal_get_destination();
+ unset($_GET['destination']);
+ }
+ $term = $this->getEntity($form_state);
+ $form_state['redirect'] = array('taxonomy/term/' . $term->tid . '/delete', array('query' => $destination));
+ }
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
index d041d16..1f34e66 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.
+ // TermFormController::save() 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..1a1f882
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php
@@ -0,0 +1,139 @@
+ 'textfield',
+ '#title' => t('Name'),
+ '#default_value' => $vocabulary->name,
+ '#maxlength' => 255,
+ '#required' => TRUE,
+ );
+ $form['machine_name'] = array(
+ '#type' => 'machine_name',
+ '#default_value' => $vocabulary->machine_name,
+ '#maxlength' => 255,
+ '#machine_name' => array(
+ 'exists' => 'taxonomy_vocabulary_machine_name_load',
+ ),
+ );
+ $form['description'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Description'),
+ '#default_value' => $vocabulary->description,
+ );
+ // Set the hierarchy to "multiple parents" by default. This simplifies the
+ // vocabulary form and standardizes the term form.
+ $form['hierarchy'] = array(
+ '#type' => 'value',
+ '#value' => '0',
+ );
+
+ if (isset($vocabulary->vid)) {
+ $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
+ }
+
+ return parent::form($form, $form_state, $vocabulary);
+ }
+
+ /**
+ * 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.
+ if (empty($form_state['confirm_delete'])) {
+ $actions = parent::actions($form, $form_state);
+ array_unshift($actions['delete']['#submit'], array($this, 'submit'));
+ return $actions;
+ }
+ else {
+ return array();
+ }
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::validate().
+ */
+ public function validate(array $form, array &$form_state) {
+ parent::validate($form, $form_state);
+
+ // Make sure that the machine name of the vocabulary is not in the
+ // disallowed list (names that conflict with menu items, such as 'list'
+ // and 'add').
+ // During the deletion there is no 'machine_name' key.
+ if (isset($form_state['values']['machine_name'])) {
+ // Do not allow machine names to conflict with taxonomy path arguments.
+ $machine_name = $form_state['values']['machine_name'];
+ $disallowed = array('add', 'list');
+ if (in_array($machine_name, $disallowed)) {
+ form_set_error('machine_name', t('The machine-readable name cannot be "add" or "list".'));
+ }
+ }
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ // @todo We should not be calling taxonomy_vocabulary_confirm_delete() from
+ // within the form builder.
+ 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 NULL;
+ }
+ else {
+ return parent::submit($form, $form_state);
+ }
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $vocabulary = $this->getEntity($form_state);
+
+ // Prevent leading and trailing spaces in vocabulary names.
+ $vocabulary->name = trim($vocabulary->name);
+
+ switch (taxonomy_vocabulary_save($vocabulary)) {
+ case SAVED_NEW:
+ drupal_set_message(t('Created new vocabulary %name.', array('%name' => $vocabulary->name)));
+ watchdog('taxonomy', 'Created new vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
+ $form_state['redirect'] = 'admin/structure/taxonomy/' . $vocabulary->machine_name;
+ break;
+
+ case SAVED_UPDATED:
+ drupal_set_message(t('Updated vocabulary %name.', array('%name' => $vocabulary->name)));
+ watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
+ $form_state['redirect'] = 'admin/structure/taxonomy';
+ break;
+ }
+
+ $form_state['values']['vid'] = $vocabulary->vid;
+ $form_state['vid'] = $vocabulary->vid;
+ }
+}
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index be020a6..19a24d4 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -102,140 +102,16 @@ function theme_taxonomy_overview_vocabularies($variables) {
}
/**
- * Form builder for the vocabulary editing form.
- *
- * @param Drupal\taxonomy\Vocabulary|null $vocabulary
- * (optional) The taxonomy vocabulary entity to edit. If NULL or omitted, the
- * form creates a new vocabulary.
- *
- * @ingroup forms
- * @see taxonomy_form_vocabulary_submit()
- * @see taxonomy_form_vocabulary_validate()
+ * Page callback: provides the vocabulary creation form.
*/
-function taxonomy_form_vocabulary($form, &$form_state, Vocabulary $vocabulary = NULL) {
- // 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'])) {
- // Create a new Vocabulary entity for the add form.
- if (!isset($vocabulary)) {
- $vocabulary = entity_create('taxonomy_vocabulary', array(
- // Default the new vocabulary to the site's default language. This is
- // the most likely default value until we have better flexible settings.
- // @todo See http://drupal.org/node/258785 and followups.
- 'langcode' => language_default()->langcode,
- ));
- }
- $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']);
- }
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Name'),
- '#default_value' => $vocabulary->name,
- '#maxlength' => 255,
- '#required' => TRUE,
- );
- $form['machine_name'] = array(
- '#type' => 'machine_name',
- '#default_value' => $vocabulary->machine_name,
- '#maxlength' => 255,
- '#machine_name' => array(
- 'exists' => 'taxonomy_vocabulary_machine_name_load',
- ),
- );
- $form['description'] = array(
- '#type' => 'textfield',
- '#title' => t('Description'),
- '#default_value' => $vocabulary->description,
- );
- // Set the hierarchy to "multiple parents" by default. This simplifies the
- // vocabulary form and standardizes the term form.
- $form['hierarchy'] = array(
- '#type' => 'value',
- '#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;
-}
-
-/**
- * Form validation handler for taxonomy_form_vocabulary().
- *
- * Makes sure that the machine name of the vocabulary is not in the
- * disallowed list (names that conflict with menu items, such as 'list'
- * and 'add').
- *
- * @see taxonomy_form_vocabulary()
- * @see taxonomy_form_vocabulary_submit()
- */
-function taxonomy_form_vocabulary_validate($form, &$form_state) {
- // During the deletion there is no 'machine_name' key
- if (isset($form_state['values']['machine_name'])) {
- // Do not allow machine names to conflict with taxonomy path arguments.
- $machine_name = $form_state['values']['machine_name'];
- $disallowed = array('add', 'list');
- if (in_array($machine_name, $disallowed)) {
- form_set_error('machine_name', t('The machine-readable name cannot be "add" or "list".'));
- }
- }
-}
-
-/**
- * Form submission handler for taxonomy_form_vocabulary().
- *
- * @see taxonomy_form_vocabulary()
- * @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);
-
- // Prevent leading and trailing spaces in vocabulary names.
- $vocabulary->name = trim($vocabulary->name);
-
- switch (taxonomy_vocabulary_save($vocabulary)) {
- case SAVED_NEW:
- drupal_set_message(t('Created new vocabulary %name.', array('%name' => $vocabulary->name)));
- watchdog('taxonomy', 'Created new vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
- $form_state['redirect'] = 'admin/structure/taxonomy/' . $vocabulary->machine_name;
- break;
-
- case SAVED_UPDATED:
- drupal_set_message(t('Updated vocabulary %name.', array('%name' => $vocabulary->name)));
- watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit'));
- $form_state['redirect'] = 'admin/structure/taxonomy';
- break;
- }
-
- $form_state['values']['vid'] = $vocabulary->vid;
- $form_state['vid'] = $vocabulary->vid;
+function taxonomy_vocabulary_add() {
+ $vocabulary = entity_create('taxonomy_vocabulary', array(
+ // Default the new vocabulary to the site's default language. This is the
+ // most likely default value until we have better flexible settings.
+ // @todo See http://drupal.org/node/258785 and followups.
+ 'langcode' => language_default()->langcode,
+ ));
+ return entity_get_form($vocabulary);
}
/**
@@ -259,7 +135,7 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) {
return taxonomy_vocabulary_confirm_reset_alphabetical($form, $form_state, $vocabulary->vid);
}
- $form['#vocabulary'] = $vocabulary;
+ $form_state['taxonomy']['vocabulary'] = $vocabulary;
$form['#tree'] = TRUE;
$form['#parent_fields'] = FALSE;
@@ -461,7 +337,7 @@ function taxonomy_overview_terms_submit($form, &$form_state) {
// Sort term order based on weight.
uasort($form_state['values'], 'drupal_sort_weight');
- $vocabulary = $form['#vocabulary'];
+ $vocabulary = $form_state['taxonomy']['vocabulary'];
// Update the current hierarchy type as we go.
$hierarchy = TAXONOMY_HIERARCHY_DISABLED;
@@ -643,247 +519,11 @@ function theme_taxonomy_overview_terms($variables) {
}
/**
- * Form function for the term edit form.
- *
- * @param Drupal\taxonomy\Term|null $term
- * (optional) The taxonomy term entity to edit. If NULL or omitted, the form
- * creates a new term.
- * @param Drupal\taxonomy\Vocabulary|null $vocabulary
- * (optional) A taxonomy vocabulary entity to create the term in. Required if
- * the term is omitted.
- *
- * @ingroup forms
- * @see taxonomy_form_term_validate()
- * @see taxonomy_form_term_submit()
- */
-function taxonomy_form_term($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.
- if (!isset($form_state['term'])) {
- // Create a new Term entity for the add form.
- if (!isset($term)) {
- $term = entity_create('taxonomy_term', array(
- 'vid' => $vocabulary->vid,
- 'vocabulary_machine_name' => $vocabulary->machine_name,
- // Default the new vocabulary to the site's default language. This is
- // the most likely default value until we have better flexible settings.
- // @todo See http://drupal.org/node/258785 and followups.
- 'langcode' => language_default()->langcode,
- ));
- }
- if (!isset($vocabulary) && isset($term->vid)) {
- $vocabulary = taxonomy_vocabulary_load($term->vid);
- }
- $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_term_load_parents($term->tid));
- $form['#term'] = (array) $term;
- $form['#term']['parent'] = $parent;
- $form['#vocabulary'] = $vocabulary;
-
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Name'),
- '#default_value' => $term->name,
- '#maxlength' => 255,
- '#required' => TRUE,
- '#weight' => -5,
- );
- $form['description'] = array(
- '#type' => 'text_format',
- '#title' => t('Description'),
- '#default_value' => $term->description,
- '#format' => $term->format,
- '#weight' => 0,
- );
-
- $form['vocabulary_machine_name'] = array(
- '#type' => 'value',
- '#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'),
- '#collapsible' => TRUE,
- '#collapsed' => ($vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE),
- '#weight' => 10,
- );
-
- // taxonomy_get_tree and taxonomy_term_load_parents may contain large numbers of
- // items so we check for taxonomy_override_selector before loading the
- // 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_term_load_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[] = $term->tid;
-
- $tree = taxonomy_get_tree($vocabulary->vid);
- $options = array('<' . t('root') . '>');
- if (empty($parent)) {
- $parent = array(0);
- }
- foreach ($tree as $item) {
- if (!in_array($item->tid, $exclude)) {
- $options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
- }
- }
- $form['relations']['parent'] = array(
- '#type' => 'select',
- '#title' => t('Parent terms'),
- '#options' => $options,
- '#default_value' => $parent,
- '#multiple' => TRUE,
- );
-
- }
- $form['relations']['weight'] = array(
- '#type' => 'textfield',
- '#title' => t('Weight'),
- '#size' => 6,
- '#default_value' => $term->weight,
- '#description' => t('Terms are displayed in ascending order by weight.'),
- '#required' => TRUE,
- );
- $form['vid'] = array(
- '#type' => 'value',
- '#value' => $vocabulary->vid,
- );
- $form['tid'] = array(
- '#type' => 'value',
- '#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 {
- $form_state['redirect'] = current_path();
- }
-
- return $form;
-}
-
-/**
- * Validation handler for the term form.
- *
- * @see taxonomy_form_term()
+ * Returns a rendered edit form to create a new term associated to the given vocabulary.
*/
-function taxonomy_form_term_validate($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'])) {
- form_set_error('weight', t('Weight value must be numeric.'));
- }
-}
-
-/**
- * Submit handler to insert or update a term.
- *
- * @see taxonomy_form_term()
- */
-function taxonomy_form_term_submit($form, &$form_state) {
- $term = taxonomy_form_term_submit_build_taxonomy_term($form, $form_state);
-
- $status = taxonomy_term_save($term);
- switch ($status) {
- case SAVED_NEW:
- drupal_set_message(t('Created new term %term.', array('%term' => $term->name)));
- watchdog('taxonomy', 'Created new term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
- break;
- case SAVED_UPDATED:
- drupal_set_message(t('Updated term %term.', array('%term' => $term->name)));
- watchdog('taxonomy', 'Updated term %term.', array('%term' => $term->name), WATCHDOG_NOTICE, l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit'));
- // Clear the page and block caches to avoid stale data.
- cache_invalidate(array('content' => TRUE));
- break;
- }
-
- $current_parent_count = count($form_state['values']['parent']);
- $previous_parent_count = count($form['#term']['parent']);
- // Root doesn't count if it's the only parent.
- if ($current_parent_count == 1 && isset($form_state['values']['parent'][0])) {
- $current_parent_count = 0;
- $form_state['values']['parent'] = array();
- }
-
- // If the number of parents has been reduced to one or none, do a check on the
- // parents of every term in the vocabulary value.
- if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
- taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']);
- }
- // If we've increased the number of parents and this is a single or flat
- // hierarchy, update the vocabulary immediately.
- elseif ($current_parent_count > $previous_parent_count && $form['#vocabulary']->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE) {
- $form['#vocabulary']->hierarchy = $current_parent_count == 1 ? TAXONOMY_HIERARCHY_SINGLE : TAXONOMY_HIERARCHY_MULTIPLE;
- taxonomy_vocabulary_save($form['#vocabulary']);
- }
-
- $form_state['values']['tid'] = $term->tid;
- $form_state['tid'] = $term->tid;
-}
-
-/**
- * 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);
-
- // Prevent leading and trailing spaces in term names.
- $term->name = trim($term->name);
-
- // Convert text_format field into values expected by taxonomy_term_save().
- $description = $form_state['values']['description'];
- $term->description = $description['value'];
- $term->format = $description['format'];
- return $term;
-}
-
-/**
- * Form submission handler for the 'Delete' button for taxonomy_form_term().
- *
- * @see taxonomy_form_term_validate()
- * @see taxonomy_form_term_submit()
- */
-function taxonomy_form_term_delete_submit($form, &$form_state) {
- $destination = array();
- if (isset($_GET['destination'])) {
- $destination = drupal_get_destination();
- unset($_GET['destination']);
- }
- $term = $form['#term'];
- $form_state['redirect'] = array('taxonomy/term/' . $term['tid'] . '/delete', array('query' => $destination));
+function taxonomy_term_add($vocabulary) {
+ $term = entity_create('taxonomy_term', array('vid' => $vocabulary->vid, 'vocabulary_machine_name' => $vocabulary->machine_name));
+ return entity_get_form($term);
}
/**
@@ -896,8 +536,7 @@ function taxonomy_term_confirm_delete($form, &$form_state, $term) {
// Always provide entity id in the same form key as in the entity edit form.
$form['tid'] = array('#type' => 'value', '#value' => $term->tid);
- $form['#term'] = $term;
- $form['#vocabulary'] = taxonomy_vocabulary_load($term->vid);;
+ $form_state['taxonomy']['vocabulary'] = taxonomy_vocabulary_load($term->vid);;
$form['type'] = array('#type' => 'value', '#value' => 'term');
$form['name'] = array('#type' => 'value', '#value' => $term->name);
$form['vocabulary_machine_name'] = array('#type' => 'value', '#value' => $term->vocabulary_machine_name);
@@ -918,7 +557,7 @@ function taxonomy_term_confirm_delete($form, &$form_state, $term) {
*/
function taxonomy_term_confirm_delete_submit($form, &$form_state) {
taxonomy_term_delete($form_state['values']['tid']);
- taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']);
+ taxonomy_check_vocabulary_hierarchy($form_state['taxonomy']['vocabulary'], $form_state['values']);
drupal_set_message(t('Deleted term %name.', array('%name' => $form_state['values']['name'])));
watchdog('taxonomy', 'Deleted term %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
if (!isset($_GET['destination'])) {
@@ -940,7 +579,7 @@ function taxonomy_vocabulary_confirm_delete($form, &$form_state, $vid) {
// Always provide entity id in the same form key as in the entity edit form.
$form['vid'] = array('#type' => 'value', '#value' => $vid);
- $form['#vocabulary'] = $vocabulary;
+ $form_state['taxonomy']['vocabulary'] = $vocabulary;
$form['#id'] = 'taxonomy_vocabulary_confirm_delete';
$form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
$form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index ca67cca..0bb318d 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,
@@ -150,6 +153,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',
@@ -295,8 +301,7 @@ 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' => 'taxonomy_vocabulary_add',
'access arguments' => array('administer taxonomy'),
'type' => MENU_LOCAL_ACTION,
'file' => 'taxonomy.admin.inc',
@@ -317,10 +322,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,
@@ -370,8 +375,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,
@@ -380,8 +385,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/translation/translation.module b/core/modules/translation/translation.module
index ba5c406..909117f 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -153,9 +153,8 @@ function translation_node_type_language_translation_enabled_validate($element, &
* @see node_form()
*/
function translation_form_node_form_alter(&$form, &$form_state) {
- if (translation_supported_type($form['#node']->type)) {
- $node = $form['#node'];
-
+ $node = $form_state['controller']->getEntity($form_state);
+ if (translation_supported_type($node->type)) {
if (!empty($node->translation_source)) {
// We are creating a translation. Add values and lock language field.
$form['translation_source'] = array('#type' => 'value', '#value' => $node->translation_source);
@@ -379,10 +378,11 @@ function translation_node_update(Node $node) {
*
* Ensures that duplicate translations can't be created for the same source.
*/
-function translation_node_validate(Node $node, $form) {
+function translation_node_validate(Node $node, $form, &$form_state) {
// Only act on translatable nodes with a tnid or translation_source.
- if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form['#node']->translation_source->nid))) {
- $tnid = !empty($node->tnid) ? $node->tnid : $form['#node']->translation_source->nid;
+ $form_node = $form_state['controller']->getEntity($form_state);
+ if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form_node->translation_source->nid))) {
+ $tnid = !empty($node->tnid) ? $node->tnid : $form_node->translation_source->nid;
$translations = translation_node_get_translations($tnid);
if (isset($translations[$node->langcode]) && $translations[$node->langcode]->nid != $node->nid) {
form_set_error('langcode', t('There is already a translation in this language.'));
diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
new file mode 100644
index 0000000..31acf0d
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
@@ -0,0 +1,330 @@
+uid);
+ $admin = user_access('administer users');
+
+ // Account information.
+ $form['account'] = array(
+ '#type' => 'container',
+ '#weight' => -10,
+ );
+
+ // Only show name field on registration form or user can change own username.
+ $form['account']['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Username'),
+ '#maxlength' => USERNAME_MAX_LENGTH,
+ '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
+ '#required' => TRUE,
+ '#attributes' => array('class' => array('username'), 'autocomplete' => 'off'),
+ '#default_value' => (!$register ? $account->name : ''),
+ '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
+ '#weight' => -10,
+ );
+
+ // The mail field is NOT required if account originally had no mail set
+ // and the user performing the edit has 'administer users' permission.
+ // This allows users without e-mail address to be edited and deleted.
+ $form['account']['mail'] = array(
+ '#type' => 'email',
+ '#title' => t('E-mail address'),
+ '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
+ '#required' => !(empty($account->mail) && user_access('administer users')),
+ '#default_value' => (!$register ? $account->mail : ''),
+ '#attributes' => array('autocomplete' => 'off'),
+ );
+
+ // Display password field only for existing users or when user is allowed to
+ // assign a password during registration.
+ if (!$register) {
+ $form['account']['pass'] = array(
+ '#type' => 'password_confirm',
+ '#size' => 25,
+ '#description' => t('To change the current user password, enter the new password in both fields.'),
+ );
+
+ // To skip the current password field, the user must have logged in via a
+ // one-time link and have the token in the URL.
+ $pass_reset = isset($_SESSION['pass_reset_' . $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_' . $account->uid]);
+ $protected_values = array();
+ $current_pass_description = '';
+
+ // The user may only change their own password without their current
+ // password if they logged in via a one-time login link.
+ if (!$pass_reset) {
+ $protected_values['mail'] = $form['account']['mail']['#title'];
+ $protected_values['pass'] = t('Password');
+ $request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
+ $current_pass_description = t('Required if you want to change the %mail or %pass below. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
+ }
+
+ // The user must enter their current password to change to a new one.
+ if ($user->uid == $account->uid) {
+ $form['account']['current_pass_required_values'] = array(
+ '#type' => 'value',
+ '#value' => $protected_values,
+ );
+
+ $form['account']['current_pass'] = array(
+ '#type' => 'password',
+ '#title' => t('Current password'),
+ '#size' => 25,
+ '#access' => !empty($protected_values),
+ '#description' => $current_pass_description,
+ '#weight' => -5,
+ '#attributes' => array('autocomplete' => 'off'),
+ );
+
+ $form_state['user'] = $account;
+ $form['#validate'][] = 'user_validate_current_pass';
+ }
+ }
+ elseif (!variable_get('user_email_verification', TRUE) || $admin) {
+ $form['account']['pass'] = array(
+ '#type' => 'password_confirm',
+ '#size' => 25,
+ '#description' => t('Provide a password for the new account in both fields.'),
+ '#required' => TRUE,
+ );
+ }
+
+ if ($admin) {
+ $status = isset($account->status) ? $account->status : 1;
+ }
+ else {
+ $status = $register ? variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS : $account->status;
+ }
+
+ $form['account']['status'] = array(
+ '#type' => 'radios',
+ '#title' => t('Status'),
+ '#default_value' => $status,
+ '#options' => array(t('Blocked'), t('Active')),
+ '#access' => $admin,
+ );
+
+ $roles = array_map('check_plain', user_roles(TRUE));
+ // The disabled checkbox subelement for the 'authenticated user' role
+ // must be generated separately and added to the checkboxes element,
+ // because of a limitation in Form API not supporting a single disabled
+ // checkbox within a set of checkboxes.
+ // @todo This should be solved more elegantly. See issue #119038.
+ $checkbox_authenticated = array(
+ '#type' => 'checkbox',
+ '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
+ '#default_value' => TRUE,
+ '#disabled' => TRUE,
+ );
+ unset($roles[DRUPAL_AUTHENTICATED_RID]);
+
+ $form['account']['roles'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Roles'),
+ '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()),
+ '#options' => $roles,
+ '#access' => $roles && user_access('administer permissions'),
+ DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
+ );
+
+ $form['account']['notify'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Notify user of new account'),
+ '#access' => $register && $admin,
+ );
+
+ // Signature.
+ $form['signature_settings'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Signature settings'),
+ '#weight' => 1,
+ '#access' => (!$register && variable_get('user_signatures', 0)),
+ );
+
+ $form['signature_settings']['signature'] = array(
+ '#type' => 'text_format',
+ '#title' => t('Signature'),
+ '#default_value' => isset($account->signature) ? $account->signature : '',
+ '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
+ '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
+ );
+
+ // Picture/avatar.
+ $form['picture'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Picture'),
+ '#weight' => 1,
+ '#access' => (!$register && variable_get('user_pictures', 0)),
+ );
+
+ $form['picture']['picture'] = array(
+ '#type' => 'value',
+ '#value' => isset($account->picture) ? $account->picture : NULL,
+ );
+
+ $form['picture']['picture_current'] = array(
+ '#markup' => theme('user_picture', array('account' => $account)),
+ );
+
+ $form['picture']['picture_delete'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Delete picture'),
+ '#access' => !empty($account->picture->fid),
+ '#description' => t('Check this box to delete your current picture.'),
+ );
+
+ $form['picture']['picture_upload'] = array(
+ '#type' => 'file',
+ '#title' => t('Upload picture'),
+ '#size' => 48,
+ '#description' => t('Your virtual face or picture. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')),
+ );
+
+ $form['#validate'][] = 'user_validate_picture';
+
+ if (module_exists('language') && language_multilingual()) {
+ $languages = language_list();
+
+ // If the user is being created, we set the user language to the page language.
+ $user_preferred_language = $register ? $language_interface : user_preferred_language($account);
+
+ $names = array();
+ foreach ($languages as $langcode => $item) {
+ $names[$langcode] = $item->name;
+ }
+
+ // Is default the interface language?
+ $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
+ $form['language'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Language settings'),
+ // Display language selector when either creating a user on the admin
+ // interface or editing a user account.
+ '#access' => !$register || user_access('administer users'),
+ );
+
+ $form['language']['preferred_langcode'] = array(
+ '#type' => (count($names) <= 5 ? 'radios' : 'select'),
+ '#title' => t('Language'),
+ '#default_value' => $user_preferred_language->langcode,
+ '#options' => $names,
+ '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."),
+ );
+ }
+ else {
+ $form['language'] = array(
+ '#type' => 'container',
+ );
+
+ $form['language']['preferred_langcode'] = array(
+ '#type' => 'value',
+ '#value' => language_default()->langcode,
+ );
+ }
+
+ // User entities contain both a langcode property (for identifying the
+ // language of the entity data) and a preferred_langcode property (see
+ // above). Rather than provide a UI forcing the user to choose both
+ // separately, assume that the user profile data is in the user's preferred
+ // language. This element provides that synchronization. For use-cases where
+ // this synchronization is not desired, a module can alter or remove this
+ // element.
+ $form['language']['langcode'] = array(
+ '#type' => 'value',
+ '#value_callback' => '_user_language_selector_langcode_value',
+ // For the synchronization to work, this element must have a larger weight
+ // than the preferred_langcode element. Set a large weight here in case
+ // a module alters the weight of the other element.
+ '#weight' => 100,
+ );
+
+ return parent::form($form, $form_state, $account);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function validate(array $form, array &$form_state) {
+ parent::validate($form, $form_state);
+
+ $account = $this->getEntity($form_state);
+ // Validate new or changing username.
+ if (isset($form_state['values']['name'])) {
+ if ($error = user_validate_name($form_state['values']['name'])) {
+ form_set_error('name', $error);
+ }
+ // Cast the user ID as an integer. It might have been set to NULL, which
+ // could lead to unexpected results.
+ else {
+ $name_taken = (bool) db_select('users')
+ ->fields('users', array('uid'))
+ ->condition('uid', (int) $account->uid, '<>')
+ ->condition('name', db_like($form_state['values']['name']), 'LIKE')
+ ->range(0, 1)
+ ->execute()
+ ->fetchField();
+
+ if ($name_taken) {
+ form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+ }
+ }
+ }
+
+ $mail = $form_state['values']['mail'];
+
+ if (!empty($mail)) {
+ $mail_taken = (bool) db_select('users')
+ ->fields('users', array('uid'))
+ ->condition('uid', (int) $account->uid, '<>')
+ ->condition('mail', db_like($mail), 'LIKE')
+ ->range(0, 1)
+ ->execute()
+ ->fetchField();
+
+ if ($mail_taken) {
+ // Format error message dependent on whether the user is logged in or not.
+ if ($GLOBALS['user']->uid) {
+ form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail)));
+ }
+ else {
+ form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $mail, '@password' => url('user/password'))));
+ }
+ }
+ }
+
+ // Make sure the signature isn't longer than the size of the database field.
+ // Signatures are disabled by default, so make sure it exists first.
+ if (isset($form_state['values']['signature'])) {
+ // Move text format for user signature into 'signature_format'.
+ $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
+ // Move text value for user signature into 'signature'.
+ $form_state['values']['signature'] = $form_state['values']['signature']['value'];
+
+ $user_schema = drupal_get_schema('users');
+ if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
+ form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
+ }
+ }
+ }
+}
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..d32cf4a
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php
@@ -0,0 +1,62 @@
+getEntity($form_state);
+
+ // @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;
+ }
+
+ /**
+ * Overrides 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);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::save().
+ */
+ public function save(array $form, array &$form_state) {
+ $account = $this->getEntity($form_state);
+ $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..9c88aec
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php
@@ -0,0 +1,155 @@
+ 'value',
+ '#value' => $admin,
+ );
+
+ // If we aren't admin but already logged on, go to the user page instead.
+ if (!$admin && $user->uid) {
+ drupal_goto('user/' . $user->uid);
+ }
+
+ $form['#attached']['library'][] = array('system', 'jquery.cookie');
+ $form['#attributes']['class'][] = 'user-info-from-cookie';
+
+ // Start with the default user account fields.
+ $form = parent::form($form, $form_state, $account);
+
+ // Attach field widgets, and hide the ones where the 'user_register_form'
+ // setting is not on.
+ field_attach_form('user', $account, $form, $form_state);
+ foreach (field_info_instances('user', 'user') as $field_name => $instance) {
+ if (empty($instance['settings']['user_register_form'])) {
+ $form[$field_name]['#access'] = FALSE;
+ }
+ }
+
+ if ($admin) {
+ // Redirect back to page which initiated the create request; usually
+ // admin/people/create.
+ $form_state['redirect'] = current_path();
+ }
+
+ return $form;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::actions().
+ */
+ protected function actions(array $form, array &$form_state) {
+ $element = parent::actions($form, $form_state);
+ $element['submit']['#value'] = t('Create new account');
+ return $element;
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function submit(array $form, array &$form_state) {
+ $admin = $form_state['values']['administer_users'];
+
+ if (!variable_get('user_email_verification', TRUE) || $admin) {
+ $pass = $form_state['values']['pass'];
+ }
+ else {
+ $pass = user_password();
+ }
+
+ // Remove unneeded values.
+ form_state_values_clean($form_state);
+
+ $form_state['values']['pass'] = $pass;
+ $form_state['values']['init'] = $form_state['values']['mail'];
+
+ parent::submit($form, $form_state);
+ }
+
+ /**
+ * Overrides Drupal\entity\EntityFormController::submit().
+ */
+ public function save(array $form, array &$form_state) {
+ $account = $this->getEntity($form_state);
+ $pass = $account->pass;
+ $admin = $form_state['values']['administer_users'];
+ $notify = !empty($form_state['values']['notify']);
+
+ $account->save();
+
+ // Terminate if an error occurred while saving the account.
+ if ($status =! SAVED_NEW) {
+ drupal_set_message(t("Error saving user account."), 'error');
+ $form_state['redirect'] = '';
+ return;
+ }
+ $form_state['user'] = $account;
+ $form_state['values']['uid'] = $account->uid;
+
+ watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
+
+ // Add plain text password into user account to generate mail tokens.
+ $account->password = $pass;
+
+ // New administrative account without notification.
+ $uri = entity_uri('user', $account);
+ if ($admin && !$notify) {
+ drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ }
+ // No e-mail verification required; log in user immediately.
+ elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
+ _user_mail_notify('register_no_approval_required', $account);
+ $form_state['uid'] = $account->uid;
+ user_login_submit(array(), $form_state);
+ drupal_set_message(t('Registration successful. You are now logged in.'));
+ $form_state['redirect'] = '';
+ }
+ // No administrator approval required.
+ elseif ($account->status || $notify) {
+ if (empty($account->mail) && $notify) {
+ drupal_set_message(t('The new user %name was created without an email address, so no welcome message was sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ }
+ else {
+ $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
+ _user_mail_notify($op, $account);
+ if ($notify) {
+ drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ }
+ else {
+ drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
+ $form_state['redirect'] = '';
+ }
+ }
+ }
+ // Administrator approval required.
+ else {
+ _user_mail_notify('register_pending_approval', $account);
+ drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
+ $form_state['redirect'] = '';
+ }
+ }
+}
diff --git a/core/modules/user/tests/user_form_test.module b/core/modules/user/tests/user_form_test.module
index 4e907f3..5702c53 100644
--- a/core/modules/user/tests/user_form_test.module
+++ b/core/modules/user/tests/user_form_test.module
@@ -27,7 +27,7 @@ function user_form_test_menu() {
*/
function user_form_test_current_password($form, &$form_state, $account) {
$account->user_form_test_field = '';
- $form['#user'] = $account;
+ $form_state['user'] = $account;
$form['user_form_test_field'] = array(
'#type' => 'textfield',
@@ -35,7 +35,7 @@ function user_form_test_current_password($form, &$form_state, $account) {
'#description' => t('A field that would require a correct password to change.'),
'#required' => TRUE,
);
-
+
$form['current_pass'] = array(
'#type' => 'password',
'#title' => t('Current password'),
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 58b0218..fac9723 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -11,7 +11,8 @@ function user_admin($callback_arg = '') {
switch ($op) {
case t('Create new account'):
case 'create':
- $build['user_register'] = drupal_get_form('user_register_form');
+ $account = entity_create('user', array());
+ $build['user_register'] = entity_get_form($account, '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 1f72aa6..313affe 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -147,6 +147,10 @@ function user_entity_info() {
'user' => array(
'label' => t('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',
@@ -399,7 +403,7 @@ function user_validate_name($name) {
/**
* Validates an image uploaded by a user.
*
- * @see user_account_form()
+ * @see AccountFormController::form()
*/
function user_validate_picture(&$form, &$form_state) {
// If required, validate the uploaded picture.
@@ -701,242 +705,6 @@ function user_user_view($account) {
}
/**
- * Helper function to add default user account fields to user registration and edit form.
- *
- * @see user_account_form_validate()
- * @see user_validate_current_pass()
- * @see user_validate_picture()
- * @see user_validate_mail()
- */
-function user_account_form(&$form, &$form_state) {
- global $user;
- $language_interface = language(LANGUAGE_TYPE_INTERFACE);
-
- $account = $form['#user'];
- $register = ($form['#user']->uid > 0 ? FALSE : TRUE);
-
- $admin = user_access('administer users');
-
- $form['#validate'][] = 'user_account_form_validate';
-
- // Account information.
- $form['account'] = array(
- '#type' => 'container',
- '#weight' => -10,
- );
- // Only show name field on registration form or user can change own username.
- $form['account']['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Username'),
- '#maxlength' => USERNAME_MAX_LENGTH,
- '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
- '#required' => TRUE,
- '#attributes' => array('class' => array('username'), 'autocomplete' => 'off'),
- '#default_value' => (!$register ? $account->name : ''),
- '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
- '#weight' => -10,
- );
-
- // The mail field is NOT required if account originally had no mail set
- // and the user performing the edit has 'administer users' permission.
- // This allows users without e-mail address to be edited and deleted.
- $form['account']['mail'] = array(
- '#type' => 'email',
- '#title' => t('E-mail address'),
- '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
- '#required' => !(empty($account->mail) && user_access('administer users')),
- '#default_value' => (!$register ? $account->mail : ''),
- '#attributes' => array('autocomplete' => 'off'),
- );
-
- // Display password field only for existing users or when user is allowed to
- // assign a password during registration.
- if (!$register) {
- $form['account']['pass'] = array(
- '#type' => 'password_confirm',
- '#size' => 25,
- '#description' => t('To change the current user password, enter the new password in both fields.'),
- );
- // To skip the current password field, the user must have logged in via a
- // one-time link and have the token in the URL.
- $pass_reset = isset($_SESSION['pass_reset_' . $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_' . $account->uid]);
- $protected_values = array();
- $current_pass_description = '';
- // The user may only change their own password without their current
- // password if they logged in via a one-time login link.
- if (!$pass_reset) {
- $protected_values['mail'] = $form['account']['mail']['#title'];
- $protected_values['pass'] = t('Password');
- $request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
- $current_pass_description = t('Required if you want to change the %mail or %pass below. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
- }
- // The user must enter their current password to change to a new one.
- if ($user->uid == $account->uid) {
- $form['account']['current_pass_required_values'] = array(
- '#type' => 'value',
- '#value' => $protected_values,
- );
- $form['account']['current_pass'] = array(
- '#type' => 'password',
- '#title' => t('Current password'),
- '#size' => 25,
- '#access' => !empty($protected_values),
- '#description' => $current_pass_description,
- '#weight' => -5,
- '#attributes' => array('autocomplete' => 'off'),
- );
- $form['#validate'][] = 'user_validate_current_pass';
- }
- }
- elseif (!variable_get('user_email_verification', TRUE) || $admin) {
- $form['account']['pass'] = array(
- '#type' => 'password_confirm',
- '#size' => 25,
- '#description' => t('Provide a password for the new account in both fields.'),
- '#required' => TRUE,
- );
- }
-
- if ($admin) {
- $status = isset($account->status) ? $account->status : 1;
- }
- else {
- $status = $register ? variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS : $account->status;
- }
- $form['account']['status'] = array(
- '#type' => 'radios',
- '#title' => t('Status'),
- '#default_value' => $status,
- '#options' => array(t('Blocked'), t('Active')),
- '#access' => $admin,
- );
-
- $roles = array_map('check_plain', user_roles(TRUE));
- // The disabled checkbox subelement for the 'authenticated user' role
- // must be generated separately and added to the checkboxes element,
- // because of a limitation in Form API not supporting a single disabled
- // checkbox within a set of checkboxes.
- // @todo This should be solved more elegantly. See issue #119038.
- $checkbox_authenticated = array(
- '#type' => 'checkbox',
- '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
- '#default_value' => TRUE,
- '#disabled' => TRUE,
- );
- unset($roles[DRUPAL_AUTHENTICATED_RID]);
- $form['account']['roles'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Roles'),
- '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()),
- '#options' => $roles,
- '#access' => $roles && user_access('administer permissions'),
- DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
- );
-
- $form['account']['notify'] = array(
- '#type' => 'checkbox',
- '#title' => t('Notify user of new account'),
- '#access' => $register && $admin,
- );
-
- // Signature.
- $form['signature_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Signature settings'),
- '#weight' => 1,
- '#access' => (!$register && variable_get('user_signatures', 0)),
- );
-
- $form['signature_settings']['signature'] = array(
- '#type' => 'text_format',
- '#title' => t('Signature'),
- '#default_value' => isset($account->signature) ? $account->signature : '',
- '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
- '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
- );
-
- // Picture/avatar.
- $form['picture'] = array(
- '#type' => 'fieldset',
- '#title' => t('Picture'),
- '#weight' => 1,
- '#access' => (!$register && variable_get('user_pictures', 0)),
- );
- $form['picture']['picture'] = array(
- '#type' => 'value',
- '#value' => isset($account->picture) ? $account->picture : NULL,
- );
- $form['picture']['picture_current'] = array(
- '#markup' => theme('user_picture', array('account' => $account)),
- );
- $form['picture']['picture_delete'] = array(
- '#type' => 'checkbox',
- '#title' => t('Delete picture'),
- '#access' => !empty($account->picture->fid),
- '#description' => t('Check this box to delete your current picture.'),
- );
- $form['picture']['picture_upload'] = array(
- '#type' => 'file',
- '#title' => t('Upload picture'),
- '#size' => 48,
- '#description' => t('Your virtual face or picture. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')),
- );
- $form['#validate'][] = 'user_validate_picture';
-
- if (module_exists('language') && language_multilingual()) {
- $languages = language_list();
-
- // If the user is being created, we set the user language to the page language.
- $user_preferred_language = $register ? $language_interface : user_preferred_language($account);
-
- $names = array();
- foreach ($languages as $langcode => $item) {
- $names[$langcode] = $item->name;
- }
- // Is default the interface language?
- $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
- $form['language'] = array(
- '#type' => 'fieldset',
- '#title' => t('Language settings'),
- // Display language selector when either creating a user on the admin
- // interface or editing a user account.
- '#access' => !$register || user_access('administer users'),
- );
- $form['language']['preferred_langcode'] = array(
- '#type' => (count($names) <= 5 ? 'radios' : 'select'),
- '#title' => t('Language'),
- '#default_value' => $user_preferred_language->langcode,
- '#options' => $names,
- '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."),
- );
- }
- else {
- $form['language'] = array(
- '#type' => 'container',
- );
- $form['language']['preferred_langcode'] = array(
- '#type' => 'value',
- '#value' => language_default()->langcode,
- );
- }
-
- // User entities contain both a langcode property (for identifying the
- // language of the entity data) and a preferred_langcode property (see above).
- // Rather than provide a UI forcing the user to choose both separately,
- // assume that the user profile data is in the user's preferred language. This
- // element provides that synchronization. For use-cases where this
- // synchronization is not desired, a module can alter or remove this element.
- $form['language']['langcode'] = array(
- '#type' => 'value',
- '#value_callback' => '_user_language_selector_langcode_value',
- // For the synchronization to work, this element must have a larger weight
- // than the preferred_langcode element. Set a large weight here in case
- // a module alters the weight of the other element.
- '#weight' => 100,
- );
-}
-
-/**
* Sets the value of the user register and profile forms' langcode element.
*/
function _user_language_selector_langcode_value($element, $input, &$form_state) {
@@ -948,12 +716,12 @@ function _user_language_selector_langcode_value($element, $input, &$form_state)
}
/**
- * Form validation handler for the current password on the user_account_form().
+ * Form validation handler for the current password on the user account form.
*
- * @see user_account_form()
+ * @see AccountFormController::form()
*/
function user_validate_current_pass(&$form, &$form_state) {
- $account = $form['#user'];
+ $account = $form_state['user'];
foreach ($form_state['values']['current_pass_required_values'] as $key => $name) {
// This validation only works for required textfields (like mail) or
// form values like password_confirm that have their own validation
@@ -971,72 +739,6 @@ function user_validate_current_pass(&$form, &$form_state) {
}
}
-/**
- * Form validation handler for user_account_form().
- *
- * @see user_account_form()
- */
-function user_account_form_validate($form, &$form_state) {
- $account = $form['#user'];
- // Validate new or changing username.
- if (isset($form_state['values']['name'])) {
- if ($error = user_validate_name($form_state['values']['name'])) {
- form_set_error('name', $error);
- }
- // Cast the user ID as an integer. It might have been set to NULL, which
- // could lead to unexpected results.
- else {
- $name_taken = (bool) db_select('users')
- ->fields('users', array('uid'))
- ->condition('uid', (int) $account->uid, '<>')
- ->condition('name', db_like($form_state['values']['name']), 'LIKE')
- ->range(0, 1)
- ->execute()
- ->fetchField();
-
- if ($name_taken) {
- form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
- }
- }
- }
-
- $mail = $form_state['values']['mail'];
-
- if (!empty($mail)) {
- $mail_taken = (bool) db_select('users')
- ->fields('users', array('uid'))
- ->condition('uid', (int) $account->uid, '<>')
- ->condition('mail', db_like($mail), 'LIKE')
- ->range(0, 1)
- ->execute()
- ->fetchField();
-
- if ($mail_taken) {
- // Format error message dependent on whether the user is logged in or not.
- if ($GLOBALS['user']->uid) {
- form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail)));
- }
- else {
- form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $mail, '@password' => url('user/password'))));
- }
- }
- }
-
- // Make sure the signature isn't longer than the size of the database field.
- // Signatures are disabled by default, so make sure it exists first.
- if (isset($form_state['values']['signature'])) {
- // Move text format for user signature into 'signature_format'.
- $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
- // Move text value for user signature into 'signature'.
- $form_state['values']['signature'] = $form_state['values']['signature']['value'];
-
- $user_schema = drupal_get_schema('users');
- if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
- form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
- }
- }
-}
-
function user_login_block($form) {
$form['#action'] = url(current_path(), array('query' => drupal_get_destination(), 'external' => FALSE));
$form['#id'] = 'user-login-form';
@@ -1419,6 +1121,11 @@ function user_is_logged_in() {
return (bool) $GLOBALS['user']->uid;
}
+function user_register() {
+ $account = entity_create('user', array());
+ return entity_get_form($account, 'register');
+}
+
function user_register_access() {
return user_is_anonymous() && variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
}
@@ -1498,8 +1205,7 @@ function user_menu() {
$items['user/register'] = array(
'title' => 'Create new account',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('user_register_form'),
+ 'page callback' => 'user_register',
'access callback' => 'user_register_access',
'type' => MENU_LOCAL_TASK,
);
@@ -1652,8 +1358,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,
@@ -3496,157 +3202,6 @@ function user_form_field_ui_field_edit_form_submit($form, &$form_state) {
}
/**
- * Form builder; the user registration form.
- *
- * @ingroup forms
- * @see user_account_form()
- * @see user_account_form_validate()
- * @see user_register_submit()
- */
-function user_register_form($form, &$form_state) {
- global $user;
-
- $admin = user_access('administer users');
-
- // Pass access information to the submit handler. Running an access check
- // inside the submit function interferes with form processing and breaks
- // hook_form_alter().
- $form['administer_users'] = array(
- '#type' => 'value',
- '#value' => $admin,
- );
-
- // If we aren't admin but already logged on, go to the user page instead.
- if (!$admin && $user->uid) {
- drupal_goto('user/' . $user->uid);
- }
-
- $form['#user'] = entity_create('user', array());
-
- $form['#attached']['library'][] = array('system', 'jquery.cookie');
- $form['#attributes']['class'][] = 'user-info-from-cookie';
-
- // Start with the default user account fields.
- user_account_form($form, $form_state);
-
- // Attach field widgets, and hide the ones where the 'user_register_form'
- // setting is not on.
- field_attach_form('user', $form['#user'], $form, $form_state);
- foreach (field_info_instances('user', 'user') as $field_name => $instance) {
- if (empty($instance['settings']['user_register_form'])) {
- $form[$field_name]['#access'] = FALSE;
- }
- }
-
- if ($admin) {
- // Redirect back to page which initiated the create request;
- // usually admin/people/create.
- $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()
- */
-function user_register_submit($form, &$form_state) {
- $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.
- if ($status =! SAVED_NEW) {
- drupal_set_message(t("Error saving user account."), 'error');
- $form_state['redirect'] = '';
- return;
- }
- $form_state['user'] = $account;
- $form_state['values']['uid'] = $account->uid;
-
- watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
-
- // Add plain text password into user account to generate mail tokens.
- $account->password = $pass;
-
- // New administrative account without notification.
- $uri = entity_uri('user', $account);
- if ($admin && !$notify) {
- drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
- }
- // No e-mail verification required; log in user immediately.
- elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
- _user_mail_notify('register_no_approval_required', $account);
- $form_state['uid'] = $account->uid;
- user_login_submit(array(), $form_state);
- drupal_set_message(t('Registration successful. You are now logged in.'));
- $form_state['redirect'] = '';
- }
- // No administrator approval required.
- elseif ($account->status || $notify) {
- if (empty($account->mail) && $notify) {
- drupal_set_message(t('The new user %name was created without an email address, so no welcome message was sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
- }
- else {
- $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
- _user_mail_notify($op, $account);
- if ($notify) {
- drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
- }
- else {
- drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
- $form_state['redirect'] = '';
- }
- }
- }
- // Administrator approval required.
- else {
- _user_mail_notify('register_pending_approval', $account);
- drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
- $form_state['redirect'] = '';
- }
-}
-
-/**
* Implements hook_modules_installed().
*/
function user_modules_installed($modules) {
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 434de72..9121c44 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -204,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) {
@@ -293,7 +213,8 @@ function user_edit_cancel_submit($form, &$form_state) {
unset($_GET['destination']);
}
// Note: We redirect from user/uid/edit to user/uid/cancel to make the tabs disappear.
- $form_state['redirect'] = array("user/" . $form['#user']->uid . "/cancel", array('query' => $destination));
+ $account = $form_state['controller']->getEntity($form_state);
+ $form_state['redirect'] = array("user/" . $account->uid . "/cancel", array('query' => $destination));
}
/**