--- modules/node/node.module +++ modules/node/node.module @@ -1972,6 +1972,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; } @@ -1979,13 +1980,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 .= '
'; + $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 Recently posted 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 diff --git a/modules/node/node.test b/modules/node/node.test --- modules/node/node.test +++ modules/node/node.test @@ -18,6 +18,11 @@ class NodeLoadMultipleUnitTest extends DrupalWebTestCase { 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(); } /** @@ -1072,3 +1077,116 @@ class NodeFeedTestCase extends DrupalWebTestCase { $this->assertTrue(strpos($output, 'Drupal is a registered trademark of Dries Buytaert.') !== 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/configure/node/recent', 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(''); + $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
, 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, t('The correct node title is shown.')); + } + } + $this->assertNoText($this->nodes[-1]->title, t('Unpublished nodes not shown')); + } +} --- modules/search/search.test +++ modules/search/search.test @@ -262,6 +262,13 @@ class SearchAdvancedSearchForm extends DrupalWebTestCase { // 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(); } /** --- modules/tracker/tracker.test +++ modules/tracker/tracker.test @@ -21,6 +21,13 @@ class TrackerTest extends DrupalWebTestCase { $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); } --- profiles/default/default.install +++ profiles/default/default.install @@ -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) {