Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.328
diff -u -p -r1.328 menu.inc
--- includes/menu.inc	10 Jun 2009 21:52:36 -0000	1.328
+++ includes/menu.inc	18 Jun 2009 15:56:04 -0000
@@ -1123,7 +1123,7 @@ function menu_tree_check_access(&$tree, 
     $nids = array_keys($node_links);
     $select = db_select('node');
     $select->addField('node', 'nid');
-    $select->condition('status', 1);
+    $select->condition('status', NODE_STATUS_PUBLISHED);
     $select->condition('nid', $nids, 'IN');
     $select->addTag('node_access');
     $nids = $select->execute()->fetchCol();
Index: includes/pager.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/pager.inc,v
retrieving revision 1.67
diff -u -p -r1.67 pager.inc
--- includes/pager.inc	12 May 2009 18:26:41 -0000	1.67
+++ includes/pager.inc	18 Jun 2009 15:56:04 -0000
@@ -162,9 +162,9 @@ class PagerDefault extends SelectQueryEx
  * certain page. However, it has to learn the total number of records returned
  * by the query to compute the number of pages (the number of records / records
  * per page). This is done by inserting "COUNT(*)" in the original query. For
- * example, the query "SELECT nid, type FROM node WHERE status = '1' ORDER BY
+ * example, the query "SELECT nid, type FROM node WHERE status = :status ORDER BY
  * sticky DESC, created DESC" would be rewritten to read "SELECT COUNT(*) FROM
- * node WHERE status = '1' ORDER BY sticky DESC, created DESC". Rewriting the
+ * node WHERE status = :status ORDER BY sticky DESC, created DESC". Rewriting the
  * query is accomplished using a regular expression.
  *
  * Unfortunately, the rewrite rule does not always work as intended for queries
Index: modules/aggregator/aggregator.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.test,v
retrieving revision 1.26
diff -u -p -r1.26 aggregator.test
--- modules/aggregator/aggregator.test	12 Jun 2009 08:39:35 -0000	1.26
+++ modules/aggregator/aggregator.test	18 Jun 2009 15:56:04 -0000
@@ -76,7 +76,7 @@ class AggregatorTestCase extends DrupalW
    */
   function getDefaultFeedItemCount() {
     // Our tests are based off of rss.xml, so let's find out how many elements should be related.
-    $feed_count = db_query_range('SELECT COUNT(*) FROM {node} n WHERE n.promote = 1 AND n.status = 1', 0, variable_get('feed_default_items', 10))->fetchField();
+    $feed_count = db_query_range('SELECT COUNT(*) FROM {node} n WHERE n.promote = 1 AND n.status = :status', array(':status' => NODE_STATUS_PUBLISHED), 0, variable_get('feed_default_items', 10))->fetchField();
     return $feed_count > 10 ? 10 : $feed_count;
   }
 
Index: modules/blog/blog.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v
retrieving revision 1.323
diff -u -p -r1.323 blog.module
--- modules/blog/blog.module	12 Jun 2009 08:39:35 -0000	1.323
+++ modules/blog/blog.module	18 Jun 2009 15:56:04 -0000
@@ -159,7 +159,7 @@ function _blog_post_exists($account) {
     ->fields('n', array('nid'))
     ->condition('type', 'blog')
     ->condition('uid', $account->uid)
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->range(0, 1)
     ->addTag('node_access')
     ->execute()
@@ -186,7 +186,7 @@ function blog_block_view($delta = '') {
     $result = db_select('node', 'n')
       ->fields('n', array('nid', 'title', 'created'))
       ->condition('type', 'blog')
-      ->condition('status', 1)
+      ->condition('status', NODE_STATUS_PUBLISHED)
       ->orderBy('created', 'DESC')
       ->range(0, 10)
       ->addTag('node_access')
Index: modules/blog/blog.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/blog/blog.pages.inc,v
retrieving revision 1.19
diff -u -p -r1.19 blog.pages.inc
--- modules/blog/blog.pages.inc	21 May 2009 21:12:23 -0000	1.19
+++ modules/blog/blog.pages.inc	18 Jun 2009 15:56:04 -0000
@@ -34,7 +34,7 @@ function blog_page_user($account) {
     ->fields('n', array('nid', 'sticky', 'created'))
     ->condition('type', 'blog')
     ->condition('uid', $account->uid)
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->orderBy('sticky', 'DESC')
     ->orderBy('created', 'DESC')
     ->limit(variable_get('default_nodes_main', 10))
@@ -83,7 +83,7 @@ function blog_page_last() {
   $nids = $query
     ->fields('n', array('nid', 'sticky', 'created'))
     ->condition('type', 'blog')
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->orderBy('sticky', 'DESC')
     ->orderBy('created', 'DESC')
     ->limit(variable_get('default_nodes_main', 10))
@@ -116,7 +116,7 @@ function blog_feed_user($account) {
     ->fields('n', array('nid', 'created'))
     ->condition('type', 'blog')
     ->condition('uid', $account->uid)
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->orderBy('created', 'DESC')
     ->range(0, variable_get('feed_default_items', 10))
     ->addTag('node_access')
@@ -136,7 +136,7 @@ function blog_feed_last() {
   $nids = db_select('node', 'n')
     ->fields('n', array('nid', 'created'))
     ->condition('type', 'blog')
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->orderBy('created', 'DESC')
     ->range(0, variable_get('feed_default_items', 10))
     ->addTag('node_access')
Index: modules/blogapi/blogapi.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blogapi/blogapi.module,v
retrieving revision 1.155
diff -u -p -r1.155 blogapi.module
--- modules/blogapi/blogapi.module	12 Jun 2009 08:39:35 -0000	1.155
+++ modules/blogapi/blogapi.module	18 Jun 2009 15:56:04 -0000
@@ -337,7 +337,7 @@ function blogapi_status_error_check($nod
   // changing or for a new node the status is not the content type's default,
   // then return an error.
   if (!user_access('administer nodes') && (($node->status != $original_status) || (empty($node->nid) && $node->status != in_array('status', $node_type_default)))) {
-    if ($node->status) {
+    if ($node->status == NODE_STATUS_PUBLISHED) {
       return blogapi_error(t('You do not have permission to publish this type of post. Please save it as a draft instead.'));
     }
     else {
@@ -650,7 +650,7 @@ function blogapi_mt_publish_post($postid
   }
 
   // Nothing needs to be done if already published.
-  if ($node->status) {
+  if ($node->status == NODE_STATUS_PUBLISHED) {
     return;
   }
 
@@ -658,7 +658,7 @@ function blogapi_mt_publish_post($postid
     return blogapi_error(t('You do not have permission to update this post.'));
   }
 
-  $node->status = 1;
+  $node->status = NODE_STATUS_PUBLISHED;
   node_save($node);
 
   return TRUE;
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.496
diff -u -p -r1.496 book.module
--- modules/book/book.module	12 Jun 2009 08:39:36 -0000	1.496
+++ modules/book/book.module	18 Jun 2009 15:56:04 -0000
@@ -69,7 +69,7 @@ function book_node_view_link($node, $tea
   if (isset($node->book['depth'])) {
     if (!$teaser) {
       $child_type = variable_get('book_child_type', 'book');
-      if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) {
+      if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && ($node->status == NODE_STATUS_PUBLISHED) && $node->book['depth'] < MENU_MAX_DEPTH) {
         $links['book_add_child'] = array(
           'title' => t('Add child page'),
           'href' => 'node/add/' . str_replace('_', '-', $child_type),
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.500
diff -u -p -r1.500 forum.module
--- modules/forum/forum.module	12 Jun 2009 08:39:37 -0000	1.500
+++ modules/forum/forum.module	18 Jun 2009 15:56:04 -0000
@@ -520,8 +520,8 @@ function forum_block_view($delta = '') {
 
       case 'new':
         $title = t('New forum topics');
-        $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count FROM {node} n INNER JOIN {taxonomy_term_node} tn ON tn.vid = n.vid INNER JOIN {taxonomy_term_data} td ON td.tid = tn.tid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 AND td.vid = %d ORDER BY n.nid DESC");
-        $result = db_query_range($sql, variable_get('forum_nav_vocabulary', ''), 0, variable_get('forum_block_num_new', '5'));
+        $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count FROM {node} n INNER JOIN {taxonomy_term_node} tn ON tn.vid = n.vid INNER JOIN {taxonomy_term_data} td ON td.tid = tn.tid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = :status AND td.vid = :vid ORDER BY n.nid DESC");
+        $result = db_query_range($sql, array(':status' => NODE_STATUS_PUBLISHED, ':vid' => variable_get('forum_nav_vocabulary', '')), 0, variable_get('forum_block_num_new', '5'));
         $content = node_title_list($result);
         break;
     }
@@ -585,9 +585,9 @@ function forum_get_forums($tid = 0) {
 
     $counts = array();
 
-    $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid WHERE n.status = 1 GROUP BY r.tid";
+    $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid WHERE n.status = :status GROUP BY r.tid";
     $sql = db_rewrite_sql($sql);
-    $_counts = db_query($sql);
+    $_counts = db_query($sql, array(':status' => NODE_STATUS_PUBLISHED));
     while ($count = db_fetch_object($_counts)) {
       $counts[$count->tid] = $count;
     }
@@ -610,9 +610,9 @@ function forum_get_forums($tid = 0) {
     // This query does not use full ANSI syntax since MySQL 3.x does not support
     // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
     // used to join node_comment_statistics to users.
-    $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {taxonomy_term_node} tn ON n.vid = tn.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
+    $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {taxonomy_term_node} tn ON n.vid = tn.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = :status AND tn.tid = :tid ORDER BY ncs.last_comment_timestamp DESC";
     $sql = db_rewrite_sql($sql);
-    $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
+    $topic = db_fetch_object(db_query_range($sql, array(':status' => NODE_STATUS_PUBLISHED, ':tid' => $forum->tid), 0, 1));
 
     $last_post = new stdClass();
     if (!empty($topic->last_comment_timestamp)) {
@@ -633,9 +633,9 @@ function forum_get_forums($tid = 0) {
  * than NODE_NEW_LIMIT.
  */
 function _forum_topics_unread($term, $uid) {
-  $sql = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {taxonomy_term_node} tn ON n.vid = tn.vid AND tn.tid = %d LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d WHERE n.status = 1 AND n.created > %d AND h.nid IS NULL";
+  $sql = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {taxonomy_term_node} tn ON n.vid = tn.vid AND tn.tid = :tid LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = :uid WHERE n.status = :status AND n.created > :created AND h.nid IS NULL";
   $sql = db_rewrite_sql($sql);
-  return db_result(db_query($sql, $term, $uid, NODE_NEW_LIMIT));
+  return db_result(db_query($sql, array(':tid' => $term, ':uid' => $uid, ':status' => NODE_STATUS_PUBLISHED), NODE_NEW_LIMIT));
 }
 
 function forum_get_topics($tid, $sortby, $forum_per_page) {
@@ -656,13 +656,13 @@ function forum_get_topics($tid, $sortby,
     }
   }
 
-  $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments, f.tid AS forum_tid FROM {node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN {users} cu ON l.last_comment_uid = cu.uid INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid = f.vid WHERE n.status = 1 AND r.tid = %d");
+  $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments, f.tid AS forum_tid FROM {node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN {users} cu ON l.last_comment_uid = cu.uid INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid = f.vid WHERE n.status = :status AND r.tid = :tid");
   $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
   $sql .= ', n.created DESC';  // Always add a secondary sort order so that the news forum topics are on top.
 
-  $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid AND r.tid = %d WHERE n.status = 1");
+  $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {taxonomy_term_node} r ON n.vid = r.vid AND r.tid = :tid WHERE n.status = :status");
 
-  $result = pager_query($sql, $forum_per_page, 0, $sql_count, $tid);
+  $result = pager_query($sql, $forum_per_page, 0, $sql_count, array(':status' => NODE_STATUS_PUBLISHED, ':tid' => $tid));
   $topics = array();
   while ($topic = db_fetch_object($result)) {
     if ($user->uid) {
@@ -951,8 +951,8 @@ function template_preprocess_forum_topic
   $output = '';
 
   // Get previous and next topic.
-  $sql = "SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {taxonomy_term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 ORDER BY n.sticky DESC, " . _forum_get_topic_order_sql(variable_get('forum_order', 1));
-  $result = db_query(db_rewrite_sql($sql), isset($variables['node']->tid) ? $variables['node']->tid : 0);
+  $sql = "SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {taxonomy_term_node} r ON n.nid = r.nid AND r.tid = :tid WHERE n.status = :status ORDER BY n.sticky DESC, " . _forum_get_topic_order_sql(variable_get('forum_order', 1));
+  $result = db_query(db_rewrite_sql($sql), array(':status' => NODE_STATUS_PUBLISHED, ':tid' => isset($variables['node']->tid) ? $variables['node']->tid : 0));
 
   $stop = $variables['prev'] = $variables['next'] = 0;
 
Index: modules/node/node.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v
retrieving revision 1.54
diff -u -p -r1.54 node.admin.inc
--- modules/node/node.admin.inc	5 Jun 2009 05:28:28 -0000	1.54
+++ modules/node/node.admin.inc	18 Jun 2009 15:56:04 -0000
@@ -121,8 +121,9 @@ function node_filters() {
   $filters['status'] = array(
     'title' => t('status'),
     'options' => array(
-      'status-1' => t('published'),
-      'status-0' => t('not published'),
+      'status-' . NODE_STATUS_PUBLISHED => t('published'),
+      'status-' . NODE_STATUS_DRAFT => t('draft'),
+      'status-' . NODE_STATUS_UNPUBLISHED => t('unpublished'),
       'promote-1' => t('promoted'),
       'promote-0' => t('not promoted'),
       'sticky-1' => t('sticky'),
@@ -489,6 +490,7 @@ function node_admin_nodes() {
 
   $languages = language_list();
   $destination = drupal_get_destination();
+  $node_status_options = _node_status_options();
   $nodes = array();
   while ($node = db_fetch_object($result)) {
     $nodes[$node->nid] = '';
@@ -496,7 +498,7 @@ function node_admin_nodes() {
     $form['title'][$node->nid] = array('#markup' => l($node->title, 'node/' . $node->nid, $options) . ' ' . theme('mark', node_mark($node->nid, $node->changed)));
     $form['name'][$node->nid] =  array('#markup' => check_plain(node_type_get_name($node)));
     $form['username'][$node->nid] = array('#markup' => theme('username', $node));
-    $form['status'][$node->nid] =  array('#markup' => ($node->status ? t('published') : t('not published')));
+    $form['status'][$node->nid] =  array('#markup' => $node_status_options[$node->status]);
     $form['changed'][$node->nid] = array('#markup' => format_date($node->changed, 'small'));
     if ($multilanguage) {
       $form['language'][$node->nid] = array('#markup' => empty($node->language) ? t('Language neutral') : t($languages[$node->language]->name));
Index: modules/node/node.js
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.js,v
retrieving revision 1.4
diff -u -p -r1.4 node.js
--- modules/node/node.js	27 Apr 2009 20:19:37 -0000	1.4
+++ modules/node/node.js	18 Jun 2009 15:56:04 -0000
@@ -24,9 +24,10 @@ Drupal.behaviors.nodeFieldsetSummaries =
         vals.push(Drupal.checkPlain($.trim($(this).text())));
       });
 
-      if (!$('#edit-status', context).is(':checked')) {
-        vals.unshift(Drupal.t('Not published'));
-      }
+      $('option:selected', context).each(function () {
+        vals.push(Drupal.checkPlain($.trim($(this).text())));
+      });
+      
       return vals.join(', ');
     });
   }
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1070
diff -u -p -r1.1070 node.module
--- modules/node/node.module	12 Jun 2009 08:39:38 -0000	1.1070
+++ modules/node/node.module	18 Jun 2009 16:02:54 -0000
@@ -46,6 +46,21 @@ define('NODE_BUILD_RSS', 4);
 define('NODE_BUILD_PRINT', 5);
 
 /**
+ * Node is in the draft state.
+ */
+define('NODE_STATUS_DRAFT', -1);
+
+/**
+ * Node is not published.
+ */
+define('NODE_STATUS_UNPUBLISHED', 0);
+
+/**
+ * Node is published.
+ */
+define('NODE_STATUS_PUBLISHED', 1);
+
+/**
  * Implement hook_help().
  */
 function node_help($path, $arg) {
@@ -1401,8 +1416,8 @@ function node_search($op = 'search', $ke
       return;
 
     case 'status':
-      $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField();
-      $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField();
+      $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = :status', array(':status' => NODE_STATUS_PUBLISHED))->fetchField();
+      $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = :status AND d.sid IS NULL OR d.reindex <> 0", array(':status' => NODE_STATUS_PUBLISHED))->fetchField();
       return array('remaining' => $remaining, 'total' => $total);
 
     case 'admin':
@@ -1433,6 +1448,7 @@ function node_search($op = 'search', $ke
       // Build matching conditions
       list($join1, $where1) = _db_rewrite_sql();
       $arguments1 = array();
+      // TODO: Convert to NODE_STATUS_PUBLISHED
       $conditions1 = 'n.status = 1';
 
       if ($type = search_query_extract($keys, 'type')) {
@@ -1926,7 +1942,7 @@ function node_feed($nids = FALSE, $chann
     $nids = db_select('node', 'n')
       ->fields('n', array('nid', 'created'))
       ->condition('n.promote', 1)
-      ->condition('status', 1)
+      ->condition('status', NODE_STATUS_PUBLISHED)
       ->orderBy('n.created', 'DESC')
       ->range(0, variable_get('feed_default_items', 10))
       ->addTag('node_access')
@@ -2018,7 +2034,7 @@ function node_page_default() {
   $select = db_select('node', 'n')
     ->fields('n', array('nid'))
     ->condition('promote', 1)
-    ->condition('status', 1)
+    ->condition('status', NODE_STATUS_PUBLISHED)
     ->orderBy('sticky', 'DESC')
     ->orderBy('created', 'DESC')
     ->extend('PagerDefault')
@@ -2324,7 +2340,7 @@ function node_access($op, $node, $accoun
 
   // If the module did not override the access rights, use those set in the
   // node_access table.
-  if ($op != 'create' && $node->nid && $node->status) {
+  if ($op != 'create' && $node->nid && ($node->status == NODE_STATUS_PUBLISHED)) {
     $query = db_select('node_access');
     $query->addExpression('COUNT(*)');
     $query
@@ -2916,6 +2932,16 @@ function node_action_info() {
         'comment' => array('delete', 'insert', 'update'),
       ),
     ),
+    'node_draft_action' => array(
+      'type' => 'node',
+      'description' => t('Make post a draft'),
+      'configurable' => FALSE,
+      'behavior' => array('changes_node_property'),
+      'hooks' => array(
+        'node' => array('presave'),
+        'comment' => array('delete', 'insert', 'update'),
+      ),
+    ),
     'node_make_sticky_action' => array(
       'type' => 'node',
       'description' => t('Make post sticky'),
@@ -2988,24 +3014,33 @@ function node_action_info() {
 
 /**
  * Implement a Drupal action.
- * Sets the status of a node to 1, meaning published.
+ * Sets the status of a node to published.
  */
 function node_publish_action($node, $context = array()) {
-  $node->status = 1;
+  $node->status = NODE_STATUS_PUBLISHED;
   watchdog('action', 'Set @type %title to published.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
 
 /**
  * Implement a Drupal action.
- * Sets the status of a node to 0, meaning unpublished.
+ * Sets the status of a node to unpublished.
  */
 function node_unpublish_action($node, $context = array()) {
-  $node->status = 0;
+  $node->status = NODE_STATUS_UNPUBLISHED;
   watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_type_get_name($node), '%title' => $node->title));
 }
 
 /**
  * Implement a Drupal action.
+ * Sets the status of a node to draft.
+ */
+function node_draft_action($node, $context = array()) {
+  $node->status = NODE_STATUS_DRAFT;
+  watchdog('action', 'Set @type %title to draft.', array('@type' => node_get_types('name', $node), '%title' => $node->title));
+}
+
+/**
+ * Implement a Drupal action.
  * Sets the sticky-at-top-of-list property of a node to 1.
  */
 function node_make_sticky_action($node, $context = array()) {
@@ -3136,7 +3171,7 @@ function node_unpublish_by_keyword_actio
 function node_unpublish_by_keyword_action($node, $context) {
   foreach ($context['keywords'] as $keyword) {
     if (strpos(drupal_render(node_build(clone $node)), $keyword) !== FALSE || strpos($node->title, $keyword) !== FALSE) {
-      $node->status = 0;
+      $node->status = NODE_STATUS_UNPUBLISHED;
       watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_type_get_name($node), '%title' => $node->title));
       break;
     }
@@ -3192,6 +3227,13 @@ function node_elements() {
 }
 
 /**
+ * Get a list of node status options.
+ */
+function _node_status_options() {
+  return array(NODE_STATUS_UNPUBLISHED => t('Unpublished'), NODE_STATUS_DRAFT => t('Draft'), NODE_STATUS_PUBLISHED => t('Published'));
+}
+
+/**
  * Format a set of node links.
  *
  * @param $element
Index: modules/node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.69
diff -u -p -r1.69 node.pages.inc
--- modules/node/node.pages.inc	12 Jun 2009 08:39:38 -0000	1.69
+++ modules/node/node.pages.inc	18 Jun 2009 19:23:20 -0000
@@ -229,9 +229,8 @@ function node_form(&$form_state, $node) 
     '#weight' => 95,
   );
   $form['options']['status'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Published'),
-    '#default_value' => $node->status,
+    '#type' => 'value',
+    '#value' => $node->status,
   );
   $form['options']['promote'] = array(
     '#type' => 'checkbox',
@@ -252,16 +251,103 @@ function node_form(&$form_state, $node) 
     );
   }
 
+  $default_options = variable_get('node_options_' . $node->type, array('status', 'published'));
+  $default_status = in_array('status', $default_options) ? NODE_STATUS_PUBLISHED : NODE_STATUS_UNPUBLISHED;
+  $new = empty($node->nid);
+  $published = !$new && $node->status == NODE_STATUS_PUBLISHED;
+  $draft = $node->status == NODE_STATUS_DRAFT;
+  $unpublished = !$new && $node->status == NODE_STATUS_UNPUBLISHED;
+  $admin = user_access('administer nodes');
+
   // Add the buttons.
   $form['buttons'] = array();
   $form['buttons']['#weight'] = 100;
-  $form['buttons']['submit'] = array(
-    '#type' => 'submit',
-    '#access' => variable_get('node_preview_' . $node->type, 1) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])),
-    '#value' => t('Save'),
-    '#weight' => 5,
-    '#submit' => array('node_form_submit'),
-  );
+  if (variable_get('node_preview_' . $node->type, 1) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview']))) {
+    // Add direct save buttons if node preview was not mandatory or if we're in
+    // preview and have seen no errors.
+
+    // Always show the "Save draft" button.
+    $form['buttons']['submit_draft'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save draft'),
+      '#weight' => 3,
+      '#submit' => array('node_form_draft_submit'),
+    );
+
+    if ($admin && !$published && ($unpublished || $default_status == NODE_STATUS_UNPUBLISHED)) {
+      // Show the "Publish" button if the node isn't currently published and we
+      // have access to publish nodes. If a node type defaults to the published
+      // status, the node can be published by clicking on the "Save" button.
+      $form['buttons']['submit_publish'] = array(
+        '#type' => 'submit',
+        '#value' => t('Publish'),
+        '#weight' => 4,
+        '#access' => array('administer nodes'),
+        '#submit' => array('node_form_publish_submit'),
+      );
+    }
+
+    if ($admin && !$unpublished && ($published || ($draft && $default_status == NODE_STATUS_PUBLISHED && $node->uid != $user->uid))) {
+      // Show the "Publish" button if the node is not currently unpublished and
+      // the user has access to unpublish nodes. If a node type defaults to the
+      // unpublished status, the node can be unpublished by clicking the "Save"
+      // button. Do not show the "Unpublish" button for a new node created by a
+      // user with the "administer nodes" permission even when the node type is
+      // published by default. Administrators cannot unpublish their own drafts
+      // if nodes of this type default to being published.
+      $form['buttons']['submit_unpublish'] = array(
+        '#type' => 'submit',
+        '#value' => $draft ? t('Save (unpublished') : t('Unpublish'),
+        '#weight' => 4,
+        '#access' => array('administer nodes'),
+        '#submit' => array('node_form_unpublish_submit'),
+      );
+    }
+
+    // Always show the save button, but change the title in certain situations.
+    if ($new || $draft) {
+      // The node is either new or a draft.
+      if ($default_status == NODE_STATUS_PUBLISHED) {
+        // If the node is new or a draft, if the default status is "Published",
+        // the user will always have access to publish the node.
+        $label = t('Publish');
+      }
+      else {
+        // If a node is new or a draft and the default status is "Unpublished",
+        // users without the "administer nodes" permission should see a "Submit
+        // for approval" button, but users administrators should see the button
+        // titled "Save (unpublished)" to contrast the title with the "Publish"
+        // button.
+        if ($admin) {
+          $label = t('Save (unpublished)');
+        }
+        else {
+          $label = t('Submit for approval');
+        }
+      }
+    }
+    else {
+      // The node is either published or unpublished.
+      if ($published) {
+        if ($admin) {
+          $label = t('Save');
+        }
+        else {
+          $label = t('Publish changes');
+        }
+      }
+      else {
+        $label = t('Save');
+      }
+    }
+    $form['buttons']['submit_save'] = array(
+      '#type' => 'submit',
+      '#value' => $node_default_status == NODE_STATUS_PUBLISHED ? t('Publish') : t('Submit for approval'),
+      '#weight' => 5,
+      '#submit' => array('node_form_submit'),
+    );
+  }  
+
   $form['buttons']['preview'] = array(
     '#access' => variable_get('node_preview_' . $node->type, 1) != DRUPAL_DISABLED,
     '#type' => 'submit',
@@ -287,6 +373,30 @@ function node_form(&$form_state, $node) 
 }
 
 /**
+ * Button submit function: handle the "Save draft" button on the node form.
+ */
+function node_form_draft_submit($form, &$form_state) {
+  $form_state['values']['status'] = NODE_STATUS_DRAFT;
+  node_form_submit($form, $form_state, TRUE);
+}
+
+/**
+ * Button submit function: handle the "Publish" button on the node form.
+ */
+function node_form_publish_submit($form, &$form_state) {
+  $form_state['values']['status'] = NODE_STATUS_PUBLISHED;
+  node_form_submit($form, $form_state, TRUE);
+}
+
+/**
+ * Button submit functioN: handle the "Unpublish" button on the node form.
+ */
+function node_form_unpublish_submit($form, &$form_state) {
+  $form_state['values']['status'] = NODE_STATUS_UNPUBLISHED;
+  node_form_submit($form, $form_state, TRUE);
+}
+
+/**
  * Button submit function: handle the 'Delete' button on the node form.
  */
 function node_form_delete_submit($form, &$form_state) {
@@ -392,8 +502,13 @@ function theme_node_preview($node) {
   return $output;
 }
 
-function node_form_submit($form, &$form_state) {
+function node_form_submit($form, &$form_state, $set_status = FALSE) {
   global $user;
+  if (!$set_status && $form_state['values']['status'] == NODE_STATUS_DRAFT) {
+    $default_options = variable_get('node_options_' . $node->type, array('status', 'published'));
+    $default_status = in_array('status', $default_options) ? NODE_STATUS_PUBLISHED : NODE_STATUS_UNPUBLISHED;
+    $form_state['values']['status'] = $default_status;
+  }
 
   $node = node_form_submit_build_node($form, $form_state);
   $insert = empty($node->nid);
Index: modules/search/search.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.api.php,v
retrieving revision 1.10
diff -u -p -r1.10 search.api.php
--- modules/search/search.api.php	12 Jun 2009 08:39:38 -0000	1.10
+++ modules/search/search.api.php	18 Jun 2009 16:38:35 -0000
@@ -80,8 +80,8 @@ function hook_search($op = 'search', $ke
       return;
 
     case 'status':
-      $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1'));
-      $remaining = db_result(db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0"));
+      $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = :status', array(':status' => NODE_STATUS_PUBLISHED)));
+      $remaining = db_result(db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = :status AND d.sid IS NULL OR d.reindex <> 0", array(':status' => NODE_STATUS_PUBLISHED)));
       return array('remaining' => $remaining, 'total' => $total);
 
     case 'admin':
@@ -112,6 +112,7 @@ function hook_search($op = 'search', $ke
       // Build matching conditions
       list($join1, $where1) = _db_rewrite_sql();
       $arguments1 = array();
+      // TODO: Convert to using NODE_STATUS_PUBLISHED.
       $conditions1 = 'n.status = 1';
 
       if ($type = search_query_extract($keys, 'type')) {
@@ -247,7 +248,7 @@ function hook_search_preprocess($text) {
 function hook_update_index() {
   $limit = (int)variable_get('search_cron_limit', 100);
 
-  $result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit);
+  $result = db_query_range('SELECT n.nid, c.last_comment_timestamp FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = :status AND n.moderate = 0 AND (n.created > :created OR n.changed > :changed OR c.last_comment_timestamp > :last_comment) ORDER BY GREATEST(n.created, n.changed, c.last_comment_timestamp) ASC', array(':status' => NODE_STATUS_PUBLISHED, ':last' => $last), 0, $limit);
 
   foreach ($result as $node) {
     $node = node_load($node->nid);
Index: modules/translation/translation.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.pages.inc,v
retrieving revision 1.7
diff -u -p -r1.7 translation.pages.inc
--- modules/translation/translation.pages.inc	13 Oct 2008 00:33:05 -0000	1.7
+++ modules/translation/translation.pages.inc	18 Jun 2009 15:56:04 -0000
@@ -26,6 +26,7 @@ function translation_node_overview($node
 
   $header = array(t('Language'), t('Title'), t('Status'), t('Operations'));
 
+  $node_status_options = _node_status_options();
   foreach (language_list() as $language) {
     $options = array();
     $language_name = $language->name;
@@ -37,7 +38,7 @@ function translation_node_overview($node
       if (node_access('update', $translation_node)) {
         $options[] = l(t('edit'), "node/$translation_node->nid/edit");
       }
-      $status = $translation_node->status ? t('Published') : t('Not published');
+      $status = $node_status_options[$translation_node->status];
       $status .= $translation_node->translate ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
       if ($translation_node->nid == $tnid) {
         $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.43
diff -u -p -r1.43 user.test
--- modules/user/user.test	13 Jun 2009 20:40:09 -0000	1.43
+++ modules/user/user.test	18 Jun 2009 15:56:04 -0000
@@ -196,7 +196,7 @@ class UserCancelTestCase extends DrupalW
 
     // Confirm user's content has not been altered.
     $test_node = node_load($node->nid, NULL, TRUE);
-    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
+    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == NODE_STATUS_PUBLISHED), t('Node of the user has not been altered.'));
   }
 
   /**
@@ -237,7 +237,7 @@ class UserCancelTestCase extends DrupalW
 
     // Confirm user's content has not been altered.
     $test_node = node_load($node->nid, NULL, TRUE);
-    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
+    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == NODE_STATUS_PUBLISHED), t('Node of the user has not been altered.'));
   }
 
   /**
@@ -311,9 +311,9 @@ class UserCancelTestCase extends DrupalW
 
     // Confirm user's content has been unpublished.
     $test_node = node_load($node->nid, NULL, TRUE);
-    $this->assertTrue($test_node->status == 0, t('Node of the user has been unpublished.'));
+    $this->assertTrue($test_node->status == NODE_STATUS_UNPUBLISHED, t('Node of the user has been unpublished.'));
     $test_node = node_load($node->nid, $node->vid, TRUE);
-    $this->assertTrue($test_node->status == 0, t('Node revision of the user has been unpublished.'));
+    $this->assertTrue($test_node->status == NODE_STATUS_UNPUBLISHED, t('Node revision of the user has been unpublished.'));
 
     // Confirm user is logged out.
     $this->assertNoText($account->name, t('Logged out.'));
@@ -360,11 +360,11 @@ class UserCancelTestCase extends DrupalW
 
     // Confirm that user's content has been attributed to anonymous user.
     $test_node = node_load($node->nid, NULL, TRUE);
-    $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), t('Node of the user has been attributed to anonymous user.'));
+    $this->assertTrue(($test_node->uid == 0 && $test_node->status == NODE_STATUS_PUBLISHED), t('Node of the user has been attributed to anonymous user.'));
     $test_node = node_load($revision_node->nid, $revision, TRUE);
-    $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), t('Node revision of the user has been attributed to anonymous user.'));
+    $this->assertTrue(($test_node->uid == 0 && $test_node->status == NODE_STATUS_PUBLISHED), t('Node revision of the user has been attributed to anonymous user.'));
     $test_node = node_load($revision_node->nid, NULL, TRUE);
-    $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), t("Current revision of the user's node was not attributed to anonymous user."));
+    $this->assertTrue(($test_node->uid != 0 && $test_node->status == NODE_STATUS_PUBLISHED), t("Current revision of the user's node was not attributed to anonymous user."));
 
     // Confirm that user is logged out.
     $this->assertNoText($account->name, t('Logged out.'));
