Index: modules/block/block.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.api.php,v
retrieving revision 1.13
diff -u -p -r1.13 block.api.php
--- modules/block/block.api.php	13 Aug 2010 12:25:14 -0000	1.13
+++ modules/block/block.api.php	3 Oct 2010 22:04:52 -0000
@@ -57,6 +57,7 @@
  *     - DRUPAL_CACHE_GLOBAL: The block is the same for every user on every
  *       page where it is visible.
  *     - DRUPAL_NO_CACHE: The block should not get cached.
+ *   - 'properties': (optional) @todo: Document me!
  *   - 'weight': (optional) Initial value for the ordering weight of this block.
  *     Most modules do not provide an initial value, and any value provided can
  *     be modified by a user on the block configuration screen.
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.902
diff -u -p -r1.902 comment.module
--- modules/comment/comment.module	3 Oct 2010 01:15:33 -0000	1.902
+++ modules/comment/comment.module	3 Oct 2010 22:04:52 -0000
@@ -402,6 +402,7 @@ function comment_permission() {
  */
 function comment_block_info() {
   $blocks['recent']['info'] = t('Recent comments');
+  $blocks['recent']['properties']['administrative'] = BLOCK_REGION_NONE;
 
   return $blocks;
 }
Index: modules/dashboard/dashboard.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/dashboard/dashboard.module,v
retrieving revision 1.39
diff -u -p -r1.39 dashboard.module
--- modules/dashboard/dashboard.module	3 Oct 2010 00:14:57 -0000	1.39
+++ modules/dashboard/dashboard.module	3 Oct 2010 22:04:52 -0000
@@ -2,6 +2,16 @@
 // $Id: dashboard.module,v 1.39 2010/10/03 00:14:57 dries Exp $
 
 /**
+ * Show the block by default on the dashboard.
+ */
+define('DASHBOARD_BLOCK_DEFAULT', 1);
+
+/**
+ * The block is available to place on the dashboard.
+ */
+define('DASHBOARD_BLOCK_AVAILABLE', 2);
+
+/**
  * Implements hook_help().
  */
 function dashboard_help($path, $arg) {
@@ -185,6 +195,49 @@ function dashboard_system_info_alter(&$i
 }
 
 /**
+ * Implements hook_block_info_alter().
+ *
+ * Assigns blocks to the dashboard_main region of the admin theme if they are
+ * currently disabled and specified by
+ * $block['properties']['administrative'] = TRUE as a default block. This
+ * auto-assignment will only be attempted once per module/delta to allow the
+ * region assignment to be overridden by the user.
+ */
+function dashboard_block_info_alter(&$blocks, $theme, $code_blocks) {
+  if (!$theme = variable_get('admin_theme')) {
+    global $theme_key;
+    $theme = $theme_key;
+  }
+  $administrative_blocks = variable_get('dashboard_administrative_blocks', array());
+
+  foreach ($blocks as $module => &$module_blocks) {
+    foreach ($module_blocks as $delta => &$block) {
+      if ($block['theme'] != $theme) {
+        // Only process for the admin theme, which displays the dashboard.
+        continue;
+      }
+      if (
+        empty($block['status'])
+        && (empty($block['region']) || $block['region'] == BLOCK_REGION_NONE)
+        && !empty($code_blocks[$module][$delta]['properties']['administrative'])
+        && $code_blocks[$module][$delta]['properties']['administrative'] !== BLOCK_REGION_NONE
+        && empty($administrative_blocks[$module][$delta])
+        ) {
+        $block['status'] = 1;
+        $block['region'] = 'dashboard_main';
+      }
+      if (!empty($code_blocks[$module][$delta]['properties']['administrative'])) {
+        $administrative_blocks[$module][$delta] = $code_blocks[$module][$delta]['properties']['administrative'];
+      }
+      else {
+        unset($administrative_blocks[$module][$delta]);
+      }
+    }
+  }
+  variable_set('dashboard_administrative_blocks', $administrative_blocks);
+}
+
+/**
  * Implements hook_theme().
  */
 function dashboard_theme() {
@@ -281,14 +334,16 @@ function dashboard_admin_blocks() {
   module_load_include('inc', 'block', 'block.admin');
 
   // Prepare the blocks for the current theme, and remove those that are
-  // currently displayed in non-dashboard regions.
+  // currently displayed in non-dashboard regions, or are not specified as
+  // "administrative" in hook_block_info().
   // @todo This assumes the current page is being displayed using the same
   //   theme that the dashboard is displayed in.
   $blocks = block_admin_display_prepare_blocks($theme_key);
   $dashboard_regions = dashboard_region_descriptions();
   $regions_to_remove = array_diff_key(system_region_list($theme_key, REGIONS_VISIBLE), $dashboard_regions);
+  $administrative_blocks = variable_get('dashboard_administrative_blocks', array());
   foreach ($blocks as $id => $block) {
-    if (isset($regions_to_remove[$block['region']])) {
+    if (isset($regions_to_remove[$block['region']]) || (empty($administrative_blocks[$block['module']][$block['delta']]) && !in_array($block['region'], dashboard_regions()))) {
       unset($blocks[$id]);
     }
   }
@@ -356,6 +411,62 @@ function dashboard_form_block_admin_conf
       $region['#options'] = array_diff_key($region['#options'], $dashboard_regions);
     }
   }
+
+  // Provide a block setting to control dashboard visibility.
+  $form['settings']['dashboard_visibility'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable on dashboard'),
+    '#default_value' => FALSE,
+  );
+  // If the block is currently assigned to a non-dashboard region, give a note
+  // to the user that the region and setting are not compatible.
+  $current_region = $form['regions'][$theme_key]['#default_value'];
+  if (!empty($current_region) && !in_array($current_region, dashboard_regions())) {
+    $form['settings']['dashboard_visibility']['#description'] = t('Note: The block must be removed from a region on the @theme theme to enable it on the dashboard.', array('@theme' => $form['regions'][$theme_key]['#title']));
+  }
+  // Set the default value based on whether the block is in a dashboard region.
+  elseif (in_array($current_region, dashboard_regions())) {
+    $form['settings']['dashboard_visibility']['#default_value'] = TRUE;
+  }
+  $form['#validate'][] = 'dashboard_form_block_admin_configure_validate';
+  $form['#submit'] = array_merge(array('dashboard_form_block_admin_configure_submit'), $form['#submit']);
+}
+
+/**
+ * Form validation handler for block configuration form.
+ */
+function dashboard_form_block_admin_configure_validate($form, &$form_state) {
+  global $theme_key;
+  drupal_theme_initialize();
+
+  // Ensure that the dashboard visibility flag is only settable if the block
+  // hasn't been assigned to a non-dashboard region.
+  $selected_region = $form_state['values']['regions'][$theme_key];
+  if ($form_state['values']['dashboard_visibility'] && $selected_region != BLOCK_REGION_NONE && !in_array($selected_region, dashboard_regions())) {
+    form_set_error('regions][' . $theme_key, t('The block must be removed from a region on the @theme theme to enable it on the dashboard.', array('@theme' => $form['regions'][$theme_key]['#title'])));
+  }
+}
+
+/**
+ * Form submission handler for block configuration form.
+ */
+function dashboard_form_block_admin_configure_submit($form, &$form_state) {
+  global $theme_key;
+  drupal_theme_initialize();
+
+  // Cause the block to be added/removed from the dashboard based on the
+  // dashboard visibility setting.
+  $region = &$form_state['values']['regions'][$theme_key];
+  if ($form_state['values']['dashboard_visibility']) {
+    // Add the block to the dashboard if not already assigned to a region.
+    if ($region == BLOCK_REGION_NONE) {
+      $region = 'dashboard_main';
+      drupal_set_message(t('Block added to the dashboard.'));
+    }
+  }
+  elseif (in_array($region, dashboard_regions())) {
+    $region = BLOCK_REGION_NONE;
+  }
 }
 
 /**
@@ -445,9 +556,11 @@ function dashboard_show_disabled() {
   // Blocks are not necessarily initialized at this point.
   $blocks = _block_rehash();
 
-  // Limit the list to disabled blocks for the current theme.
+  // Limit the list to disabled blocks for the current theme which are
+  // specified as "administrative".
+  $administrative_blocks = variable_get('dashboard_administrative_blocks', array());
   foreach ($blocks as $key => $block) {
-    if ($block['theme'] != $theme_key || (!empty($block['status']) && !empty($block['region']))) {
+    if ($block['theme'] != $theme_key || (!empty($block['status']) && !empty($block['region'])) || empty($administrative_blocks[$block['module']][$block['delta']])) {
       unset($blocks[$key]);
     }
   }
Index: modules/dashboard/dashboard.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/dashboard/dashboard.test,v
retrieving revision 1.5
diff -u -p -r1.5 dashboard.test
--- modules/dashboard/dashboard.test	14 Sep 2010 21:51:01 -0000	1.5
+++ modules/dashboard/dashboard.test	3 Oct 2010 22:04:52 -0000
@@ -43,6 +43,7 @@ class DashboardBlocksTestCase extends Dr
     $custom_block['title'] = $this->randomName(8);
     $custom_block['body[value]'] = $this->randomName(32);
     $custom_block['regions[stark]'] = 'dashboard_main';
+    $custom_block['dashboard_visibility'] = TRUE;
     $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
 
     // Ensure admin access.
@@ -79,4 +80,24 @@ class DashboardBlocksTestCase extends Dr
       $this->assertTrue(empty($elements), t('%region is not an available choice on the block configuration page.', array('%region' => $region)));
     }
   }
+
+  /**
+   * Test that defining a block with ['properties']['administrative'] = TRUE
+   * adds it to the dashboard automatically.
+   */
+  function testDashboardHooks() {
+    // Enable our test module and flush caches. This *should* trigger a block
+    // rehash and add our new default block to the dashboard.
+    module_enable(array('dashboard_test'), FALSE);
+    drupal_flush_all_caches();
+    $this->drupalGet('admin/structure/dashboard');
+    // test1 should show up and be enabled on the dashboard.
+    $elements = $this->xpath('//select[@id=:id]//option[@value=:value]', array(':id' => 'edit-blocks-dashboard-test-test1-region', ':value' => 'dashboard_main'));
+    $this->assertTrue(!empty($elements[0]['selected']), t('Default dashboard block defined successfully.'));
+    // test2 should be available for the dashboard, but not enabled.
+    $elements = $this->xpath('//select[@id=:id]//option[@selected=:value]', array(':id' => 'edit-blocks-dashboard-test-test2-region', ':value' => 'selected'));
+    $this->assertTrue($this->assertText('test2') && (empty($elements) || $elements[0]['value'] == BLOCK_REGION_NONE), t('Available dashboard block defined successfully.'));
+    // test3 should not show up in the UI.
+    $this->assertNoText('test3', t('Blocks not defined as "administrative" are excluded from dashboard UI by default.'));
+  }
 }
Index: modules/dashboard/tests/dashboard_test.info
===================================================================
RCS file: modules/dashboard/tests/dashboard_test.info
diff -N modules/dashboard/tests/dashboard_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/dashboard/tests/dashboard_test.info	3 Oct 2010 22:04:52 -0000
@@ -0,0 +1,8 @@
+; $Id$
+name = Dashboard test
+description = Tests dashboard hooks.
+package = Testing
+version = VERSION
+core = 7.x
+files[] = dashboard_test.module
+hidden = TRUE
Index: modules/dashboard/tests/dashboard_test.module
===================================================================
RCS file: modules/dashboard/tests/dashboard_test.module
diff -N modules/dashboard/tests/dashboard_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/dashboard/tests/dashboard_test.module	3 Oct 2010 22:04:52 -0000
@@ -0,0 +1,34 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   Tests dashboard hooks.
+ */
+
+
+/**
+ * Implements hook_block_info().
+ */
+function dashboard_test_block_info() {
+  return array(
+    'test1' => array(
+      'info' => 'test1',
+      'properties' => array('administrative' => TRUE),
+    ),
+    'test2' => array(
+      'info' => 'test2',
+      'properties' => array('administrative' => BLOCK_REGION_NONE),
+    ),
+    'test3' => array(
+      'info' => 'test3',
+    ),
+  );
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function dashboard_test_block_view() {
+  return array('content' => 'test content');
+}
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1304
diff -u -p -r1.1304 node.module
--- modules/node/node.module	3 Oct 2010 01:15:33 -0000	1.1304
+++ modules/node/node.module	3 Oct 2010 22:04:53 -0000
@@ -2045,6 +2045,7 @@ function node_block_info() {
   $blocks['syndicate']['cache'] = DRUPAL_NO_CACHE;
 
   $blocks['recent']['info'] = t('Recent content');
+  $blocks['recent']['properties']['administrative'] = TRUE;
 
   return $blocks;
 }
Index: modules/search/search.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.module,v
retrieving revision 1.363
diff -u -p -r1.363 search.module
--- modules/search/search.module	1 Oct 2010 15:24:18 -0000	1.363
+++ modules/search/search.module	3 Oct 2010 22:04:53 -0000
@@ -143,6 +143,8 @@ function search_block_info() {
   $blocks['form']['info'] = t('Search form');
   // Not worth caching.
   $blocks['form']['cache'] = DRUPAL_NO_CACHE;
+  $blocks['form']['properties']['administrative'] = TRUE;
+
   return $blocks;
 }
 
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1205
diff -u -p -r1.1205 user.module
--- modules/user/user.module	3 Oct 2010 01:15:34 -0000	1.1205
+++ modules/user/user.module	3 Oct 2010 22:04:53 -0000
@@ -1273,10 +1273,13 @@ function user_block_info() {
   $blocks['login']['cache'] = DRUPAL_NO_CACHE;
 
   $blocks['new']['info'] = t('Who\'s new');
+  $blocks['new']['properties']['administrative'] = TRUE;
 
   // Too dynamic to cache.
   $blocks['online']['info'] = t('Who\'s online');
   $blocks['online']['cache'] = DRUPAL_NO_CACHE;
+  $blocks['online']['properties']['administrative'] = BLOCK_REGION_NONE;
+
   return $blocks;
 }
 
