Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1169
diff -u -r1.1169 node.module
--- modules/node/node.module	19 Nov 2009 04:00:47 -0000	1.1169
+++ modules/node/node.module	23 Nov 2009 16:50:23 -0000
@@ -1994,6 +1994,7 @@
   $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 @@
  * 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('uid', '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 .= theme('username', array('account' => user_load($node['uid'])));
+          $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
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.56
diff -u -r1.56 node.test
--- modules/node/node.test	19 Nov 2009 04:00:47 -0000	1.56
+++ modules/node/node.test	23 Nov 2009 16:50:23 -0000
@@ -1166,3 +1166,116 @@
     $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'));
+  }
+}
