Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.476
diff -u -p -r1.476 form.inc
--- includes/form.inc	7 Jul 2010 17:56:42 -0000	1.476
+++ includes/form.inc	9 Jul 2010 18:00:19 -0000
@@ -657,6 +657,7 @@ function drupal_retrieve_form($form_id, 
     }
     if (isset($form_definition['callback'])) {
       $callback = $form_definition['callback'];
+      $form_state['build_info']['base_form_id'] = $callback;
     }
     // In case $form_state['wrapper_callback'] is not defined already, we also
     // allow hook_forms() to define one.
@@ -856,20 +857,37 @@ function drupal_prepare_form($form_id, &
   $form += array('#tree' => FALSE, '#parents' => array());
 
   if (!isset($form['#validate'])) {
+    // Check for a handler specific to $form_id.
     if (function_exists($form_id . '_validate')) {
-      $form['#validate'] = array($form_id . '_validate');
+      $form['#validate'][] = $form_id . '_validate';
+    }
+    // Otherwise check whether this is a shared form and whether there is a
+    // handler for the shared $form_id.
+    elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) {
+      $form['#validate'][] = $form_state['build_info']['base_form_id'] . '_validate';
     }
   }
 
   if (!isset($form['#submit'])) {
+    // Check for a handler specific to $form_id.
     if (function_exists($form_id . '_submit')) {
-      // We set submit here so that it can be altered.
-      $form['#submit'] = array($form_id . '_submit');
+      $form['#submit'][] = $form_id . '_submit';
+    }
+    // Otherwise check whether this is a shared form and whether there is a
+    // handler for the shared $form_id.
+    elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
+      $form['#submit'][] = $form_state['build_info']['base_form_id'] . '_submit';
     }
   }
 
-  // Invoke hook_form_alter() and hook_form_FORM_ID_alter() implementations.
-  drupal_alter(array('form', 'form_' . $form_id), $form, $form_state, $form_id);
+  // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
+  // hook_form_FORM_ID_alter() implementations.
+  $hooks = array('form');
+  if (isset($form_state['build_info']['base_form_id'])) {
+    $hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
+  }
+  $hooks[] = 'form_' . $form_id;
+  drupal_alter($hooks, $form, $form_state, $form_id);
 }
 
 
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.547
diff -u -p -r1.547 book.module
--- modules/book/book.module	7 Jul 2010 01:07:33 -0000	1.547
+++ modules/book/book.module	9 Jul 2010 18:00:19 -0000
@@ -387,35 +387,33 @@ function book_get_books() {
 }
 
 /**
- * Implements hook_form_alter().
+ * Implements hook_form_BASE_FORM_ID_alter().
  *
  * Adds the book fieldset to the node form.
+ *
+ * @see book_pick_book_nojs_submit()
  */
-function book_form_alter(&$form, &$form_state, $form_id) {
-  if (!empty($form['#node_edit_form'])) {
-    // Add elements to the node form.
-    $node = $form['#node'];
-
-    $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))) {
-        // Already in the book hierarchy, or this node type is allowed.
-        $access = TRUE;
-      }
+function book_form_node_form_alter(&$form, &$form_state, $form_id) {
+  $node = $form['#node'];
+  $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))) {
+      // Already in the book hierarchy, or this node type is allowed.
+      $access = TRUE;
     }
+  }
 
-    if ($access) {
-      _book_add_form_elements($form, $form_state, $node);
-      // Since the "Book" dropdown can't trigger a form submission when
-      // JavaScript is disabled, add a submit button to do that. book.css hides
-      // this button when JavaScript is enabled.
-      $form['book']['pick-book'] = array(
-        '#type' => 'submit',
-        '#value' => t('Change book (update list of parents)'),
-        '#submit' => array('book_pick_book_nojs_submit'),
-        '#weight' => 20,
-      );
-    }
+  if ($access) {
+    _book_add_form_elements($form, $form_state, $node);
+    // Since the "Book" dropdown can't trigger a form submission when
+    // JavaScript is disabled, add a submit button to do that. book.css hides
+    // this button when JavaScript is enabled.
+    $form['book']['pick-book'] = array(
+      '#type' => 'submit',
+      '#value' => t('Change book (update list of parents)'),
+      '#submit' => array('book_pick_book_nojs_submit'),
+      '#weight' => 20,
+    );
   }
 }
 
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.883
diff -u -p -r1.883 comment.module
--- modules/comment/comment.module	23 Jun 2010 02:45:35 -0000	1.883
+++ modules/comment/comment.module	9 Jul 2010 18:00:19 -0000
@@ -682,7 +682,7 @@ function comment_node_view($node, $view_
     }
 
     $node->content['links']['comment'] = array(
-      '#theme' => 'links__comment_node',
+      '#theme' => 'links__comment_node__' . $node->type,
       '#links' => $links,
       '#attributes' => array('class' => array('links', 'inline')),
     );
