Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.456
diff -u -r1.456 theme.inc
--- includes/theme.inc	16 Dec 2008 22:05:50 -0000	1.456
+++ includes/theme.inc	27 Dec 2008 22:23:53 -0000
@@ -2048,5 +2048,5 @@
 
   $variables['template_files'][] = 'block-' . $variables['block']->region;
   $variables['template_files'][] = 'block-' . $variables['block']->module;
-  $variables['template_files'][] = 'block-' . $variables['block']->module . '-' . $variables['block']->delta;
+  $variables['template_files'][] = 'block-' . $variables['block']->block_id;
 }
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.949
diff -u -r1.949 user.module
--- modules/user/user.module	23 Dec 2008 14:18:31 -0000	1.949
+++ modules/user/user.module	27 Dec 2008 22:24:48 -0000
@@ -739,26 +739,87 @@
  */
 function user_block_list() {
   global $user;
-
-  $blocks['login']['info'] = t('User login');
-  // Not worth caching.
-  $blocks['login']['cache'] = BLOCK_NO_CACHE;
-
-  $blocks['navigation']['info'] = t('Navigation');
-  // Menu blocks can't be cached because each menu item can have
-  // a custom access callback. menu.inc manages its own caching.
-  $blocks['navigation']['cache'] = BLOCK_NO_CACHE;
-
-  $blocks['new']['info'] = t('Who\'s new');
-
-  // Too dynamic to cache.
-  $blocks['online']['info'] = t('Who\'s online');
-  $blocks['online']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['user_login'] = array(
+    'title' => t('User login'),
+    'block callback' => 'drupal_get_form',
+    'block arguments' => array('user_login_block'), 
+    'access callback' => '_user_block_login_access',
+    'description' => t('Provides the user login box.'),
+    'cache' => BLOCK_NO_CACHE // Not worth caching.
+  ); 
+  $blocks['user_navigation'] = array(
+    'title' => t('Navigation'),
+    'block callback' => 'menu_tree',
+    'title callbacl' => '_user_block_navigation_title',
+    'description' => t('Provides the main interactive menu for the site.'),
+    'cache' => BLOCK_NO_CACHE // Not worth caching.
+  );  
+  $blocks['user_new'] = array(
+    'title' => t('Who \'s new'),
+    'access arguments' => array('access content'),
+    'block callback' => '_user_block_new',
+    'description' => t('Provides  a list of new users who have subsequently accessed the site successfully.'),
+  );    
+  $blocks['user_online'] = array(
+    'title' => t('Who \'s online'),
+    'access arguments' => array('access content'),
+    'block callback' => '_user_block_online',
+    'description' => t('Provides a list of users active within the defined period.'),
+    'cache' => BLOCK_NO_CACHE // Too dynamic to cache.
+  );    
+  
   return $blocks;
 }
 
