=== modified file 'includes/common.inc'
--- includes/common.inc	2009-11-21 00:43:42 +0000
+++ includes/common.inc	2009-11-21 09:44:47 +0000
@@ -4544,6 +4544,9 @@ function drupal_page_set_cache() {
     $header_names = _drupal_set_preferred_header_name();
     foreach (drupal_get_http_header() as $name_lower => $value) {
       $cache->headers[$header_names[$name_lower]] = $value;
+      if ($name_lower == 'expires') {
+        $cache->expire = strtotime($value);
+      }
     }
 
     if ($cache->data) {

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2009-11-19 04:00:47 +0000
+++ modules/node/node.module	2009-11-21 15:47:17 +0000
@@ -1994,6 +1994,7 @@ function node_block_info() {
   $blocks['syndicate']['info'] = t('Syndicate');
   // Not worth caching.
   $blocks['syndicate']['cache'] = DRUPAL_NO_CACHE;
+  $blocks['recent']['info'] = t('Recent content');
   return $blocks;
 }
 
@@ -2001,13 +2002,68 @@ function node_block_info() {
  * Implement hook_block_view().
  */
 function node_block_view($delta = '') {
-  $block['subject'] = t('Syndicate');
-  $block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate')));
+  $block = array();
 
+  switch ($delta) {
+    case 'syndicate':
+      $block['subject'] = t('Syndicate');
+      $block['content'] = theme('feed_icon', array('url' => url('rss.xml'), 'title' => t('Syndicate')));
+      break;
+
+    case 'recent':
+      if (user_access('access content')) {
+        $block['subject'] = t('Recent content');
+        $items = array();
+        $select = db_select('node', 'n')
+          ->fields('n', array('created', 'nid', 'title'))
+          ->condition('status', 1)
+          ->orderBy('created', 'DESC')
+          ->range(0, variable_get('node_recent_block_count', 10))
+          ->addTag('node_access')
+          ->execute();
+        while ($node = $select->fetch(PDO::FETCH_ASSOC)) {
+          $item = l($node['title'], 'node/' . $node['nid']);
+          $item .= '<br />';
+          $item .= t('@time ago', array('@time' => format_interval(REQUEST_TIME - $node['created'])));
+          $items[] = $item;
+        }
+        if (count($items)) {
+          $block['content'] = theme('item_list', array('items' => $items));
+        }
+      }
+      break;
+
+  }
   return $block;
 }
 
 /**
+ * Implementation of hook_block_configure().
+ */
+function node_block_configure($delta = '') {
+  $form = array();
+    if ($delta == 'recent') {
+      $form['node_recent_block_count'] = array(
+        '#type' => 'select',
+        '#title' => t('Amount of recent content'),
+        '#default_value' => variable_get('node_recent_block_count', 10),
+        '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)),
+        '#description' => t('Pieces of content displayed in the <em>Recently posted</em> block.'),
+      );
+    }
+  return $form;
+}
+
+/**
+ * Implementation of hook_block_save().
+ */
+function node_block_save($delta = '', $edit = array()) {
+  if ($delta == 'recent') {
+    variable_set('node_recent_block_count', $edit['node_recent_block_count']);
+  }
+}
+
+/**
  * A generic function for generating RSS feeds from a set of nodes.
  *
  * @param $nids

=== modified file 'modules/node/node.test'
--- modules/node/node.test	2009-11-19 04:00:47 +0000
+++ modules/node/node.test	2009-11-21 22:25:12 +0000
@@ -18,6 +18,11 @@ class NodeLoadMultipleUnitTest extends D
     parent::setUp();
     $web_user = $this->drupalCreateUser(array('create article content', 'create page content'));
     $this->drupalLogin($web_user);
+    // Disable the "recent content" block, it will mess up our assertions if enabled.
+    db_delete('block')
+      ->condition('module', 'node')
+      ->condition('delta', 'recent')
+      ->execute();
   }
 
   /**
@@ -1166,3 +1171,116 @@ class NodeFeedTestCase extends DrupalWeb
     $this->assertTrue(strpos($output, '<copyright>Drupal is a registered trademark of Dries Buytaert.</copyright>') !== FALSE);
   }
 }
+
+class RecentContentBlockTestCase extends DrupalWebTestCase {
+  protected $nodes;
+  protected $count = 10;
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+     return array(
+      'name' => t('Recent content block'),
+      'description' => t('Test to make sure the recent content block works properly; it makes sure that nodes appear there, in the correct order, and the settings work properly.'),
+      'group' => t('Node'),
+    );
+  }
+
+  function setUp() {
+    // Clear the static $nodes cache.
+    $this->nodes = array();
+    parent::setUp();
+  }
+
+  /**
+   * Test the recent content block with a few nodes, less than the maximum.
+   */
+  function testRecentContentBlock() {
+    $this->createTestNodes(3);
+    $this->assertRecentContent();
+  }
+
+  /**
+   * Test the recent content block with a lot of nodes, more than the maximum.
+   */
+  function testRecentContentBlockOverflow() {
+    $this->createTestNodes(18);
+    $this->assertRecentContent();
+  }
+
+  /**
+   * Test the recent content block with a non-standard maximum number of nodes.
+   */
+  function testRecentContentBlockSettings() {
+    $this->setNodeRecentBlockCount(20);
+    $this->createTestNodes(18);
+    $this->assertRecentContent();
+  }
+
+  /**
+   * Test the recent content block with a non-standard maximum number of nodes,
+   * and have more nodes than that custom maximum.
+   */
+  function testRecentContentBlockSettingsOverflow() {
+    $this->setNodeRecentBlockCount(5);
+    $this->createTestNodes(18);
+    $this->assertRecentContent();
+  }
+
+  /**
+   * Create a number of nodes, and store them in the protected $nodes array.
+   *
+   * @param $count
+   *   The number of nodes to create.
+   */
+  protected function createTestNodes($count) {
+    $time = REQUEST_TIME - 600;
+    for ($i = 0; $i < $count; $i++) {
+      $this->nodes[($count - $i) - 1] = $this->drupalCreateNode(array('created' => REQUEST_TIME + $i));
+    }
+    $this->nodes[-1] = $this->drupalCreateNode(array('create' => REQUEST_TIME, 'status' => 0));
+  }
+
+  /**
+   * Helper function to set the maximum recent nodes shown on the block through
+   * the user interface.
+   */
+  function setNodeRecentBlockCount($count) {
+    $test_user = $this->drupalCreateUser(array('administer blocks'));
+    $this->drupalLogin($test_user);
+    $this->drupalPost('admin/structure/block/manage/node/recent/configure', array('node_recent_block_count' => $count), t('Save block'));
+    $this->count = $count;
+    $this->drupalLogout();
+  }
+
+  /**
+   * Helper assertion that makes sure the nodes appear on the block correctly.
+   */
+  protected function assertRecentContent() {
+    $this->refreshVariables();
+    $count = $this->count;
+    $this->drupalGet('<front>');
+    $results = $this->elements->xpath('//div[@id="block-node-recent"]');
+    $div = $results[0];
+    if (!count($this->nodes)) {
+      $this->assertFalse($div, t('Block does not appear when no nodes exist.'));
+      return;
+    }
+    $this->assertTrue($div, t('Block appears when several nodes exist.'));
+    $title = $div->h2;
+    $this->assertEqual($title, t('Recent content'), t('Title of the block matches correctly.'));
+    // Under the block div, we have the <div class="content">, and then another
+    // div surrounding the themed item list before we get to the ul.
+    $ul = $div->div->div->ul;
+    $this->assertEqual(count($ul->li), min(count($this->nodes) - 1, $count), t('The correct number of nodes are shown.'));
+    for ($i = 0; $i < $count; $i++) {
+      if (isset($this->nodes[$i])) {
+        $list_item = $ul->li[$i];
+        $link = $list_item->a;
+        $this->assertEqual($link, $this->nodes[$i]->title[FIELD_LANGUAGE_NONE][0]['value'], t('The correct node title is shown.'));
+      }
+    }
+    $this->assertNoText($this->nodes[-1]->title[FIELD_LANGUAGE_NONE][0]['value'], t('Unpublished nodes not shown'));
+  }
+}

=== modified file 'modules/search/search.test'
--- modules/search/search.test	2009-10-19 23:28:40 +0000
+++ modules/search/search.test	2009-11-21 15:47:17 +0000
@@ -262,6 +262,13 @@ class SearchAdvancedSearchForm extends D
     // and searching has to happen in the same request, so running the shutdown
     // function manually is needed to finish the indexing process.
     search_update_totals();
+
+    // Remove the "recent content" block because it will mess up our assertions
+    // later if enabled.
+    db_delete('block')
+      ->condition('module', 'node')
+      ->condition('delta', 'recent')
+      ->execute();
   }
 
   /**

=== modified file 'modules/system/system.module'
--- modules/system/system.module	2009-11-19 01:24:44 +0000
+++ modules/system/system.module	2009-11-21 09:42:58 +0000
@@ -3076,8 +3076,11 @@ function system_page_build(&$page) {
  * @see system_run_cron_image_access()
  */
 function system_run_cron_image() {
-  drupal_page_is_cacheable(FALSE);
-
+  $cron_last = variable_get('cron_last', NULL);
+  $cron_threshold = variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD);
+  // Let's set Expires to the next run.
+  drupal_add_http_header('Expires', gmdate(DATE_RFC1123, $cron_last + $cron_threshold));
+ 
   // Output a transparent 1x1 image to the browser; required for clients not
   // supporting JavaScript.
   drupal_add_http_header('Content-Type', 'image/gif');
@@ -3098,8 +3101,6 @@ function system_run_cron_image() {
   }
   else {
     // Run cron automatically if it has never run or threshold was crossed.
-    $cron_last = variable_get('cron_last', NULL);
-    $cron_threshold = variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD);
     if (!isset($cron_last) || (REQUEST_TIME - $cron_last > $cron_threshold)) {
       // Lock cron threshold semaphore.
       variable_set('cron_threshold_semaphore', REQUEST_TIME);

=== modified file 'modules/tracker/tracker.test'
--- modules/tracker/tracker.test	2009-11-14 07:58:49 +0000
+++ modules/tracker/tracker.test	2009-11-21 15:47:17 +0000
@@ -21,6 +21,13 @@ class TrackerTest extends DrupalWebTestC
     $this->user = $this->drupalCreateUser($permissions);
     $this->other_user = $this->drupalCreateUser($permissions);
 
+    // Remove the "recent content" block because it will mess up our assertions
+    // later on if enabled.
+    db_delete('block')
+      ->condition('module', 'node')
+      ->condition('delta', 'recent')
+      ->execute();
+
     // Make node preview optional.
     variable_set('comment_preview_page', 0);
   }

=== modified file 'profiles/default/default.install'
--- profiles/default/default.install	2009-11-10 17:27:53 +0000
+++ profiles/default/default.install	2009-11-21 15:47:17 +0000
@@ -130,6 +130,16 @@ function default_install() {
       'pages' => '',
       'cache' => -1,
     ),
+    array(
+      'module' => 'node',
+      'delta' => 'recent',
+      'theme' => 'garland',
+      'status' => 1,
+      'weight' => 0,
+      'region' => 'sidebar_first',
+      'pages' => '',
+      'cache' => -1,
+    ),
   );
   $query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
   foreach ($values as $record) {