@@ -691,7 +691,7 @@ function comment_node_view($node, $view_
     // page. We compare $node and $page_node to ensure that comments are not
     // appended to other nodes shown on the page, for example a node_reference
     // displayed in 'full' view mode within another node.
-    if ($node->comment && node_is_page($node) && empty($node->in_preview) && user_access('access comments')) {
+    if ($node->comment && node_is_page($node) && empty($node->in_preview)) {
       $node->content['comments'] = comment_node_page_additions($node);
     }
   }
@@ -709,7 +709,7 @@ function comment_node_page_additions($no
   // Only attempt to render comments if the node has visible comments.
   // Unpublished comments are not included in $node->comment_count, so show
   // comments unconditionally if the user is an administrator.
-  if ($node->comment_count || user_access('administer comments')) {
+  if (($node->comment_count && user_access('access comments')) || user_access('administer comments')) {
     $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
     $comments_per_page = variable_get('comment_default_per_page_' . $node->type, 50);
     if ($cids = comment_get_thread($node, $mode, $comments_per_page)) {
@@ -724,13 +724,13 @@ function comment_node_page_additions($no
 
   // 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)) {
-    $build = drupal_get_form('comment_form', (object) array('nid' => $node->nid));
+    $build = drupal_get_form($node->type . '_comment_form', (object) array('nid' => $node->nid));
     $additions['comment_form'] = $build;
   }
 
   if ($additions) {
     $additions += array(
-      '#theme' => 'comment_wrapper',
+      '#theme' => 'comment_wrapper__' . $node->type,
       '#node' => $node,
       'comments' => array(),
       'comment_form' => array(),
@@ -907,7 +907,7 @@ function comment_view($comment, $node, $
   unset($comment->content);
 
   $build += array(
-    '#theme' => 'comment',
+    '#theme' => 'comment__' . $node->type,
     '#comment' => $comment,
     '#node' => $node,
     '#view_mode' => $view_mode,
@@ -1138,69 +1138,67 @@ function comment_form_node_type_form_alt
 }
 
 /**
- * Implements hook_form_alter().
+ * Implements hook_form_BASE_FORM_ID_alter().
  */
-function comment_form_alter(&$form, $form_state, $form_id) {
-  if (!empty($form['#node_edit_form'])) {
-    $node = $form['#node'];
-    $form['comment_settings'] = array(
-      '#type' => 'fieldset',
-      '#access' => user_access('administer comments'),
-      '#title' => t('Comment settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#group' => 'additional_settings',
-      '#attached' => array(
-        'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'),
-       ),
-      '#weight' => 30,
-    );
-    $comment_count = isset($node->nid) ? db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() : 0;
-    $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment;
-    $form['comment_settings']['comment'] = array(
-      '#type' => 'radios',
+function comment_form_node_form_alter(&$form, $form_state) {
+  $node = $form['#node'];
+  $form['comment_settings'] = array(
+    '#type' => 'fieldset',
+    '#access' => user_access('administer comments'),
+    '#title' => t('Comment settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#group' => 'additional_settings',
+    '#attached' => array(
+      'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'),
+     ),
+    '#weight' => 30,
+  );
+  $comment_count = isset($node->nid) ? db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() : 0;
+  $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment;
+  $form['comment_settings']['comment'] = array(
+    '#type' => 'radios',
+    '#parents' => array('comment'),
+    '#default_value' => $comment_settings,
+    '#options' => array(
+      COMMENT_NODE_OPEN => t('Open'),
+      COMMENT_NODE_CLOSED => t('Closed'),
+      COMMENT_NODE_HIDDEN => t('Hidden'),
+    ),
+    COMMENT_NODE_OPEN => array(
+      '#type' => 'radio',
+      '#title' => t('Open'),
+      '#description' => t('Users with the "Post comments" permission can post comments.'),
+      '#return_value' => COMMENT_NODE_OPEN,
+      '#default_value' => $comment_settings,
+      '#id' => 'edit-comment-2',
       '#parents' => array('comment'),
+    ),
+    COMMENT_NODE_CLOSED => array(
+      '#type' => 'radio',
+      '#title' => t('Closed'),
+      '#description' => t('Users cannot post comments, but existing comments will be displayed.'),
+      '#return_value' => COMMENT_NODE_CLOSED,
       '#default_value' => $comment_settings,
-      '#options' => array(
-        COMMENT_NODE_OPEN => t('Open'),
-        COMMENT_NODE_CLOSED => t('Closed'),
-        COMMENT_NODE_HIDDEN => t('Hidden'),
-      ),
-      COMMENT_NODE_OPEN => array(
-        '#type' => 'radio',
-        '#title' => t('Open'),
-        '#description' => t('Users with the "Post comments" permission can post comments.'),
-        '#return_value' => COMMENT_NODE_OPEN,
-        '#default_value' => $comment_settings,
-        '#id' => 'edit-comment-2',
-        '#parents' => array('comment'),
-      ),
-      COMMENT_NODE_CLOSED => array(
-        '#type' => 'radio',
-        '#title' => t('Closed'),
-        '#description' => t('Users cannot post comments, but existing comments will be displayed.'),
-        '#return_value' => COMMENT_NODE_CLOSED,
-        '#default_value' => $comment_settings,
-        '#id' => 'edit-comment-1',
-        '#parents' => array('comment'),
-      ),
-      COMMENT_NODE_HIDDEN => array(
-        '#type' => 'radio',
-        '#title' => t('Hidden'),
-        '#description' => t('Comments are hidden from view.'),
-        '#return_value' => COMMENT_NODE_HIDDEN,
-        '#default_value' => $comment_settings,
-        '#id' => 'edit-comment-0',
-        '#parents' => array('comment'),
-      ),
-    );
-    // If the node doesn't have any comments, the "hidden" option makes no
-    // sense, so don't even bother presenting it to the user.
-    if (empty($comment_count)) {
-      unset($form['comment_settings']['comment']['#options'][COMMENT_NODE_HIDDEN]);
-      unset($form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]);
-      $form['comment_settings']['comment'][COMMENT_NODE_CLOSED]['#description'] = t('Users cannot post comments.');
-    }
+      '#id' => 'edit-comment-1',
+      '#parents' => array('comment'),
+    ),
+    COMMENT_NODE_HIDDEN => array(
+      '#type' => 'radio',
+      '#title' => t('Hidden'),
+      '#description' => t('Comments are hidden from view.'),
+      '#return_value' => COMMENT_NODE_HIDDEN,
+      '#default_value' => $comment_settings,
+      '#id' => 'edit-comment-0',
+      '#parents' => array('comment'),
+    ),
+  );
+  // If the node doesn't have any comments, the "hidden" option makes no
+  // sense, so don't even bother presenting it to the user.
+  if (empty($comment_count)) {
+    unset($form['comment_settings']['comment']['#options'][COMMENT_NODE_HIDDEN]);
+    unset($form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]);
+    $form['comment_settings']['comment'][COMMENT_NODE_CLOSED]['#description'] = t('Users cannot post comments.');
   }
 }
 
@@ -1748,7 +1746,21 @@ function comment_get_display_page($cid, 
  */
 function comment_edit_page($comment) {
   drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject)), PASS_THROUGH);
-  return drupal_get_form('comment_form', $comment);
+  $node = node_load($comment->nid);
+  return drupal_get_form($node->type . '_comment_form', $comment);
+}
+
+/**
+ * Implements hook_forms().
+ */
+function comment_forms() {
+  $forms = array();
+  if ($types = node_type_get_types()) {
+    foreach (array_keys($types) as $type) {
+      $forms[$type . '_comment_form']['callback'] = 'comment_form';
+    }
+  }
+  return $forms;
 }
 
 /**
@@ -1791,6 +1803,10 @@ function comment_form($form, &$form_stat
   $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['#attributes']['class'][] = 'comment-form';
+
   $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
   $is_admin = (!empty($comment->cid) && user_access('administer comments'));
 
Index: modules/comment/comment.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.pages.inc,v
retrieving revision 1.39
diff -u -p -r1.39 comment.pages.inc
--- modules/comment/comment.pages.inc	10 Jun 2010 06:57:20 -0000	1.39
+++ modules/comment/comment.pages.inc	9 Jul 2010 18:00:19 -0000
@@ -37,7 +37,7 @@ function comment_reply($node, $pid = NUL
     // The user is previewing a comment prior to submitting it.
     if ($op == t('Preview')) {
       if (user_access('post comments')) {
-        $build['comment_form'] = drupal_get_form('comment_form', (object) array('pid' => $pid, 'nid' => $node->nid));
+        $build['comment_form'] = drupal_get_form($node->type . '_comment_form', (object) array('pid' => $pid, 'nid' => $node->nid));
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
@@ -83,7 +83,7 @@ function comment_reply($node, $pid = NUL
       }
       elseif (user_access('post comments')) {
         $edit = array('nid' => $node->nid, 'pid' => $pid);
-        $build['comment_form'] = drupal_get_form('comment_form', (object) $edit);
+        $build['comment_form'] = drupal_get_form($node->type . '_comment_form', (object) $edit);
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.293
diff -u -p -r1.293 locale.module
--- modules/locale/locale.module	12 May 2010 08:26:14 -0000	1.293
+++ modules/locale/locale.module	9 Jul 2010 18:00:19 -0000
@@ -377,7 +377,7 @@ function locale_multilingual_node_type($
 /**
  * Implements hook_form_alter().
  *
- * Adds language fields to forms.
+ * Adds language fields to user forms.
  */
 function locale_form_alter(&$form, &$form_state, $form_id) {
   // Only alter user forms if there is more than one language.
@@ -388,25 +388,29 @@ function locale_form_alter(&$form, &$for
       locale_language_selector_form($form, $form_state, $form['#user']);
     }
   }
-  if (!empty($form['#node_edit_form'])) {
-    if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
-      $form['language'] = array(
-        '#type' => 'select',
-        '#title' => t('Language'),
-        '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
-        '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
-      );
-    }
-    // Node type without language selector: assign the default for new nodes
-    elseif (!isset($form['#node']->nid)) {
-      $default = language_default();
-      $form['language'] = array(
-        '#type' => 'value',
-        '#value' => $default->language
-      );
-    }
-    $form['#submit'][] = 'locale_field_node_form_submit';
+}
+
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ */
+function locale_form_node_form_alter(&$form, &$form_state) {
+  if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
+    $form['language'] = array(
+      '#type' => 'select',
+      '#title' => t('Language'),
+      '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
+      '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
+    );
+  }
+  // Node type without language selector: assign the default for new nodes
+  elseif (!isset($form['#node']->nid)) {
+    $default = language_default();
+    $form['language'] = array(
+      '#type' => 'value',
+      '#value' => $default->language
+    );
   }
+  $form['#submit'][] = 'locale_form_node_form_submit';
 }
 
 /**
@@ -415,7 +419,7 @@ function locale_form_alter(&$form, &$for
  * Checks if Locale is registered as a translation handler and handle possible
  * node language changes.
  */
-function locale_field_node_form_submit($form, &$form_state) {
+function locale_form_node_form_submit($form, &$form_state) {
   if (field_has_translation_handler('node', 'locale')) {
     $node = (object) $form_state['values'];
     $available_languages = field_content_languages();
@@ -993,7 +997,7 @@ function locale_url_outbound_alter(&$pat
   }
 }
 
-/*
+/**
  * Implements hook_form_FORM_ID_alter().
  */
 function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.230
diff -u -p -r1.230 menu.module
--- modules/menu/menu.module	17 Jun 2010 13:44:45 -0000	1.230
+++ modules/menu/menu.module	9 Jul 2010 18:00:19 -0000
@@ -583,84 +583,88 @@ function _menu_parent_depth_limit($item)
 }
 
 /**
- * Implements hook_form_alter(). Adds menu item fields to the node form.
+ * Implements hook_form_BASE_FORM_ID_alter().
+ *
+ * Adds menu item fields to the node form.
+ *
+ * @see menu_node_submit()
  */
-function menu_form_alter(&$form, $form_state, $form_id) {
-  if (!empty($form['#node_edit_form'])) {
-    // Generate a list of possible parents.
-    // @todo This must be handled in a #process handler.
-    $type = $form['#node']->type;
-    $options = menu_parent_options(menu_get_menus(), $type);
-    // If no possible parent menu items were found, there is nothing to display.
-    if (empty($options)) {
-      return;
-    }
-    $link = $form['#node']->menu;
+function menu_form_node_form_alter(&$form, $form_state) {
+  // Generate a list of possible parents.
+  // @todo This must be handled in a #process handler.
+  $type = $form['#node']->type;
+  $options = menu_parent_options(menu_get_menus(), $type);
+  // If no possible parent menu items were found, there is nothing to display.
+  if (empty($options)) {
+    return;
+  }
+  $link = $form['#node']->menu;
 
-    $form['menu'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Menu settings'),
-      '#access' => user_access('administer menu'),
-      '#collapsible' => TRUE,
-      '#collapsed' => !$link['link_title'],
-      '#group' => 'additional_settings',
-      '#attached' => array(
-        'js' => array(drupal_get_path('module', 'menu') . '/menu.js'),
-      ),
-      '#tree' => TRUE,
-      '#weight' => -2,
-      '#attributes' => array('class' => array('menu-link-form')),
-    );
-    $form['menu']['enabled'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Provide a menu link'),
-      '#default_value' => (int) (bool) $link['mlid'],
-    );
-    $form['menu']['link'] = array(
-      '#type' => 'container',
-      '#parents' => array('menu'),
-      '#states' => array(
-        'invisible' => array(
-          'input[name="menu[enabled]"]' => array('checked' => FALSE),
-        ),
+  $form['menu'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Menu settings'),
+    '#access' => user_access('administer menu'),
+    '#collapsible' => TRUE,
+    '#collapsed' => !$link['link_title'],
+    '#group' => 'additional_settings',
+    '#attached' => array(
+      'js' => array(drupal_get_path('module', 'menu') . '/menu.js'),
+    ),
+    '#tree' => TRUE,
+    '#weight' => -2,
+    '#attributes' => array('class' => array('menu-link-form')),
+  );
+  $form['menu']['enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Provide a menu link'),
+    '#default_value' => (int) (bool) $link['mlid'],
+  );
+  $form['menu']['link'] = array(
+    '#type' => 'container',
+    '#parents' => array('menu'),
+    '#states' => array(
+      'invisible' => array(
+        'input[name="menu[enabled]"]' => array('checked' => FALSE),
       ),
-    );
+    ),
+  );
 
-    // Populate the element with the link data.
-    foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) {
-      $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]);
-    }
+  // Populate the element with the link data.
+  foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) {
+    $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]);
+  }
 
-    $form['menu']['link']['link_title'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Menu link title'),
-      '#default_value' => $link['link_title'],
-    );
-
-    $default = ($link['mlid'] ? $link['menu_name'] . ':' . $link['plid'] : variable_get('menu_parent_' . $type, 'main-menu:0'));
-    // @todo This will fail with the new selective menus per content type.
-    if (!isset($options[$default])) {
-      $default = 'navigation:0';
-    }
-    $form['menu']['link']['parent'] = array(
-      '#type' => 'select',
-      '#title' => t('Parent item'),
-      '#default_value' => $default,
-      '#options' => $options,
-      '#attributes' => array('class' => array('menu-parent-select')),
-    );
-    $form['menu']['link']['weight'] = array(
-      '#type' => 'weight',
-      '#title' => t('Weight'),
-      '#delta' => 50,
-      '#default_value' => $link['weight'],
-      '#description' => t('Menu links with smaller weights are displayed before links with larger weights.'),
-    );
+  $form['menu']['link']['link_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Menu link title'),
+    '#default_value' => $link['link_title'],
+  );
+
+  $default = ($link['mlid'] ? $link['menu_name'] . ':' . $link['plid'] : variable_get('menu_parent_' . $type, 'main-menu:0'));
+  // @todo This will fail with the new selective menus per content type.
+  if (!isset($options[$default])) {
+    $default = 'navigation:0';
   }
+  $form['menu']['link']['parent'] = array(
+    '#type' => 'select',
+    '#title' => t('Parent item'),
+    '#default_value' => $default,
+    '#options' => $options,
+    '#attributes' => array('class' => array('menu-parent-select')),
+  );
+  $form['menu']['link']['weight'] = array(
+    '#type' => 'weight',
+    '#title' => t('Weight'),
+    '#delta' => 50,
+    '#default_value' => $link['weight'],
+    '#description' => t('Menu links with smaller weights are displayed before links with larger weights.'),
+  );
 }
 
 /**
  * Implements hook_node_submit().
+ *
+ * @see menu_form_node_form_alter()
  */
 function menu_node_submit($node, $form, $form_state) {
   // Decompose the selected menu parent option into 'menu_name' and 'plid', if
@@ -671,7 +675,8 @@ function menu_node_submit($node, $form, 
 }
 
 /**
- * Implements hook_form_FORM_ID_alter() for the node type form.
+ * Implements hook_form_FORM_ID_alter().
+ *
  * Adds menu options to the node type form.
  */
 function menu_form_node_type_form_alter(&$form, $form_state) {
Index: modules/node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.127
diff -u -p -r1.127 node.pages.inc
--- modules/node/node.pages.inc	17 Jun 2010 13:44:45 -0000	1.127
+++ modules/node/node.pages.inc	9 Jul 2010 18:00:19 -0000
@@ -117,7 +117,9 @@ function node_form($form, &$form_state, 
   }
 
   // Identify this as a node edit form.
+  // @todo Remove in D8.
   $form['#node_edit_form'] = TRUE;
+
   $form['#attributes']['class'][] = 'node-form';
   if (!empty($node->type)) {
     $form['#attributes']['class'][] = 'node-' . $node->type . '-form';
@@ -282,7 +284,11 @@ function node_form($form, &$form_state, 
       '#submit' => array('node_form_delete_submit'),
     );
   }
+  // Unlike others, this form uses a button-level submit handler for the main
+  // action, too. We therefore explicitly define #validate and #submit handlers
+  // to skip any handlers automatically assigned by Form API by default.
   $form['#validate'][] = 'node_form_validate';
+  $form['#submit'] = array();
 
   $form['#builder_function'] = 'node_form_submit_build_node';
   field_attach_form('node', $node, $form, $form_state, $node->language);
Index: modules/path/path.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.module,v
retrieving revision 1.183
diff -u -p -r1.183 path.module
--- modules/path/path.module	13 Feb 2010 21:41:58 -0000	1.183
+++ modules/path/path.module	9 Jul 2010 18:00:19 -0000
@@ -94,55 +94,53 @@ function path_menu() {
 }
 
 /**
- * Implements hook_form_alter().
+ * Implements hook_form_BASE_FORM_ID_alter().
  */
-function path_form_alter(&$form, $form_state, $form_id) {
-  if (!empty($form['#node_edit_form'])) {
-    $path = array();
-    if (!empty($form['#node']->nid)) {
-      $conditions = array('source' => 'node/' . $form['#node']->nid);
-      if ($form['#node']->language != LANGUAGE_NONE) {
-        $conditions['language'] = $form['#node']->language;
-      }
-      $path = path_load($conditions);
-      if ($path === FALSE) {
-        $path = array();
-      }
+function path_form_node_form_alter(&$form, $form_state) {
+  $path = array();
+  if (!empty($form['#node']->nid)) {
+    $conditions = array('source' => 'node/' . $form['#node']->nid);
+    if ($form['#node']->language != LANGUAGE_NONE) {
+      $conditions['language'] = $form['#node']->language;
+    }
+    $path = path_load($conditions);
+    if ($path === FALSE) {
+      $path = array();
     }
-    $path += array(
-      'pid' => NULL,
-      'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL,
-      'alias' => '',
-      'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE,
-    );
-
-    $form['path'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('URL path settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => empty($path['alias']),
-      '#group' => 'additional_settings',
-      '#attached' => array(
-        'js' => array(drupal_get_path('module', 'path') . '/path.js'),
-      ),
-      '#access' => user_access('create url aliases') || user_access('administer url aliases'),
-      '#weight' => 30,
-      '#tree' => TRUE,
-      '#element_validate' => array('path_form_element_validate'),
-    );
-    $form['path']['alias'] = array(
-      '#type' => 'textfield',
-      '#title' => t('URL alias'),
-      '#default_value' => $path['alias'],
-      '#maxlength' => 255,
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
-    );
-    $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
-    $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
-    $form['path']['language'] = array('#type' => 'value', '#value' => $path['language']);
   }
+  $path += array(
+    'pid' => NULL,
+    'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL,
+    'alias' => '',
+    'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE,
+  );
+
+  $form['path'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('URL path settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => empty($path['alias']),
+    '#group' => 'additional_settings',
+    '#attached' => array(
+      'js' => array(drupal_get_path('module', 'path') . '/path.js'),
+    ),
+    '#access' => user_access('create url aliases') || user_access('administer url aliases'),
+    '#weight' => 30,
+    '#tree' => TRUE,
+    '#element_validate' => array('path_form_element_validate'),
+  );
+  $form['path']['alias'] = array(
+    '#type' => 'textfield',
+    '#title' => t('URL alias'),
+    '#default_value' => $path['alias'],
+    '#maxlength' => 255,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
+  );
+  $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
+  $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
+  $form['path']['language'] = array('#type' => 'value', '#value' => $path['language']);
 }
 
 /**
Index: modules/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.352
diff -u -p -r1.352 poll.module
--- modules/poll/poll.module	24 Jun 2010 18:53:31 -0000	1.352
+++ modules/poll/poll.module	9 Jul 2010 19:16:57 -0000
@@ -294,6 +294,7 @@ function poll_form($node, &$form_state) 
 
   // Add initial or additional choices.
   $existing_delta = $delta;
+  $weight++;
   for ($delta; $delta < $choice_count; $delta++) {
     $key = 'new:' . ($delta - $existing_delta);
     $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count);
@@ -383,6 +384,7 @@ function poll_more_choices_submit($form,
 function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight = 0, $size = 10) {
   $form = array(
     '#tree' => TRUE,
+    '#weight' => $weight,
   );
 
   // We'll manually set the #parents property of these fields so that
@@ -433,8 +435,8 @@ function poll_choice_js($form, $form_sta
 /**
  * Renumber fields and create a teaser when a poll node is submitted.
  */
-function poll_node_form_submit(&$form, &$form_state) {
-  // Renumber fields
+function poll_node_form_submit($form, &$form_state) {
+  // Renumber fields.
   $form_state['values']['choice'] = array_values($form_state['values']['choice']);
   $form_state['values']['teaser'] = poll_teaser((object) $form_state['values']);
 }
@@ -821,14 +823,14 @@ function theme_poll_choices($variables) 
 
   drupal_add_tabledrag('poll-choice-table', 'order', 'sibling', 'poll-weight');
 
+  $is_admin= user_access('administer nodes');
   $delta = 0;
   $rows = array();
-  $headers = array(
-    '',
-    t('Choice'),
-    t('Vote count'),
-    t('Weight'),
-  );
+  $headers = array('', t('Choice'));
+  if ($is_admin) {
+    $headers[] = t('Vote count');
+  }
+  $headers[] = t('Weight');
 
   foreach (element_children($form) as $key) {
     $delta++;
@@ -840,11 +842,13 @@ function theme_poll_choices($variables) 
       'data' => array(
         array('class' => array('choice-flag')),
         drupal_render($form[$key]['chtext']),
-        drupal_render($form[$key]['chvotes']),
-        drupal_render($form[$key]['weight']),
       ),
       'class' => array('draggable'),
     );
+    if ($is_admin) {
+      $row['data'][] = drupal_render($form[$key]['chvotes']);
+    }
+    $row['data'][] = drupal_render($form[$key]['weight']);
 
     // Add any additional classes set on the row.
     if (!empty($form[$key]['#attributes']['class'])) {
Index: modules/poll/poll.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.test,v
retrieving revision 1.35
diff -u -p -r1.35 poll.test
--- modules/poll/poll.test	23 Jun 2010 19:15:07 -0000	1.35
+++ modules/poll/poll.test	9 Jul 2010 18:48:08 -0000
@@ -11,10 +11,15 @@ class PollTestCase extends DrupalWebTest
   /**
    * Creates a poll.
    *
-   * @param string $title The title of the poll.
-   * @param array $choices Choices.
-   * @param boolean $test_preview Whether to test if the preview is working or not.
-   * @return integer The nid of the created poll, or FALSE on error.
+   * @param string $title
+   *   The title of the poll.
+   * @param array $choices
+   *   A list of choice labels.
+   * @param boolean $test_preview
+   *   Whether to test if the preview is working or not.
+   *
+   * @return
+   *   The node id of the created poll, or FALSE on error.
    */
   function pollCreate($title, $choices, $test_preview = TRUE) {
     $this->assertTrue(TRUE, 'Create a poll');
@@ -28,8 +33,8 @@ class PollTestCase extends DrupalWebTest
     // Prepare a form with two choices
     list($edit, $index) = $this->_pollGenerateEdit($title, $choices);
 
+    // Re-submit the form until all choices are filled in.
     if (count($choices) > 2) {
-      // Re-submit the form while the choices are all in
       while ($index < count($choices)) {
         $this->drupalPost(NULL, $edit, t('More choices'));
         list($edit, $index) = $this->_pollGenerateEdit($title, $choices, $index);
@@ -57,9 +62,8 @@ class PollTestCase extends DrupalWebTest
     $already_submitted_choices = array_slice($choices, 0, $index);
     $new_choices = array_values(array_slice($choices, $index, $max_new_choices));
 
-    $langcode = LANGUAGE_NONE;
     $edit = array(
-      "title" => $title
+      'title' => $title,
     );
     foreach ($already_submitted_choices as $k => $text) {
       $edit['choice[chid:' . $k . '][chtext]'] = $text;
@@ -332,7 +336,6 @@ class PollJSAddChoice extends DrupalWebT
     $web_user = $this->drupalCreateUser(array('create poll content', 'access content'));
     $this->drupalLogin($web_user);
     $this->drupalGet('node/add/poll');
-    $langcode = LANGUAGE_NONE;
     $edit = array(
       "title" => $this->randomName(),
       'choice[new:0][chtext]' => $this->randomName(),
@@ -343,6 +346,7 @@ class PollJSAddChoice extends DrupalWebT
     // as the tested content.
     $commands = $this->drupalPostAJAX(NULL, $edit, array('op' => t('More choices')));
     $this->content = $commands[1]['data'];
+    $this->verbose('AJAX result on: ' . $this->getUrl() . '<hr />' . $this->content);
 
     $this->assertFieldByName('choice[chid:0][chtext]', $edit['choice[new:0][chtext]'], t('Field !i found', array('!i' => 0)));
     $this->assertFieldByName('choice[chid:1][chtext]', $edit['choice[new:1][chtext]'], t('Field !i found', array('!i' => 1)));
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.178
diff -u -p -r1.178 system.api.php
--- modules/system/system.api.php	7 Jul 2010 08:05:01 -0000	1.178
+++ modules/system/system.api.php	9 Jul 2010 18:00:19 -0000
@@ -1346,6 +1346,40 @@ function hook_form_FORM_ID_alter(&$form,
 }
 
 /**
+ * Provide a form-specific alteration for shared forms.
+ *
+ * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
+ * form belonging to multiple form_ids, rather than implementing
+ * hook_form_alter() and checking for conditions that would identify the
+ * shared form constructor.
+ *
+ * Examples for such forms are node_form() or comment_form().
+ *
+ * Note that this hook fires after hook_form_FORM_ID_alter() and before
+ * hook_form_alter().
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form.
+ *
+ * @see hook_form_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ */
+function hook_form_BASE_FORM_ID_alter(&$form, &$form_state) {
+  // Modification for the form with the given BASE_FORM_ID goes here. For
+  // example, if BASE_FORM_ID is "node_form", this code would run on every
+  // node form, regardless of node type.
+
+  // Add a checkbox to the node form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
  * Map form_ids to form builder functions.
  *
  * By default, when drupal_get_form() is called, the system will look for a
Index: modules/translation/translation.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.module,v
retrieving revision 1.80
diff -u -p -r1.80 translation.module
--- modules/translation/translation.module	6 Jun 2010 00:24:16 -0000	1.80
+++ modules/translation/translation.module	9 Jul 2010 18:00:19 -0000
@@ -123,8 +123,8 @@ function translation_form_node_type_form
  * - Alters language fields on node forms when a translation
  *   is about to be created.
  */
-function translation_form_alter(&$form, &$form_state, $form_id) {
-  if (!empty($form['#node_edit_form']) && translation_supported_type($form['#node']->type)) {
+function translation_form_node_form_alter(&$form, &$form_state) {
+  if (translation_supported_type($form['#node']->type)) {
     $node = $form['#node'];
     if (!empty($node->translation_source)) {
       // We are creating a translation. Add values and lock language field.