+function _user_block_login_access() {
+  global $user;
+  // For usability's sake, avoid showing two login forms on one page.
+  return !$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)));
+}
+
+function _user_block_navigation_title() {
+  global $user; 
+  return $user->uid ? check_plain($user->name) : t('Navigation');  
+}
+
+function _user_block_new() {
+  // Retrieve a list of new users who have subsequently accessed the site successfully.
+  $items = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', array(), 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
+  return theme('user_list', $items);
+}
+
+function _user_block_online() {
+  // Count users active within the defined period.
+  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
+
+  // Perform database queries to gather online user lists. We use s.timestamp
+  // rather than u.access because it is much faster.
+  $anonymous_count = drupal_session_count($interval);
+  $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
+
+  // Format the output with proper grammar.
+  if ($anonymous_count == 1 && $authenticated_count == 1) {
+    $output = t('There is currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
+  }
+  else {
+    $output = t('There are currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
+  }
+
+  // Display a list of currently online users.
+  $max_users = variable_get('user_block_max_list_count', 10);
+  if ($authenticated_count && $max_users) {
+    $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', array(':interval' => $interval), 0, $max_users)->fetchAll();
+    $output .= theme('user_list', $items, t('Online users'));
+  }
+  return $output;   
+}
+
 /**
  * Implementation of hook_block_configure().
+ * 
+ * @TODO: Remove this hook and instead add configuration options by altering the 
+ * blocks configuration form.
  */
 function user_block_configure($delta = '') {
   global $user;
@@ -783,6 +844,9 @@
 
 /**
  * Implementation of hook_block_save().
+ * 
+ * @TODO: Remove this hook and instead save configuration options by registering a submit 
+ * handler in the blocks configuration form.
  */
 function user_block_save($delta = '', $edit = array()) {
   global $user;
@@ -800,74 +864,6 @@
 }
 
 /**
- * Implementation of hook_block_view().
- */
-function user_block_view($delta = '') {
-  global $user;
-
-  $block = array();
-
-  switch ($delta) {
-    case 'login':
-      // For usability's sake, avoid showing two login forms on one page.
-      if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
-
-        $block['subject'] = t('User login');
-        $block['content'] = drupal_get_form('user_login_block');
-      }
-      return $block;
-
-    case 'navigation':
-      if ($menu = menu_tree()) {
-        $block['subject'] = $user->uid ? check_plain($user->name) : t('Navigation');
-        $block['content'] = $menu;
-      }
-      return $block;
-
-    case 'new':
-      if (user_access('access content')) {
-        // Retrieve a list of new users who have subsequently accessed the site successfully.
-        $items = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', array(), 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
-        $output = theme('user_list', $items);
-
-        $block['subject'] = t('Who\'s new');
-        $block['content'] = $output;
-      }
-      return $block;
-
-    case 'online':
-      if (user_access('access content')) {
-        // Count users active within the defined period.
-        $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
-
-        // Perform database queries to gather online user lists. We use s.timestamp
-        // rather than u.access because it is much faster.
-        $anonymous_count = drupal_session_count($interval);
-        $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
-
-        // Format the output with proper grammar.
-        if ($anonymous_count == 1 && $authenticated_count == 1) {
-          $output = t('There is currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
-        }
-        else {
-          $output = t('There are currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
-        }
-
-        // Display a list of currently online users.
-        $max_users = variable_get('user_block_max_list_count', 10);
-        if ($authenticated_count && $max_users) {
-          $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', array(':interval' => $interval), 0, $max_users)->fetchAll();
-          $output .= theme('user_list', $items, t('Online users'));
-        }
-
-        $block['subject'] = t('Who\'s online');
-        $block['content'] = $output;
-      }
-      return $block;
-  }
-}
-
-/**
  * Process variables for user-picture.tpl.php.
  *
  * The $variables array contains the following arguments:
Index: modules/system/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/block.tpl.php,v
retrieving revision 1.7
diff -u -r1.7 block.tpl.php
--- modules/system/block.tpl.php	13 Oct 2008 12:31:43 -0000	1.7
+++ modules/system/block.tpl.php	27 Dec 2008 22:23:56 -0000
@@ -25,9 +25,9 @@
  * @see template_preprocess_block()
  */
 ?>
-<div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="block block-<?php print $block->module ?>">
-<?php if ($block->subject): ?>
-  <h2><?php print $block->subject ?></h2>
+<div id="block-<?php print $block->block_id; ?>" class="block block-<?php print $block->module ?>">
+<?php if ($block->title): ?>
+  <h2><?php print $block->title?></h2>
 <?php endif;?>
 
   <div class="content">
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.294
diff -u -r1.294 system.install
--- modules/system/system.install	26 Dec 2008 11:04:39 -0000	1.294
+++ modules/system/system.install	27 Dec 2008 22:24:30 -0000
@@ -367,10 +367,14 @@
 
   db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'theme_default', 's:7:"garland";');
   db_query("UPDATE {system} SET status = %d WHERE type = '%s' AND name = '%s'", 1, 'theme', 'garland');
-  db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'login', 'garland', 1, 0, 'left', '', -1);
-  db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'navigation', 'garland', 1, 0, 'left', '', -1);
-  db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'system', 'powered-by', 'garland', 1, 10, 'footer', '', -1);
-
+  
+  // Add a user login block instance to the left sidebar of the default theme.
+  db_query("INSERT INTO {block_instance} (block_id, theme, region, weight, pages) VALUES ('%s', '%s', '%s', %d, '%s')", 'user_login','garland', 'left', 0, '');
+  // Add the main interactive navigation menu for the site to the left sidebar of the default theme.
+  db_query("INSERT INTO {block_instance} (block_id, theme, region, weight, pages) VALUES ('%s', '%s', '%s', %d, '%s')", 'user_navigation','garland', 'left', 1, '');
+  // Add the powered-by block to the footer of the default theme.
+  db_query("INSERT INTO {block_instance} (block_id, theme, region, weight, pages) VALUES ('%s', '%s', '%s', %d, '%s')", 'system_powerd_by','garland', 'footer', 0, '');
+    
   db_query("INSERT INTO {node_access} (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, '%s', %d, %d, %d)", 0, 0, 'all', 1, 0, 0);
 
   // Add input formats.
Index: themes/garland/block.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/block.tpl.php,v
retrieving revision 1.4
diff -u -r1.4 block.tpl.php
--- themes/garland/block.tpl.php	14 Apr 2008 17:48:46 -0000	1.4
+++ themes/garland/block.tpl.php	27 Dec 2008 22:24:48 -0000
@@ -1,10 +1,10 @@
 <?php
 // $Id: block.tpl.php,v 1.4 2008/04/14 17:48:46 dries Exp $
 ?>
-<div id="block-<?php print $block->module . '-' . $block->delta; ?>" class="clear-block block block-<?php print $block->module ?>">
+<div id="block-<?php print $block->block_id ?>" class="clear-block block block-<?php print $block->module ?>">
 
-<?php if (!empty($block->subject)): ?>
-  <h2><?php print $block->subject ?></h2>
+<?php if (!empty($block->title)): ?>
+  <h2><?php print $block->title ?></h2>
 <?php endif;?>
 
   <div class="content"><?php print $block->content ?></div>
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.316
diff -u -r1.316 block.module
--- modules/block/block.module	16 Dec 2008 23:57:31 -0000	1.316
+++ modules/block/block.module	27 Dec 2008 22:23:56 -0000
@@ -184,7 +184,8 @@
  */
 function block_block_list() {
   $blocks = array();
-
+  
+  // @TODO: convert to callbacks structure.
   $result = db_query('SELECT bid, info FROM {box} ORDER BY info');
   while ($block = db_fetch_object($result)) {
     $blocks[$block->bid]['info'] = $block->info;
@@ -196,6 +197,9 @@
 
 /**
  * Implementation of hook_block_configure().
+ 
+ * @TODO: Remove this hook and instead add configuration options by altering the 
+ * blocks configuration form.
  */
 function block_block_configure($delta = 0, $edit = array()) {
   $box = array('format' => FILTER_FORMAT_DEFAULT);
@@ -209,6 +213,8 @@
 
 /**
  * Implementation of hook_block_save().
+ * @TODO: Remove this hook and instead save configuration options by registering a submit 
+ * handler in the blocks configuration form. 
  */
 function block_block_save($delta = 0, $edit = array()) {
   block_box_save($edit, $delta);
@@ -226,6 +232,95 @@
 }
 
 /**
+ * Collect, alter and store the block definitions exported by enabled modules.
+ */
+function block_router_build($reset = FALSE) {
+  static $blocks;  
+  
+  if (!isset($blocks) || $reset) {
+    if (!$reset && ($cache = cache_get('router:', 'cache_block')) && isset($cache->data)) {
+      $blocks = $cache->data;
+    }
+    else {
+      // We need to manually call each module so that we can know which module
+      // a given block came from.
+      $blocks = array();
+      foreach (module_implements('block_list', TRUE) as $module) {
+        $module_blocks = call_user_func($module . '_block_list');
+        if (isset($module_blocks) && is_array($module_blocks)) {
+          foreach (array_keys($module_blocks) as $block_id) {
+            $module_blocks[$block_id]['module'] = $module;
+          }
+          $blocks = array_merge($blocks, $module_blocks);
+        }
+      }
+      
+      // Alter the block callbacks.
+      drupal_alter('block', $callbacks);
+      
+      // Delete the existing block router since we have some data to replace it.
+      db_delete('block_router')->execute();
+     
+      // Prepare insert object.
+      $insert = db_insert('block_router')
+        ->fields(array(
+          'block_id',        
+          'module',          
+          'block_name',      
+          'description',     
+          'cache',           
+          'title',           
+          'title_callback',  
+          'title_arguments', 
+          'block_callback',  
+          'block_arguments', 
+          'access_callback', 
+          'access_arguments'
+        ));
+        
+      foreach ($blocks as $block_id => $v) {
+        $block = &$blocks[$block_id];
+    
+        // Set defaults.
+        $block += array(
+          'access arguments' => array(),
+          'access callback' => 'user_access',
+          'block arguments' => array(),
+          'block callback' => '',
+          'title' => '',
+          'title arguments' => array(),
+          'title callback' => 't',
+          'description' => '',
+          'cache' => BLOCK_CACHE_PER_ROLE,
+        );    
+        
+        // Fill in insert object values.
+        $insert->values(array(
+          'block_id' => $block_id,        
+          'module' => $block['module'],          
+          'block_name' => $block['title'],      
+          'description' => $block['description'],     
+          'cache' => $block['cache'],           
+          'title' => $block['title'],           
+          'title_callback' => $block['title callback'],  
+          'title_arguments' => serialize($block['title arguments']), 
+          'block_callback' => $block['block callback'],  
+          'block_arguments' => serialize($block['block arguments']), 
+          'access_callback' => $block['access callback'], 
+          'access_arguments' => serialize($block['access arguments']),
+        ));
+      }
+      // Execute insert object.
+      $insert->execute();
+
+      // Sotore a copy in the block cache.  
+      cache_set('router:', $blocks, 'cache_block');
+    }
+  }    
+  return $blocks;
+}
+
+/**
  * Update the 'block' DB table with the blocks currently exported by modules.
  *
  * @return
@@ -234,72 +329,25 @@
 function _block_rehash() {
   global $theme_key;
 
+  $blocks = block_router_build(TRUE);
+  $blocks_ids = array_keys($blocks);
+  
   init_theme();
-
-  $result = db_query("SELECT * FROM {block} WHERE theme = '%s'", $theme_key);
-  $old_blocks = array();
-  while ($old_block = db_fetch_array($result)) {
-    $old_blocks[$old_block['module']][$old_block['delta']] = $old_block;
-  }
-
-  $blocks = array();
+  
   // Valid region names for the theme.
-  $regions = system_region_list($theme_key);
-
-  foreach (module_implements('block_list') as $module) {
-    $module_blocks = module_invoke($module, 'block_list');
-    if ($module_blocks) {
-      foreach ($module_blocks as $delta => $block) {
-        if (empty($old_blocks[$module][$delta])) {
-          // If it's a new block, add identifiers.
-          $block['module'] = $module;
-          $block['delta']  = $delta;
-          $block['theme']  = $theme_key;
-          if (!isset($block['pages'])) {
-            // {block}.pages is type 'text', so it cannot have a
-            // default value, and not null, so we need to provide
-            // value if the module did not.
-            $block['pages']  = '';
-          }
-          // Add defaults and save it into the database.
-          drupal_write_record('block', $block);
-          // Set region to none if not enabled.
-          $block['region'] = $block['status'] ? $block['region'] : BLOCK_REGION_NONE;
-          // Add to the list of blocks we return.
-          $blocks[] = $block;
-        }
-        else {
-          // If it's an existing block, database settings should overwrite
-          // the code. But aside from 'info' everything that's definable in
-          // code is stored in the database and we do not store 'info', so we
-          // do not need to update the database here.
-          // Add 'info' to this block.
-          $old_blocks[$module][$delta]['info'] = $block['info'];
-          // If the region name does not exist, disable the block and assign it to none.
-          if (!empty($old_blocks[$module][$delta]['region']) && !isset($regions[$old_blocks[$module][$delta]['region']])) {
-            drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $old_blocks[$module][$delta]['info'], '%region' => $old_blocks[$module][$delta]['region'])), 'warning');
-            $old_blocks[$module][$delta]['status'] = 0;
-            $old_blocks[$module][$delta]['region'] = BLOCK_REGION_NONE;
-          }
-          else {
-            $old_blocks[$module][$delta]['region'] = $old_blocks[$module][$delta]['status'] ? $old_blocks[$module][$delta]['region'] : BLOCK_REGION_NONE;
-          }
-          // Add this block to the list of blocks we return.
-          $blocks[] = $old_blocks[$module][$delta];
-          // Remove this block from the list of blocks to be deleted.
-          unset($old_blocks[$module][$delta]);
-        }
-      }
-    }
-  }
-
-  // Remove blocks that are no longer defined by the code from the database.
-  foreach ($old_blocks as $module => $old_module_blocks) {
-    foreach ($old_module_blocks as $delta => $block) {
-      db_query("DELETE FROM {block} WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $module, $delta, $theme_key);
-    }
-  }
-  return $blocks;
+  $regions = array_keys(system_region_list($theme_key));
+  
+  // Remove  all instances of blocks that are no longer defined, 
+  // or that are assigned to an invalid region in the current theme.
+  db_delete('block_instance')
+    ->condition(db_or()
+      ->condition('block_id', $blocks_ids, 'NOT IN')
+      ->condition(db_and()
+        ->condition('region', $regions, 'NOT IN')
+        ->condition('theme' , $theme_key)
+      )
+    ) 
+    ->execute();
 }
 
 function block_box_get($bid) {
@@ -386,14 +434,10 @@
  *   The name of a region.
  *
  * @return
- *   An array of block objects, indexed with <i>module</i>_<i>delta</i>.
+ *   An array of block objects, indexed with the block ID.
  *   If you are displaying your blocks in one or two sidebars, you may check
  *   whether this array is empty to see how many columns are going to be
  *   displayed.
- *
- * @todo
- *   Now that the blocks table has a primary key, we should use that as the
- *   array key instead of <i>module</i>_<i>delta</i>.
  */
 function block_list($region) {
   static $blocks = array();
@@ -417,18 +461,28 @@
  */
 function _block_load_blocks() {
   global $user, $theme_key;
-
+  
+  $block_router = block_router_build();
+  
   $blocks = array();
   $rids = array_keys($user->roles);
-  $result = db_query(db_rewrite_sql("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN (" . db_placeholders($rids) . ") OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", 'b', 'bid'), array_merge(array($theme_key), $rids));
+  // @todo: join {block_instance_role} in this query
+  $result = db_query("SELECT * FROM {block_instance}  WHERE theme = '%s' ORDER BY region, weight", $theme_key);
+  
   while ($block = db_fetch_object($result)) {
+    $block->router = $block_router[$block->block_id];
+    $block->module = $block_router[$block->block_id]['module'];
+    
     if (!isset($blocks[$block->region])) {
       $blocks[$block->region] = array();
     }
+    // Check access control.
+    // @todo: check access control
+    
     // Use the user's block visibility setting, if necessary.
-    if ($block->custom != 0) {
-      if ($user->uid && isset($user->block[$block->module][$block->delta])) {
-        $enabled = $user->block[$block->module][$block->delta];
+    if (!empty($block->custom)) {
+      if ($user->uid && isset($user->block[$block->block_id])) {
+        $enabled = $user->block[$block->block_id];
       }
       else {
         $enabled = ($block->custom == 1);
@@ -461,7 +515,7 @@
     }
     $block->enabled = $enabled;
     $block->page_match = $page_match;
-    $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block;
+    $blocks[$block->region][] = $block;
   }
 
   return $blocks;
@@ -490,27 +544,30 @@
           $array = $cache->data;
         }
         else {
-          $array = module_invoke($block->module, 'block_view', $block->delta);
+          // Render block content using block callback function.
+          $block->content = call_user_func_array($block->router['block callback'], $block->router['block arguments']);
+          // Render block title using block title callback function.
+          // t() is a special case. Since it is used very close to all the time,
+          // we handle it directly instead of using indirect, slower methods.
+          if ($block->router['title callback'] == 't') {
+            $block->title = t($block->router['title'], $block->router['title arguments']);
+          }
+          else {
+            $block->title = call_user_func_array($block->router['title callback'], array($block->router['title arguments']));
+          }
+          
           if (isset($cid)) {
-            cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
+            cache_set($cid, $block, 'cache_block', CACHE_TEMPORARY);
           }
         }
 
-        if (isset($array) && is_array($array)) {
-          foreach ($array as $k => $v) {
-            $block->$k = $v;
-          }
-        }
-        if (isset($block->content) && $block->content) {
+        if (!empty($block->content)) {
           // Override default block title if a custom display title is present.
-          if ($block->title) {
+          if (!empty($block->custom_title)) {
             // Check plain here to allow module generated titles to keep any markup.
-            $block->subject = $block->title == '<none>' ? '' : check_plain($block->title);
-          }
-          if (!isset($block->subject)) {
-            $block->subject = '';
+            $block->title = $block->custom_title == '<none>' ? '' : check_plain($block->custom_title);
           }
-          $region_blocks["{$block->module}_{$block->delta}"] = $block;
+          $region_blocks[$block->block_id] = $block;
         }
       }
     }
Index: modules/block/block.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.install,v
retrieving revision 1.17
diff -u -r1.17 block.install
--- modules/block/block.install	16 Dec 2008 23:57:31 -0000	1.17
+++ modules/block/block.install	27 Dec 2008 22:23:53 -0000
@@ -5,64 +5,148 @@
  * Implementation of hook_schema().
  */
 function block_schema() {
-  $schema['block'] = array(
-    'description' => 'Stores block settings, such as region and visibility settings.',
+  $schema['block_router'] = array(
+    'description' => 'Stores block\'s info and callbacks defined by enabled modules.',
     'fields' => array(
-      'bid' => array(
-        'type' => 'serial',
+      'block_id' => array(
+        'description' => 'Primary Key: the block unique identifier',
+        'type' => 'varchar',
+        'length' => 255,
         'not null' => TRUE,
-        'description' => 'Primary Key: Unique block ID.',
+        'default' => '',
       ),
       'module' => array(
         'type' => 'varchar',
-        'length' => 64,
+        'length' => 255,
         'not null' => TRUE,
         'default' => '',
         'description' => "The module from which the block originates; for example, 'user' for the Who's Online block, and 'block' for any custom blocks.",
       ),
-      'delta' => array(
+      'block_name' => array(
+        'description' => 'The human readable name of the block, as shown to the user in the block admin pages.',
         'type' => 'varchar',
-        'length' => 32,
+        'length' => 255,
         'not null' => TRUE,
-        'default' => '0',
-        'description' => 'Unique ID for block within a module.',
+        'default' => '',
       ),
-      'theme' => array(
+      'description' => array(
+        'description' => 'Provide a more descriptive information about the block.',
         'type' => 'varchar',
-        'length' => 64,
+        'length' => 255, 
         'not null' => TRUE,
         'default' => '',
-        'description' => 'The theme under which the block settings apply.',
       ),
-      'status' => array(
+      'cache' => array(
         'type' => 'int',
         'not null' => TRUE,
-        'default' => 0,
+        'default' => 1,
         'size' => 'tiny',
-        'description' => 'Block enabled status. (1 = enabled, 0 = disabled)',
+        'description' => 'Binary flag to indicate block cache mode. (-1: Do not cache, 1: Cache per role, 2: Cache per user, 4: Cache per page, 8: Block cache global) See BLOCK_CACHE_* constants in block.module for more detailed information.',
+      ),      
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Title for the block provided by modules through hook_blocks.)',
+      ),
+      'title_callback' => array(
+        'description' => 'A function which will alter the title. Defaults to )',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'title_arguments' => array(
+        'description' => 'A serialized array of arguments for the title callback. If empty, the title will be used as the sole argument for the title callback.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),      
+      'block_callback' => array(
+        'description' => 'Name of a function used to render the block on the system administration page for this item.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'default' => '',
+      ), 
+      'block_arguments' => array(
+        'description' => 'A serialized array of arguments for the block callback.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),  
+      'access_callback' => array(
+        'description' => 'The callback which determines the access to this block. Defaults to user_access.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'access_arguments' => array(
+        'description' => 'A serialized array of arguments for the access callback.',
+        'type' => 'text',
+        'not null' => FALSE,
+      ),       
+    ),
+    
+    'primary key' => array('block_id'),
+  );
+  
+  $schema['block_instance'] = array(
+    'description' => 'Stores blocks intances in themes and regions.',
+    'fields' => array(
+      'biid' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,  
+        'description' => 'Primary key: The block instance unique identifier.',
+      ),
+      'block_id' => array(
+        'description' => 'The block unique identifier.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
       ),
+      'theme' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Theme name in which the block instance is set.',
+      ),
+      'region' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Theme region within which the block instance is set.',
+      ),    
       'weight' => array(
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => 'Block weight within region.',
+        'description' => 'Block instance weight within region.',
       ),
-      'region' => array(
+      'custom_title' => array(
         'type' => 'varchar',
-        'length' => 64,
+        'length' => 255,
         'not null' => TRUE,
         'default' => '',
-        'description' => 'Theme region within which the block is set.',
-      ),
-      'custom' => array(
+        'description' => 'Custom block title.',
+      ),       
+      'user_visibilty' => array(
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
         'description' => 'Flag to indicate how users may control visibility of the block. (0 = Users cannot control, 1 = On by default, but can be hidden, 2 = Hidden by default, but can be shown)',
       ),
-      'visibility' => array(
+      'page_visibility' => array(
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
@@ -73,45 +157,26 @@
         'type' => 'text',
         'not null' => TRUE,
         'description' => 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.',
-      ),
-      'title' => array(
-        'type' => 'varchar',
-        'length' => 64,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Custom title for the block. (Empty string will use block default title, &lt;none&gt; will remove the title, text will cause block to use specified title.)',
-      ),
-      'cache' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-        'size' => 'tiny',
-        'description' => 'Binary flag to indicate block cache mode. (-1: Do not cache, 1: Cache per role, 2: Cache per user, 4: Cache per page, 8: Block cache global) See BLOCK_CACHE_* constants in block.module for more detailed information.',
-      ),
+      ),      
     ),
-    'primary key' => array('bid'),
+    'primary key' => array('biid'),
     'unique keys' => array(
-      'tmd' => array('theme', 'module', 'delta'),
-    ),
+      'btr' => array('block_id', 'theme', 'region')
+    ),    
     'indexes' => array(
-      'list' => array('theme', 'status', 'region', 'weight', 'module'),
-    ),
-  );
+      'list' => array('theme', 'region', 'weight')
+    )
+  );  
 
-  $schema['block_role'] = array(
-    'description' => 'Sets up access permissions for blocks based on user roles',
+  $schema['block_instance_role'] = array(
+    'description' => 'Sets up access permissions for blocks instances based on user roles.',
     'fields' => array(
-      'module' => array(
-        'type' => 'varchar',
-        'length' => 64,
-        'not null' => TRUE,
-        'description' => "The block's origin module, from {block}.module.",
-      ),
-      'delta' => array(
+      'biid' => array(
+        'description' => 'The block instance ID from {block_instance}.biid.',
         'type' => 'varchar',
-        'length' => 32,
+        'length' => 255,
         'not null' => TRUE,
-        'description' => "The block's unique delta within module, from {block}.delta.",
+        'default' => '',
       ),
       'rid' => array(
         'type' => 'int',
@@ -120,10 +185,7 @@
         'description' => "The user's role ID from {users_roles}.rid.",
       ),
     ),
-    'primary key' => array('module', 'delta', 'rid'),
-    'indexes' => array(
-      'rid' => array('rid'),
-    ),
+    'primary key' => array('biid', 'rid'),
   );
 
   $schema['box'] = array(
@@ -133,7 +195,7 @@
         'type' => 'serial',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'description' => "The block's {block}.bid.",
+        'description' => "Primary key: The box unique identifier.",
       ),
       'body' => array(
         'type' => 'text',
