diff --git a/core/lib/Drupal/Core/Annotation/Evaluate.php b/core/lib/Drupal/Core/Annotation/Evaluate.php
new file mode 100644
index 0000000..a2dfd25
--- /dev/null
+++ b/core/lib/Drupal/Core/Annotation/Evaluate.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Annotation\Evaluate.
+ */
+
+namespace Drupal\Core\Annotation;
+
+use Drupal\Core\Annotation\AnnotationInterface;
+
+/**
+ * @Annotation
+ */
+class Evaluate implements AnnotationInterface {
+  protected $code;
+
+  public function __construct($values) {
+    $this->code = $values['value'];
+  }
+
+  /**
+   * Implements AnnotationInterface::get().
+   */
+  public function get() {
+    return eval($this->code);
+  }
+}
diff --git a/core/lib/Drupal/Core/Annotation/Plugin.php b/core/lib/Drupal/Core/Annotation/Plugin.php
index cb1ed60..d1a03c8 100644
--- a/core/lib/Drupal/Core/Annotation/Plugin.php
+++ b/core/lib/Drupal/Core/Annotation/Plugin.php
@@ -36,14 +36,22 @@ class Plugin implements AnnotationInterface {
    * classed annotations that were used.
    */
   public function __construct($values) {
+    $this->definition = $this->parse($values);
+  }
+
+  public function parse($values) {
     foreach ($values as $key => $value) {
       if ($value instanceof AnnotationInterface) {
-        $this->definition[$key] = $value->get();
+        $definitions[$key] = $value->get();
+      }
+      elseif (is_array($value)) {
+        $definitions[$key] = $this->parse($value);
       }
       else {
-        $this->definition[$key] = $value;
+        $definitions[$key] = $value;
       }
     }
+    return $definitions;
   }
 
   /**
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 047026a..ceecd3f 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -347,95 +347,6 @@ function aggregator_queue_info() {
 }
 
 /**
- * Implements hook_block_info().
- */
-function aggregator_block_info() {
-  $blocks = array();
-  $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
-  foreach ($result as $category) {
-    $blocks['category-' . $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
-  }
-  $result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
-  foreach ($result as $feed) {
-    $blocks['feed-' . $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
-  }
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function aggregator_block_configure($delta = '') {
-  list($type, $id) = explode('-', $delta);
-  if ($type == 'category') {
-    $value = db_query('SELECT block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchField();
-    $form['block'] = array(
-      '#type' => 'select',
-      '#title' => t('Number of news items in block'),
-      '#default_value' => $value,
-      '#options' => drupal_map_assoc(range(2, 20)),
-    );
-    return $form;
-  }
-}
-
-/**
- * Implements hook_block_save().
- */
-function aggregator_block_save($delta = '', $edit = array()) {
-  list($type, $id) = explode('-', $delta);
-  if ($type == 'category') {
-    db_update('aggregator_category')
-      ->fields(array('block' => $edit['block']))
-      ->condition('cid', $id)
-      ->execute();
-  }
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates blocks for the latest news items in each category and feed.
- */
-function aggregator_block_view($delta = '') {
-  if (user_access('access news feeds')) {
-    $block = array();
-    list($type, $id) = explode('-', $delta);
-    $result = FALSE;
-    switch ($type) {
-      case 'feed':
-        if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
-          $block['subject'] = check_plain($feed->title);
-          $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id));
-          $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
-        }
-        break;
-
-      case 'category':
-        if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
-          $block['subject'] = check_plain($category->title);
-          $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $category->block, array(':cid' => $category->cid));
-          $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
-        }
-        break;
-    }
-
-    $items = array();
-    if (!empty($result)) {
-      foreach ($result as $item) {
-        $items[] = theme('aggregator_block_item', array('item' => $item));
-      }
-    }
-
-    // Only display the block if there are items to show.
-    if (count($items) > 0) {
-      $block['content'] = theme('item_list', array('items' => $items)) . $read_more;
-    }
-    return $block;
-  }
-}
-
-/**
  * Adds/edits/deletes aggregator categories.
  *
  * @param $edit
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php
new file mode 100644
index 0000000..c6b2e9a
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/CategoryBlock.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class CategoryBlock implements DerivativeInterface {
+  protected $derivatives;
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject();
+    $this->derivatives[$derivative_id] = $base_plugin_definition;
+    $this->derivatives[$derivative_id]['delta'] = $result->cid;
+    $this->derivatives[$derivative_id]['subject'] = t('@title category latest items', array('@title' => $result->title));
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
+    foreach ($result as $category) {
+      $this->derivatives[$category->cid] = $this->config;
+      $this->derivatives[$category->cid]['delta'] = $category->cid;
+      $this->derivatives[$category->cid]['subject'] = t('@title category latest items', array('@title' => $category->title));
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php
new file mode 100644
index 0000000..456535d
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Derivative/FeedBlock.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class FeedBlock implements DerivativeInterface {
+  protected $derivatives;
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject();
+    $this->derivatives[$derivative_id] = $base_plugin_definition;
+    $this->derivatives[$derivative_id]['delta'] = $result->cid;
+    $this->derivatives[$derivative_id]['subject'] = t('@title feed latest items', array('@title' => $result->title));
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
+    foreach ($result as $feed) {
+      $this->derivatives[$feed->fid] = $this->config;
+      $this->derivatives[$feed->fid]['delta'] = $feed->fid;
+      $this->derivatives[$feed->fid]['subject'] = t('@title feed latest items', array('@title' => $feed->title));
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php
new file mode 100644
index 0000000..d893f67
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/CategoryBlock.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "aggregator_category_block",
+ *   subject = @Translation("Aggregator category"),
+ *   module = "aggregator",
+ *   derivative = "Drupal\aggregator\Plugin\Derivative\CategoryBlock",
+ *   settings = {
+ *    "block_count" = "10",
+ *    "status" = "TRUE"
+ *   }
+ * )
+ */
+class CategoryBlock extends BlockBase {
+  protected $derivatives;
+  protected $derivative;
+
+  public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
+    parent::__construct($configuration, $plugin_id, $discovery);
+    $this->derivatives = array();
+  }
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    return user_access('access news feeds');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of news items in block'),
+      '#default_value' => $this->config['settings']['block_count'],
+      '#options' => drupal_map_assoc(range(2, 20)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->config['settings']['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $id = $this->derivative;
+    if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
+      $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $this->config['settings']['block_count'], array(':cid' => $category->cid));
+      $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
+
+      $items = array();
+      foreach ($result as $item) {
+        $items[] = theme('aggregator_block_item', array('item' => $item));
+      }
+
+      // Only display the block if there are items to show.
+      if (count($items) > 0) {
+        return array(
+          '#block' => $this->derivatives[$this->derivative],
+          '#children' => theme('item_list', array('items' => $items)) . $read_more,
+        );
+      }
+      return array();
+    }
+  }
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php
new file mode 100644
index 0000000..7a9e6d1
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/block/block/FeedBlock.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\aggregator\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "aggregator_feed_block",
+ *   subject = @Translation("Aggregator feed"),
+ *   module = "aggregator",
+ *   derivative = "Drupal\aggregator\Plugin\Derivative\FeedBlock",
+ *   settings = {
+ *    "block_count" = "10",
+ *    "status" = "TRUE"
+ *   }
+ * )
+ */
+class FeedBlock extends BlockBase {
+  protected $derivatives;
+  protected $derivative;
+
+  public function __construct() {
+    $this->derivatives = array();
+  }
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    return user_access('access news feeds');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of news items in block'),
+      '#default_value' => $this->config['block_count'],
+      '#options' => drupal_map_assoc(range(2, 20)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->config['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $id = $this->derivative;
+    if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
+      $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $this->config['block_count'], array(':fid' => $id));
+      $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
+
+      $items = array();
+      foreach ($result as $item) {
+        $items[] = theme('aggregator_block_item', array('item' => $item));
+      }
+
+      // Only display the block if there are items to show.
+      if (count($items) > 0) {
+        return array(
+          '#block' => $this->config,
+          '#children' => theme('item_list', array('items' => $items)) . $read_more,
+        );
+      }
+    }
+  }
+}
diff --git a/core/modules/block/block-library-form.tpl.php b/core/modules/block/block-library-form.tpl.php
new file mode 100644
index 0000000..8179076
--- /dev/null
+++ b/core/modules/block/block-library-form.tpl.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to configure blocks.
+ *
+ * Available variables:
+ * - $left: Any form array elements that should appear in the left hand column.
+ * - $right: Any form array elements that should appear in the right hand column.
+ * - $form_submit: Form submit button.
+ *
+ * @see template_preprocess_block_library_form()
+ * @see theme_block_library_form()
+ *
+ * @ingroup themeable
+ */
+?>
+<div id="block-library" class="container">
+  <div class="left-col">
+    <div class="inside">
+      <?php print $left; ?>
+    </div>
+  </div>
+  <div class="right-col">
+    <div class="inside">
+      <?php print $right; ?>
+    </div>
+  </div>
+  <?php if ($form_submit) { ?>
+  <div class="bottom-bar"><?php print $form_submit; ?></div>
+  <?php } ?>
+</div>
diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc
index 848298f..292b472 100644
--- a/core/modules/block/block.admin.inc
+++ b/core/modules/block/block.admin.inc
@@ -114,18 +114,15 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r
   $form['blocks'] = array();
   $form['#tree'] = TRUE;
 
-  foreach ($blocks as $i => $block) {
-    $key = $block['module'] . '_' . $block['delta'];
-    $form['blocks'][$key]['module'] = array(
+  foreach ($blocks as $key => $instance) {
+    $block = $instance->getConfig();
+    $form['blocks'][$key]['config_id'] = array(
       '#type' => 'value',
-      '#value' => $block['module'],
-    );
-    $form['blocks'][$key]['delta'] = array(
-      '#type' => 'value',
-      '#value' => $block['delta'],
+      '#value' => $block['config_id'],
     );
+    $info = $instance->getDefinition();
     $form['blocks'][$key]['info'] = array(
-      '#markup' => check_plain($block['info']),
+      '#markup' => check_plain($info['subject']),
     );
     $form['blocks'][$key]['theme'] = array(
       '#type' => 'hidden',
@@ -136,28 +133,26 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r
       '#default_value' => $block['weight'],
       '#delta' => $weight_delta,
       '#title_display' => 'invisible',
-      '#title' => t('Weight for @block block', array('@block' => $block['info'])),
+      '#title' => t('Weight for @block block', array('@block' => $info['subject'])),
     );
     $form['blocks'][$key]['region'] = array(
       '#type' => 'select',
       '#default_value' => $block['region'] != BLOCK_REGION_NONE ? $block['region'] : NULL,
       '#empty_value' => BLOCK_REGION_NONE,
       '#title_display' => 'invisible',
-      '#title' => t('Region for @block block', array('@block' => $block['info'])),
+      '#title' => t('Region for @block block', array('@block' => $info['subject'])),
       '#options' => $block_regions,
     );
     $form['blocks'][$key]['configure'] = array(
       '#type' => 'link',
       '#title' => t('configure'),
-      '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure',
+      '#href' => 'admin/structure/block/manage/' . $block['config_id'] . '/' . $theme . '/configure',
     );
-    if ($block['module'] == 'block') {
-      $form['blocks'][$key]['delete'] = array(
-        '#type' => 'link',
-        '#title' => t('delete'),
-        '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete',
-     );
-    }
+    $form['blocks'][$key]['delete'] = array(
+      '#type' => 'link',
+      '#title' => t('delete'),
+      '#href' => 'admin/structure/block/manage/' . $block['config_id'] . '/' . $theme . '/delete',
+   );
   }
   // Do not allow disabling the main system content block when it is present.
   if (isset($form['blocks']['system_main']['region'])) {
@@ -182,27 +177,11 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r
  * @see block_admin_display_form()
  */
 function block_admin_display_form_submit($form, &$form_state) {
-  $transaction = db_transaction();
-  try {
-    foreach ($form_state['values']['blocks'] as $block) {
-      $block['status'] = (int) ($block['region'] != BLOCK_REGION_NONE);
-      $block['region'] = $block['status'] ? $block['region'] : '';
-      db_update('block')
-        ->fields(array(
-          'status' => $block['status'],
-          'weight' => $block['weight'],
-          'region' => $block['region'],
-        ))
-        ->condition('module', $block['module'])
-        ->condition('delta', $block['delta'])
-        ->condition('theme', $block['theme'])
-        ->execute();
-    }
-  }
-  catch (Exception $e) {
-    $transaction->rollback();
-    watchdog_exception('block', $e);
-    throw $e;
+  foreach ($form_state['values']['blocks'] as $block) {
+    $config = config($block['config_id']);
+    $config->set('weight', $block['weight']);
+    $config->set('region', $block['region']);
+    $config->save();
   }
   drupal_set_message(t('The block settings have been updated.'));
   cache_invalidate(array('content' => TRUE));
@@ -213,8 +192,10 @@ function block_admin_display_form_submit($form, &$form_state) {
  *
  * Callback for usort() in block_admin_display_prepare_blocks().
  */
-function _block_compare($a, $b) {
+function _block_compare($ainstance, $binstance) {
   global $theme_key;
+  $a = $ainstance->getConfig();
+  $b = $binstance->getConfig();
 
   // Theme should be set before calling this function, or the current theme
   // is being used.
@@ -247,7 +228,9 @@ function _block_compare($a, $b) {
     }
   }
   // Sort by title.
-  return strcmp($a['info'], $b['info']);
+  $ainfo = $ainstance->getDefinition();
+  $binfo = $binstance->getDefinition();
+  return strcmp($ainfo['subject'], $binfo['subject']);
 }
 
 /**
@@ -265,247 +248,24 @@ function _block_compare($a, $b) {
  * @see block_admin_configure_submit()
  * @ingroup forms
  */
-function block_admin_configure($form, &$form_state, $module, $delta) {
-  $block = block_load($module, $delta);
-  $form['module'] = array(
+function block_admin_configure($form, &$form_state, $plugin_id, $theme = NULL) {
+  $instance = block_load($plugin_id);
+  $form['instance'] = array(
     '#type' => 'value',
-    '#value' => $block->module,
-  );
-  $form['delta'] = array(
-    '#type' => 'value',
-    '#value' => $block->delta,
-  );
-
-  // Get the block subject for the page title.
-  $info = module_invoke($block->module, 'block_info');
-  if (isset($info[$block->delta])) {
-    drupal_set_title(t("'%name' block", array('%name' => $info[$block->delta]['info'])), PASS_THROUGH);
-  }
-
-  $form['settings']['title'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Block title'),
-    '#maxlength' => 64,
-    '#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use <em>!placeholder</em> to display no title, or leave blank to use the default block title.', array('!placeholder' => '&lt;none&gt;')),
-    '#default_value' => isset($block->title) ? $block->title : '',
-    '#weight' => -19,
-  );
-
-  // Module-specific block configuration.
-  if ($settings = module_invoke($block->module, 'block_configure', $block->delta)) {
-    foreach ($settings as $k => $v) {
-      $form['settings'][$k] = $v;
-    }
-  }
-
-  // Region settings.
-  $form['regions'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Region settings'),
-    '#collapsible' => FALSE,
-    '#description' => t('Specify in which themes and regions this block is displayed.'),
-    '#tree' => TRUE,
-  );
-
-  $theme_default = variable_get('theme_default', 'stark');
-  $admin_theme = variable_get('admin_theme');
-  foreach (list_themes() as $key => $theme) {
-    // Only display enabled themes
-    if ($theme->status) {
-      $region = db_query("SELECT region FROM {block} WHERE module = :module AND delta = :delta AND theme = :theme", array(
-        ':module' => $block->module,
-        ':delta' => $block->delta,
-        ':theme' => $key,
-      ))->fetchField();
-
-      // Use a meaningful title for the main site theme and administrative
-      // theme.
-      $theme_title = $theme->info['name'];
-      if ($key == $theme_default) {
-        $theme_title = t('!theme (default theme)', array('!theme' => $theme_title));
-      }
-      elseif ($admin_theme && $key == $admin_theme) {
-        $theme_title = t('!theme (administration theme)', array('!theme' => $theme_title));
-      }
-      $form['regions'][$key] = array(
-        '#type' => 'select',
-        '#title' => $theme_title,
-        '#default_value' => !empty($region) && $region != -1 ? $region : NULL,
-        '#empty_value' => BLOCK_REGION_NONE,
-        '#options' => system_region_list($key, REGIONS_VISIBLE),
-        '#weight' => ($key == $theme_default ? 9 : 10),
-      );
-    }
-  }
-
-  // Visibility settings.
-  $form['visibility_title'] = array(
-    '#type' => 'item',
-    '#title' => t('Visibility settings'),
-  );
-  $form['visibility'] = array(
-    '#type' => 'vertical_tabs',
-    '#attached' => array(
-      'library' => array(array('block', 'drupal.block')),
-    ),
+    '#value' => $instance,
   );
-
-  // Per-path visibility.
-  $form['visibility']['path'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Pages'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#group' => 'visibility',
-    '#weight' => 0,
-  );
-
-  $access = user_access('use PHP for settings');
-  if (isset($block->visibility) && $block->visibility == BLOCK_VISIBILITY_PHP && !$access) {
-    $form['visibility']['path']['visibility'] = array(
-      '#type' => 'value',
-      '#value' => BLOCK_VISIBILITY_PHP,
-    );
-    $form['visibility']['path']['pages'] = array(
-      '#type' => 'value',
-      '#value' => isset($block->pages) ? $block->pages : '',
-    );
+  $config = $instance->getConfig();
+  if (!isset($config['config_id']) && !$theme) {
+    $theme = variable_get('theme_default', 'stark');
   }
-  else {
-    $options = array(
-      BLOCK_VISIBILITY_NOTLISTED => t('All pages except those listed'),
-      BLOCK_VISIBILITY_LISTED => t('Only the listed pages'),
-    );
-    $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %user for the current user's page and %user-wildcard for every user page. %front is the front page.", array('%user' => 'user', '%user-wildcard' => 'user/*', '%front' => '<front>'));
-
-    if (module_exists('php') && $access) {
-      $options += array(BLOCK_VISIBILITY_PHP => t('Pages on which this PHP code returns <code>TRUE</code> (experts only)'));
-      $title = t('Pages or PHP code');
-      $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => '<?php ?>'));
-    }
-    else {
-      $title = t('Pages');
-    }
-    $form['visibility']['path']['visibility'] = array(
-      '#type' => 'radios',
-      '#title' => t('Show block on specific pages'),
-      '#options' => $options,
-      '#default_value' => isset($block->visibility) ? $block->visibility : BLOCK_VISIBILITY_NOTLISTED,
-    );
-    $form['visibility']['path']['pages'] = array(
-      '#type' => 'textarea',
-      '#title' => '<span class="element-invisible">' . $title . '</span>',
-      '#default_value' => isset($block->pages) ? $block->pages : '',
-      '#description' => $description,
-    );
+  elseif (!$theme && isset($config['config_id'])) {
+    list(, , , $theme) = explode('.', $config['config_id']);
   }
-
-  // Configure the block visibility per language.
-  if (module_exists('language') && language_multilingual()) {
-    $configurable_language_types = language_types_get_configurable();
-    $existing_language_settings = db_query("SELECT type, langcode FROM {block_language} WHERE module = :module AND delta = :delta", array(
-      ':module' => $form['module']['#value'],
-      ':delta' => $form['delta']['#value'],
-    ))->fetchAll();
-    $default_langcode_options = array();
-    $default_language_type = $configurable_language_types[0];
-    foreach ($existing_language_settings as $setting) {
-      $default_langcode_options[] = $setting->langcode;
-      // Overwrite default language type if we have it set. Although this
-      // theoretically would allow per language type association, our UI
-      // only allows language type association overall for a block, so we
-      // only need a single value.
-      $default_language_type = $setting->type;
-    }
-
-    // Fetch languages.
-    $languages = language_list(LANGUAGE_ALL);
-    foreach ($languages as $language) {
-      // @TODO $language->name is not wrapped with t(), it should be replaced
-      // by CMI translation implementation.
-      $langcodes_options[$language->langcode] = $language->name;
-    }
-    $form['visibility']['language'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Languages'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#group' => 'visibility',
-      '#weight' => 5,
-    );
-    // If there are multiple configurable language types, let the user pick
-    // which one should be applied to this visibility setting. This way users
-    // can limit blocks by interface language or content language for exmaple.
-    $language_types = language_types_info();
-    $language_type_options = array();
-    foreach ($configurable_language_types as $type_key) {
-      $language_type_options[$type_key] = $language_types[$type_key]['name'];
-    }
-    $form['visibility']['language']['language_type'] = array(
-      '#type' => 'radios',
-      '#title' => t('Language type'),
-      '#options' => $language_type_options,
-      '#default_value' => $default_language_type,
-      '#access' => count($language_type_options) > 1,
-    );
-    $form['visibility']['language']['langcodes'] = array(
-      '#type' => 'checkboxes',
-      '#title' => t('Show this block only for specific languages'),
-      '#default_value' => $default_langcode_options,
-      '#options' => $langcodes_options,
-      '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visibile in all languages.'),
-    );
-  }
-
-  // Per-role visibility.
-  $default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module AND delta = :delta", array(
-    ':module' => $block->module,
-    ':delta' => $block->delta,
-  ))->fetchCol();
-  $role_options = array_map('check_plain', user_roles());
-  $form['visibility']['role'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Roles'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#group' => 'visibility',
-    '#weight' => 10,
-  );
-  $form['visibility']['role']['roles'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Show block for specific roles'),
-    '#default_value' => $default_role_options,
-    '#options' => $role_options,
-    '#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'),
-  );
-
-  // Per-user visibility.
-  $form['visibility']['user'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Users'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#group' => 'visibility',
-    '#weight' => 20,
-  );
-  $form['visibility']['user']['custom'] = array(
-    '#type' => 'radios',
-    '#title' => t('Customizable per user'),
-    '#options' => array(
-      BLOCK_CUSTOM_FIXED => t('Not customizable'),
-      BLOCK_CUSTOM_ENABLED => t('Customizable, visible by default'),
-      BLOCK_CUSTOM_DISABLED => t('Customizable, hidden by default'),
-    ),
-    '#description' => t('Allow individual users to customize the visibility of this block in their account settings.'),
-    '#default_value' => isset($block->custom) ? $block->custom : BLOCK_CUSTOM_FIXED,
-  );
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save block'),
+  $form['theme'] = array(
+    '#type' => 'value',
+    '#value' => $theme,
   );
-
+  $form += $instance->configure($form, $form_state);
   return $form;
 }
 
@@ -516,15 +276,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) {
  * @see block_admin_configure_submit()
  */
 function block_admin_configure_validate($form, &$form_state) {
-  if ($form_state['values']['module'] == 'block') {
-    $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array(
-      ':bid' => $form_state['values']['delta'],
-      ':info' => $form_state['values']['info'],
-    ))->fetchField();
-    if (empty($form_state['values']['info']) || $custom_block_exists) {
-      form_set_error('info', t('Ensure that each block description is unique.'));
-    }
-  }
+  $form_state['values']['instance']->configureValidate($form, $form_state);
 }
 
 /**
@@ -534,186 +286,16 @@ function block_admin_configure_validate($form, &$form_state) {
  * @see block_admin_configure_validate()
  */
 function block_admin_configure_submit($form, &$form_state) {
-  if (!form_get_errors()) {
-    $transaction = db_transaction();
-    try {
-      db_update('block')
-        ->fields(array(
-          'visibility' => (int) $form_state['values']['visibility'],
-          'pages' => trim($form_state['values']['pages']),
-          'custom' => (int) $form_state['values']['custom'],
-          'title' => $form_state['values']['title'],
-        ))
-        ->condition('module', $form_state['values']['module'])
-        ->condition('delta', $form_state['values']['delta'])
-        ->execute();
-
-      db_delete('block_role')
-        ->condition('module', $form_state['values']['module'])
-        ->condition('delta', $form_state['values']['delta'])
-        ->execute();
-      $query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
-      foreach (array_filter($form_state['values']['roles']) as $rid) {
-        $query->values(array(
-          'rid' => $rid,
-          'module' => $form_state['values']['module'],
-          'delta' => $form_state['values']['delta'],
-        ));
-      }
-      $query->execute();
-
-      // Store regions per theme for this block.
-      foreach ($form_state['values']['regions'] as $theme => $region) {
-        db_merge('block')
-          ->key(array('theme' => $theme, 'delta' => $form_state['values']['delta'], 'module' => $form_state['values']['module']))
-          ->fields(array(
-            'region' => ($region == BLOCK_REGION_NONE ? '' : $region),
-            'pages' => trim($form_state['values']['pages']),
-            'status' => (int) ($region != BLOCK_REGION_NONE),
-          ))
-          ->execute();
-      }
-
-      // Update the block visibility settings if we have settings to store
-      // for the existing languages.
-      if (module_exists('language') && isset($form_state['values']['langcodes'])) {
-        db_delete('block_language')
-          ->condition('module', $form_state['values']['module'])
-          ->condition('delta', $form_state['values']['delta'])
-          ->execute();
-        $query = db_insert('block_language')->fields(array(
-          'type', 'langcode', 'module', 'delta'
-        ));
-        foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
-          $query->values(array(
-            'type' => $form_state['values']['language_type'],
-            'langcode' => $langcode,
-            'module' => $form_state['values']['module'],
-            'delta' => $form_state['values']['delta'],
-          ));
-        }
-        $query->execute();
-      }
-
-      module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']);
-    }
-    catch (Exception $e) {
-      $transaction->rollback();
-      watchdog_exception('block', $e);
-      throw $e;
-    }
-    drupal_set_message(t('The block configuration has been saved.'));
-    cache_invalidate(array('content' => TRUE));
-    $form_state['redirect'] = 'admin/structure/block';
-  }
-}
-
-/**
- * Form constructor for the add block form.
- *
- * @see block_menu()
- * @see block_add_block_form_validate()
- * @see block_add_block_form_submit()
- * @ingroup forms
- */
-function block_add_block_form($form, &$form_state) {
-  return block_admin_configure($form, $form_state, 'block', NULL);
-}
-
-/**
- * Form validation handler for block_add_block_form().
- *
- * @see block_add_block_form()
- * @see block_add_block_form_submit()
- */
-function block_add_block_form_validate($form, &$form_state) {
-  $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE info = :info', 0, 1, array(':info' => $form_state['values']['info']))->fetchField();
-
-  if (empty($form_state['values']['info']) || $custom_block_exists) {
-    form_set_error('info', t('Ensure that each block description is unique.'));
+  $form_state['values']['instance']->configureSubmit($form, $form_state);
+  $config_values = $form_state['values']['instance']->getConfig();
+  $machine_name = 'plugin.core.block.' . $form_state['values']['theme'] . '.' . $form_state['values']['machine_name'];
+  $config = config($machine_name);
+  $config->set('id', $form_state['values']['instance']->getPluginId());
+  foreach ($config_values as $key => $value) {
+    $config->set($key, $value);
   }
-}
-
-/**
- * Form submission handler for block_add_block_form().
- *
- * Saves the new custom block.
- *
- * @see block_add_block_form()
- * @see block_add_block_form_validate()
- */
-function block_add_block_form_submit($form, &$form_state) {
-  $delta = db_insert('block_custom')
-    ->fields(array(
-      'body' => $form_state['values']['body']['value'],
-      'info' => $form_state['values']['info'],
-      'format' => $form_state['values']['body']['format'],
-    ))
-    ->execute();
-  // Store block delta to allow other modules to work with new block.
-  $form_state['values']['delta'] = $delta;
-
-  $query = db_insert('block')->fields(array('visibility', 'pages', 'custom', 'title', 'module', 'theme', 'status', 'weight', 'delta', 'cache'));
-  foreach (list_themes() as $key => $theme) {
-    if ($theme->status) {
-      $query->values(array(
-        'visibility' => (int) $form_state['values']['visibility'],
-        'pages' => trim($form_state['values']['pages']),
-        'custom' => (int) $form_state['values']['custom'],
-        'title' => $form_state['values']['title'],
-        'module' => $form_state['values']['module'],
-        'theme' => $theme->name,
-        'status' => 0,
-        'weight' => 0,
-        'delta' => $delta,
-        'cache' => DRUPAL_NO_CACHE,
-      ));
-    }
-  }
-  $query->execute();
-
-  $query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
-  foreach (array_filter($form_state['values']['roles']) as $rid) {
-    $query->values(array(
-      'rid' => $rid,
-      'module' => $form_state['values']['module'],
-      'delta' => $delta,
-    ));
-  }
-  $query->execute();
-
-  // Store regions per theme for this block.
-  foreach ($form_state['values']['regions'] as $theme => $region) {
-    db_merge('block')
-      ->key(array('theme' => $theme, 'delta' => $delta, 'module' => $form_state['values']['module']))
-      ->fields(array(
-        'region' => ($region == BLOCK_REGION_NONE ? '' : $region),
-        'pages' => trim($form_state['values']['pages']),
-        'status' => (int) ($region != BLOCK_REGION_NONE),
-      ))
-      ->execute();
-  }
-
-  // Update the block visibility settings if we have settings to store
-  // for the existing languages.
-  if (module_exists('language') && isset($form_state['values']['langcodes'])) {
-    $query = db_insert('block_language')->fields(array(
-      'type', 'langcode', 'module', 'delta'
-    ));
-    foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
-      $query->values(array(
-        'type' => $form_state['values']['language_type'],
-        'langcode' => $langcode,
-        'module' => $form_state['values']['module'],
-        'delta' => $form_state['values']['delta'],
-      ));
-    }
-    $query->execute();
-  }
-
-  drupal_set_message(t('The block has been created.'));
-  cache_invalidate(array('content' => TRUE));
-  $form_state['redirect'] = 'admin/structure/block';
+  $config->save();
+  $form_state['redirect'] = 'admin/structure/block/list/' . $form_state['values']['theme'];
 }
 
 /**
@@ -728,13 +310,16 @@ function block_add_block_form_submit($form, &$form_state) {
  * @see block_menu()
  * @see block_custom_block_delete_submit()
  */
-function block_custom_block_delete($form, &$form_state, $module, $delta) {
-  $block = block_load($module, $delta);
-  $custom_block = block_custom_block_get($block->delta);
-  $form['info'] = array('#type' => 'hidden', '#value' => $custom_block['info'] ? $custom_block['info'] : $custom_block['title']);
-  $form['bid'] = array('#type' => 'hidden', '#value' => $block->delta);
-
-  return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $custom_block['info'])), 'admin/structure/block', '', t('Delete'), t('Cancel'));
+function block_custom_block_delete($form, &$form_state, $plugin_id, $theme) {
+  $block = block_load($plugin_id);
+  $form['id'] = array('#type' => 'value', '#value' => $plugin_id);
+  $form['theme'] = array('#type' => 'value', '#value' => $theme);
+  $definition = $block->getDefinition();
+  $config = $block->getConfig();
+  $subject = empty($config['subject']) ? $definition['subject'] : $config['subject'];
+  $form['subject'] = array('#type' => 'value', '#value' => $subject);
+
+  return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $subject)), 'admin/structure/block', '', t('Delete'), t('Cancel'));
 }
 
 /**
@@ -743,26 +328,10 @@ function block_custom_block_delete($form, &$form_state, $module, $delta) {
  * @see block_custom_block_delete()
  */
 function block_custom_block_delete_submit($form, &$form_state) {
-  db_delete('block_custom')
-    ->condition('bid', $form_state['values']['bid'])
-    ->execute();
-  db_delete('block')
-    ->condition('module', 'block')
-    ->condition('delta', $form_state['values']['bid'])
-    ->execute();
-  db_delete('block_role')
-    ->condition('module', 'block')
-    ->condition('delta', $form_state['values']['bid'])
-    ->execute();
-  db_delete('block_language')
-    ->condition('module', 'block')
-    ->condition('delta', $form_state['values']['bid'])
-    ->execute();
-
-  drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info'])));
-  cache_invalidate(array('content' => TRUE));
-  $form_state['redirect'] = 'admin/structure/block';
-  return;
+  $config = config($form_state['values']['id']);
+  $config->delete();
+  drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['subject'])));
+  $form_state['redirect'] = 'admin/structure/block/list/' . $form_state['values']['theme'];
 }
 
 /**
diff --git a/core/modules/block/block.library.css b/core/modules/block/block.library.css
new file mode 100644
index 0000000..207a6ee
--- /dev/null
+++ b/core/modules/block/block.library.css
@@ -0,0 +1,29 @@
+#block-library .left-col,
+#block-library .right-col {
+  float:left;
+  width:66%;
+  height:100%;
+  background-color:#ffffff;
+}
+
+#block-library .right-col {
+  width:34%;
+  background-color:#f7f7f7;
+}
+
+#block-library .right-col h3 {
+  margin: 1em -20px;
+  background-color:#d7d7d7;
+  color:#333333;
+  padding:8px 15px;
+  font-size:1.1em;
+}
+
+#block-library .inside {
+  margin:0 20px;
+}
+
+#block-library .bottom-bar {
+  width:100%;
+  clear:both;
+}
\ No newline at end of file
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 0626c08..facdf99 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -1,5 +1,8 @@
 <?php
 
+use Drupal\block\Plugins\Type\BlockManager;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
 /**
  * @file
  * Controls the visual building blocks a page is constructed with.
@@ -56,10 +59,10 @@ function block_help($path, $arg) {
       $output .= '<dt>' . t('Controlling visibility') . '</dt>';
       $output .= '<dd>' . t('Blocks can be configured to be visible only on certain pages, only to users of certain roles, or only on pages displaying certain <a href="@content-type">content types</a>. Administrators can also allow specific blocks to be enabled or disabled by users when they edit their <a href="@user">My account</a> page. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.', array('@content-type' => url('admin/structure/types'), '@user' => url('user'))) . '</dd>';
       $output .= '<dt>' . t('Creating custom blocks') . '</dt>';
-      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can <a href="@block-add">add custom blocks</a>, which are then listed on the <a href="@blocks">Blocks administration page</a>. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/add'))) . '</dd>';
+      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can <a href="@block-add">add custom blocks</a>, which are then listed on the <a href="@blocks">Blocks administration page</a>. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/library'))) . '</dd>';
       $output .= '</dl>';
       return $output;
-    case 'admin/structure/block/add':
+    case 'admin/structure/block/library':
       return '<p>' . t('Use this page to create a new custom block.') . '</p>';
   }
   if ($arg[0] == 'admin' && $arg[1] == 'structure' && $arg['2'] == 'block' && (empty($arg[3]) || $arg[3] == 'list')) {
@@ -133,16 +136,11 @@ function block_menu() {
     'context' => MENU_CONTEXT_NONE,
     'file' => 'block.admin.inc',
   );
-  $items['admin/structure/block/add'] = array(
-    'title' => 'Add block',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('block_add_block_form'),
-    'access arguments' => array('administer blocks'),
-    'type' => MENU_LOCAL_ACTION,
-    'file' => 'block.admin.inc',
-  );
-  foreach (list_themes() as $key => $theme) {
-    $items['admin/structure/block/list/' . $key] = array(
+  $themes = list_themes();
+  foreach (system_plugin_manager('plugin_ui')->getDefinitions() as $plugin_id => $plugin) {
+    list($plugin_base, $key) = explode(':', $plugin_id);
+    $theme = $themes[$key];
+    $items['admin/structure/block/list/' . $plugin_id] = array(
       'title' => check_plain($theme->info['name']),
       'page arguments' => array($key),
       'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
@@ -151,16 +149,6 @@ function block_menu() {
       'access arguments' => array($key),
       'file' => 'block.admin.inc',
     );
-    if ($key != $default_theme) {
-      $items['admin/structure/block/list/' . $key . '/add'] = array(
-        'title' => 'Add block',
-        'page callback' => 'drupal_get_form',
-        'page arguments' => array('block_add_block_form'),
-        'access arguments' => array('administer blocks'),
-        'type' => MENU_LOCAL_ACTION,
-        'file' => 'block.admin.inc',
-      );
-    }
     $items['admin/structure/block/demo/' . $key] = array(
       'title' => check_plain($theme->info['name']),
       'page callback' => 'block_admin_demo',
@@ -212,53 +200,6 @@ function _block_custom_theme($theme = NULL) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function block_block_info() {
-  $blocks = array();
-
-  $result = db_query('SELECT bid, info FROM {block_custom} ORDER BY info');
-  foreach ($result as $block) {
-    $blocks[$block->bid]['info'] = $block->info;
-    // Not worth caching.
-    $blocks[$block->bid]['cache'] = DRUPAL_NO_CACHE;
-  }
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function block_block_configure($delta = 0) {
-  if ($delta) {
-    $custom_block = block_custom_block_get($delta);
-  }
-  else {
-    $custom_block = array();
-  }
-  return block_custom_block_form($custom_block);
-}
-
-/**
- * Implements hook_block_save().
- */
-function block_block_save($delta = 0, $edit = array()) {
-  block_custom_block_save($edit, $delta);
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates the administrator-defined blocks for display.
- */
-function block_block_view($delta = '') {
-  $block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject();
-  $data['subject'] = NULL;
-  $data['content'] = check_markup($block->body, $block->format, '', TRUE);
-  return $data;
-}
-
-/**
  * Implements hook_page_build().
  *
  * Renders blocks into their regions.
@@ -343,7 +284,6 @@ function block_get_blocks_by_region($region) {
  *   A renderable array.
  */
 function _block_get_renderable_region($list = array()) {
-  $weight = 0;
   $build = array();
   // Block caching is not compatible with node_access modules. We also
   // preserve the submission of forms in blocks, by fetching from cache
@@ -356,23 +296,27 @@ function _block_get_renderable_region($list = array()) {
     !in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD'));
 
   foreach ($list as $key => $block) {
+    $config = $block->getConfig();
+    $definition = $block->getDefinition();
     $build[$key] = array(
       '#block' => $block,
-      '#weight' => ++$weight,
+      '#weight' => (int) $config['weight'],
       '#theme_wrappers' => array('block'),
     );
 
-    if ($not_cacheable || in_array($block->cache, array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) {
+    if ($not_cacheable || in_array($config['cache'], array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) {
       // Non-cached blocks get built immediately. Provides more content
       // that can be easily manipulated during hook_page_alter().
       $build[$key] = _block_get_renderable_block($build[$key]);
     }
     else {
+      $key_components = explode('.', $key);
+      $id = array_pop($key_components);
       $build[$key] += array(
         '#pre_render' => array('_block_get_renderable_block'),
         '#cache' => array(
-          'keys' => array($block->module, $block->delta),
-          'granularity' => $block->cache,
+          'keys' => array($id, $config['module']),
+          'granularity' => $config['cache'],
           'bin' => 'block',
           'tags' => array('content' => TRUE),
         ),
@@ -384,11 +328,11 @@ function _block_get_renderable_region($list = array()) {
     // skip the help block, since we assume that most users do not need or want
     // to perform contextual actions on the help block, and the links needlessly
     // draw attention on it.
-    if ($key != 'system_main' && $key != 'system_help') {
-      $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta));
+    if ($definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemHelpBlock' && $definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemMainBlock') {
+      global $theme;
+      $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key, $theme));
     }
   }
-  $build['#sorted'] = TRUE;
   return $build;
 }
 
@@ -403,107 +347,12 @@ function _block_get_renderable_region($list = array()) {
  *   Blocks currently exported by modules.
  */
 function _block_rehash($theme = NULL) {
-  global $theme_key;
-
-  drupal_theme_initialize();
-  if (!isset($theme)) {
-    // If theme is not specifically set, rehash for the current theme.
-    $theme = $theme_key;
-  }
-  $regions = system_region_list($theme);
-
-  // These are the blocks the function will return.
   $blocks = array();
-  // These are the blocks defined by code and modified by the database.
-  $current_blocks = array();
-  // These are {block}.bid values to be kept.
-  $bids = array();
-  $or = db_or();
-  // Gather the blocks defined by modules.
-  foreach (module_implements('block_info') as $module) {
-    $module_blocks = module_invoke($module, 'block_info');
-    foreach ($module_blocks as $delta => $block) {
-      // Compile a condition to retrieve this block from the database.
-      $condition = db_and()
-        ->condition('module', $module)
-        ->condition('delta', $delta);
-      $or->condition($condition);
-      // Add identifiers.
-      $block['module'] = $module;
-      $block['delta']  = $delta;
-      $block['theme']  = $theme;
-      $current_blocks[$module][$delta] = $block;
-    }
-  }
-  // Save the blocks defined in code for alter context.
-  $code_blocks = $current_blocks;
-  $database_blocks = db_select('block', 'b')
-    ->fields('b')
-    ->condition($or)
-    ->condition('theme', $theme)
-    ->execute();
-  foreach ($database_blocks as $block) {
-    // Preserve info which is not in the database.
-    $block->info = $current_blocks[$block->module][$block->delta]['info'];
-    // The cache mode can only by set from hook_block_info(), so that has
-    // precedence over the database's value.
-    if (isset($current_blocks[$block->module][$block->delta]['cache'])) {
-      $block->cache = $current_blocks[$block->module][$block->delta]['cache'];
-    }
-    // Blocks stored in the database override the blocks defined in code.
-    $current_blocks[$block->module][$block->delta] = get_object_vars($block);
-    // Preserve this block.
-    $bids[$block->bid] = $block->bid;
-  }
-  drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
-  foreach ($current_blocks as $module => $module_blocks) {
-    foreach ($module_blocks as $delta => $block) {
-      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']  = '';
-      }
-      // Make sure weight is set.
-      if (!isset($block['weight'])) {
-        $block['weight'] = 0;
-      }
-      if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) {
-        drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block['info'], '%region' => $block['region'])), 'warning');
-        // Disabled modules are moved into the BLOCK_REGION_NONE later so no
-        // need to move the block to another region.
-        $block['status'] = 0;
-      }
-      // Set region to none if not enabled and make sure status is set.
-      if (empty($block['status'])) {
-        $block['status'] = 0;
-        $block['region'] = BLOCK_REGION_NONE;
-      }
-      // There is no point saving disabled blocks. Still, we need to save them
-      // because the 'title' attribute is saved to the {blocks} table.
-      if (isset($block['bid'])) {
-        // If the block has a bid property, it comes from the database and
-        // the record needs to be updated, so set the primary key to 'bid'
-        // before passing to drupal_write_record().
-        $primary_keys = array('bid');
-        // Remove a block from the list of blocks to keep if it became disabled.
-        unset($bids[$block['bid']]);
-      }
-      else {
-        $primary_keys = array();
-      }
-      drupal_write_record('block', $block, $primary_keys);
-      // Add to the list of blocks we return.
-      $blocks[] = $block;
-    }
-  }
-  if ($bids) {
-    // Remove disabled that are no longer defined by the code from the
-    // database.
-    db_delete('block')
-      ->condition('bid', $bids, 'NOT IN')
-      ->condition('theme', $theme)
-      ->execute();
+  $instances = array();
+  $theme = $theme ? $theme : variable_get('theme_default', 'stark');
+  $block_configs = config_get_storage_names_with_prefix('plugin.core.block.' . $theme);
+  foreach ($block_configs as $config) {
+    $blocks[$config] = block_load($config);
   }
   return $blocks;
 }
@@ -721,31 +570,29 @@ function block_list($region) {
   return $blocks[$region];
 }
 
+function block_manager() {
+  return drupal_container()->get('plugin.manager.block');
+}
+
 /**
  * Loads a block object from the database.
  *
- * @param $module
- *   Name of the module that implements the block to load.
- * @param $delta
- *   Unique ID of the block within the context of $module. Pass NULL to return
- *   an empty block object for $module.
+ * @param $plugin_id
+ *   The plugin id to load.
+ * @param $conf
+ *   An optional configuration array for creating a block instance from php
+ *   instead of relying on configuration xml.
  *
  * @return
  *   A block object.
  */
-function block_load($module, $delta) {
-  if (isset($delta)) {
-    $block = db_query('SELECT * FROM {block} WHERE module = :module AND delta = :delta', array(':module' => $module, ':delta' => $delta))->fetchObject();
+function block_load($plugin_id, $conf = array()) {
+  try {
+    $block = block_manager()->getInstance(array('config' => $plugin_id));
   }
-
-  // If the block does not exist in the database yet return a stub block
-  // object.
-  if (empty($block)) {
-    $block = new stdClass();
-    $block->module = $module;
-    $block->delta = $delta;
+  catch (Drupal\Component\Plugin\Exception\PluginException $e) {
+    $block = block_manager()->createInstance($plugin_id, $conf);
   }
-
   return $block;
 }
 
@@ -756,28 +603,14 @@ function block_load($module, $delta) {
  *   An array of blocks grouped by region.
  */
 function _block_load_blocks() {
-  global $theme_key;
-
-  $query = db_select('block', 'b');
-  $query->addField('b', 'title', 'subject');
-  $result = $query
-    ->fields('b')
-    ->condition('b.theme', $theme_key)
-    ->condition('b.status', 1)
-    ->orderBy('b.region')
-    ->orderBy('b.weight')
-    ->orderBy('b.module')
-    ->addTag('block_load')
-    ->addTag('translatable')
-    ->execute();
-
-  $block_info = $result->fetchAllAssoc('bid');
-  // Allow modules to modify the block list.
-  drupal_alter('block_list', $block_info);
-
+  global $theme;
   $blocks = array();
-  foreach ($block_info as $block) {
-    $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block;
+  $instances = config_get_storage_names_with_prefix('plugin.core.block.' . $theme);
+  //$instances = config_get_storage_names_with_prefix('plugin.core.block');
+  foreach ($instances as $plugin_id) {
+    $block = block_manager()->getInstance(array('config' => $plugin_id));
+    $config = $block->getConfig();
+    $blocks[$config['region']]["$plugin_id"] = $block;
   }
   return $blocks;
 }
@@ -904,45 +737,18 @@ function block_block_list_alter(&$blocks) {
  */
 function _block_get_renderable_block($element) {
   $block = $element['#block'];
-
-  // Render the block content if it has not been created already.
-  if (!isset($block->content)) {
-    $array = module_invoke($block->module, 'block_view', $block->delta);
-
-    // Allow modules to modify the block before it is viewed, via either
-    // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
-    drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
-
-    if (empty($array['content'])) {
-      // Blocks without content should emit no markup at all.
-      $element += array(
-        '#access' => FALSE,
-        '#printed' => TRUE,
-      );
-    }
-    elseif (isset($array) && is_array($array)) {
-      foreach ($array as $k => $v) {
-        $block->$k = $v;
+  // Don't bother to build blocks that aren't accessible.
+  if ($element['#access'] = $block->access()) {
+    $build = $block->build();
+    if ($build) {
+      if (isset($build['#title'])) {
+        $element['#title'] = $build['#title'];
       }
+      $element += $build;
     }
-  }
-
-  if (isset($block->content) && $block->content) {
-    // Normalize to the drupal_render() structure.
-    if (is_string($block->content)) {
-      $block->content = array('#markup' => $block->content);
-    }
-    // Override default block title if a custom display title is present.
-    if ($block->title) {
-      // Check plain here to allow module generated titles to keep any
-      // markup.
-      $block->subject = $block->title == '<none>' ? '' : check_plain($block->title);
+    else {
+      $element = array();
     }
-
-    // Add the content renderable array to the main element.
-    $element['content'] = $block->content;
-    unset($block->content);
-    $element['#block'] = $block;
   }
   return $element;
 }
@@ -986,7 +792,10 @@ function block_rebuild() {
  */
 function template_preprocess_block(&$variables) {
   $block_counter = &drupal_static(__FUNCTION__, array());
-  $variables['block'] = $variables['elements']['#block'];
+  $variables['block'] = (object) array_merge($variables['elements']['#block']->getDefinition(), $variables['elements']['#block']->getConfig());
+  if (!empty($variables['elements']['#title']) && empty($variables['block']->subject)) {
+    $variables['block']->subject = $variables['elements']['#title'];
+  }
   // All blocks get an independent counter for each region.
   if (!isset($block_counter[$variables['block']->region])) {
     $block_counter[$variables['block']->region] = 1;
@@ -1013,10 +822,20 @@ function template_preprocess_block(&$variables) {
   // contains a hyphen, it will end up as an underscore after this conversion,
   // and your function names won't be recognized. So, we need to convert
   // hyphens to underscores in block deltas for the theme suggestions.
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . strtr($variables['block']->delta, '-', '_');
-
+  if (!empty($variables['block']->derivative)) {
+    list($plugin, $derivative) = explode(':', $variables['block']->id);
+    $variables['theme_hook_suggestions'][] = 'block__' . strtr($plugin, '-', '_');
+    $variables['theme_hook_suggestions'][] = 'block__' . strtr($plugin, '-', '_') . '__' . strtr($derivative, '-', '_');
+  }
+  else {
+    $plugin = $variables['block']->id;
+    $variables['theme_hook_suggestions'][] = 'block__' . strtr($plugin, '-', '_');
+  }
   // Create a valid HTML ID and make sure it is unique.
-  $variables['block_html_id'] = drupal_html_id('block-' . $variables['block']->module . '-' . $variables['block']->delta);
+  $config_id = explode('.', $variables['block']->config_id);
+  $machine_name = array_pop($config_id);
+  $variables['block_html_id'] = drupal_html_id('block-' . strtr($machine_name, '-', '_'));
+  $variables['theme_hook_suggestions'][] = 'block__' . $machine_name;
 }
 
 /**
diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php
new file mode 100644
index 0000000..382dee0
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockBase.php
@@ -0,0 +1,292 @@
+<?php
+
+namespace Drupal\block;
+
+use Drupal\Component\Plugin\PluginBase;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * @file
+ *   An abstract block implementation to be inherited from to take care of many
+ *   common block setup tasks.
+ */
+abstract class BlockBase extends PluginBase implements BlockInterface {
+  protected $context;
+
+  /**
+   * Implements BlockInterface::getConfig().
+   */
+  public function getConfig() {
+    if (empty($this->configuration)) {
+      $definition = $this->getDefinition();
+      $this->configuration = $definition['settings'];
+    }
+    return $this->configuration;
+  }
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return TRUE;
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $definition = $this->getDefinition();
+    $config = $this->getConfig();
+    $form['id'] = array(
+      '#type' => 'value',
+      '#value' => $definition['id'],
+    );
+    $form['module'] = array(
+      '#type' => 'value',
+      '#value' => $definition['module'],
+    );
+
+    // Get the block subject for the page title.
+    $subject = isset($config['subject']) ? $config['subject'] : '';
+    if ($subject) {
+      drupal_set_title(t("'%subject' block", array('%subject' => $subject)), PASS_THROUGH);
+    }
+
+    $form['settings']['machine_name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Block machine name'),
+      '#maxlength' => 64,
+      '#description' => t('A unique name to save this block configuration. Must be alpha-numeric and be underscore separated.'),
+      '#default_value' => isset($config['config_id']) ? $config['config_id'] : '',
+      '#weight' => -20,
+      '#required' => TRUE,
+    );
+    if (isset($config['config_id'])) {
+      $form['settings']['machine_name']['#disabled'] = TRUE;
+    }
+
+    $form['settings']['title'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Block title'),
+      '#maxlength' => 64,
+      '#description' => $definition['module'] == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use <em>!placeholder</em> to display no title, or leave blank to use the default block title.', array('!placeholder' => '&lt;none&gt;')),
+      '#default_value' => isset($subject) ? $subject : '',
+      '#weight' => -19,
+    );
+
+    // Region settings.
+    $form['regions'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Region settings'),
+      '#collapsible' => FALSE,
+      '#description' => t('Specify in which themes and regions this block is displayed.'),
+    );
+
+    $theme_default = variable_get('theme_default', 'stark');
+    $admin_theme = variable_get('admin_theme');
+    $themes = list_themes();
+    $key = $form['theme']['#value'];
+    $theme = $themes[$key];
+    // Only display enabled themes
+    if ($theme->status) {
+      // Use a meaningful title for the main site theme and administrative
+      // theme.
+      $theme_title = $theme->info['name'];
+      if ($key == $theme_default) {
+        $theme_title = t('!theme (default theme)', array('!theme' => $theme_title));
+      }
+      elseif ($admin_theme && $key == $admin_theme) {
+        $theme_title = t('!theme (administration theme)', array('!theme' => $theme_title));
+      }
+      $form['regions']['region'] = array(
+        '#type' => 'select',
+        '#title' => $theme_title,
+        '#default_value' => !empty($config['region']) && $config['region'] != -1 ? $config['region'] : NULL,
+        '#empty_value' => BLOCK_REGION_NONE,
+        '#options' => system_region_list($key, REGIONS_VISIBLE),
+        '#required' => TRUE,
+      );
+    }
+
+    // Visibility settings.
+    $form['visibility_title'] = array(
+      '#type' => 'item',
+      '#title' => t('Visibility settings'),
+    );
+    $form['visibility'] = array(
+      '#type' => 'vertical_tabs',
+      '#attached' => array(
+        'js' => array(drupal_get_path('module', 'block') . '/block.js'),
+      ),
+    );
+
+    // Per-path visibility.
+    $form['visibility']['path'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Pages'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#group' => 'visibility',
+      '#weight' => 0,
+    );
+
+    //@TODO remove this access check and inject it in some other way.
+    // In fact this entire visibility settings section probably needs a
+    // separate user interface in the near future.
+    $access = user_access('use PHP for settings');
+    if (isset($block->visibility) && $block->visibility == BLOCK_VISIBILITY_PHP && !$access) {
+      $form['visibility']['path']['visibility'] = array(
+        '#type' => 'value',
+        '#value' => BLOCK_VISIBILITY_PHP,
+      );
+      $form['visibility']['path']['pages'] = array(
+        '#type' => 'value',
+        '#value' => isset($block->pages) ? $block->pages : '',
+      );
+    }
+    else {
+      $options = array(
+        BLOCK_VISIBILITY_NOTLISTED => t('All pages except those listed'),
+        BLOCK_VISIBILITY_LISTED => t('Only the listed pages'),
+      );
+      $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %user for the current user's page and %user-wildcard for every user page. %front is the front page.", array('%user' => 'user', '%user-wildcard' => 'user/*', '%front' => '<front>'));
+
+      if (module_exists('php') && $access) {
+        $options += array(BLOCK_VISIBILITY_PHP => t('Pages on which this PHP code returns <code>TRUE</code> (experts only)'));
+        $title = t('Pages or PHP code');
+        $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => '<?php ?>'));
+      }
+      else {
+        $title = t('Pages');
+      }
+      $form['visibility']['path']['visibility'] = array(
+        '#type' => 'radios',
+        '#title' => t('Show block on specific pages'),
+        '#options' => $options,
+        '#default_value' => isset($block->visibility) ? $block->visibility : BLOCK_VISIBILITY_NOTLISTED,
+      );
+      $form['visibility']['path']['pages'] = array(
+        '#type' => 'textarea',
+        '#title' => '<span class="element-invisible">' . $title . '</span>',
+        '#default_value' => isset($block->pages) ? $block->pages : '',
+        '#description' => $description,
+      );
+    }
+
+    // Per-role visibility.
+    $default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module", array(
+      ':module' => $definition['module'],
+    ))->fetchCol();
+    $role_options = array_map('check_plain', user_roles());
+    $form['visibility']['role'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Roles'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#group' => 'visibility',
+      '#weight' => 10,
+    );
+    $form['visibility']['role']['roles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Show block for specific roles'),
+      '#default_value' => $default_role_options,
+      '#options' => $role_options,
+      '#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'),
+    );
+
+    // Per-user visibility.
+    $form['visibility']['user'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Users'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#group' => 'visibility',
+      '#weight' => 20,
+    );
+    $form['visibility']['user']['custom'] = array(
+      '#type' => 'radios',
+      '#title' => t('Customizable per user'),
+      '#options' => array(
+        BLOCK_CUSTOM_FIXED => t('Not customizable'),
+        BLOCK_CUSTOM_ENABLED => t('Customizable, visible by default'),
+        BLOCK_CUSTOM_DISABLED => t('Customizable, hidden by default'),
+      ),
+      '#description' => t('Allow individual users to customize the visibility of this block in their account settings.'),
+      '#default_value' => isset($block->custom) ? $block->custom : BLOCK_CUSTOM_FIXED,
+    );
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save block'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureValidate().
+   */
+  public function configureValidate($form, &$form_state) {
+    if (empty($form['settings']['machine_name']['#disabled'])) {
+      if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['machine_name'])) {
+        form_set_error('machine_name', t('Block name must be alphanumeric or underscores only.'));
+      }
+      if (in_array('plugin.core.block.' . $form_state['values']['machine_name'], config_get_storage_names_with_prefix('plugin.core.block'))) {
+        form_set_error('machine_name', t('Block name must be unique.'));
+      }
+    }
+    else {
+      $config_id = explode('.', $form_state['values']['machine_name']);
+      $form_state['values']['machine_name'] = array_pop($config_id);
+    }
+    if ($form_state['values']['module'] == 'block') {
+      $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array(
+        ':bid' => $form_state['values']['delta'],
+        ':info' => $form_state['values']['info'],
+      ))->fetchField();
+      if (empty($form_state['values']['info']) || $custom_block_exists) {
+        form_set_error('info', t('Ensure that each block description is unique.'));
+      }
+    }
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    if (!form_get_errors()) {
+      $transaction = db_transaction();
+      try {
+        $keys = array(
+          'visibility' => 'visibility',
+          'pages' => 'pages',
+          'custom' => 'custom',
+          'title' => 'subject',
+          'module' => 'module',
+          'region' => 'region',
+        );
+        foreach ($keys as $key => $new_key) {
+          if (isset($form_state['values'][$key])) {
+            $this->configuration[$new_key] = $form_state['values'][$key];
+          }
+        }
+      }
+      catch (Exception $e) {
+        $transaction->rollback();
+        watchdog_exception('block', $e);
+        throw $e;
+      }
+      if (empty($this->configuration['weight'])) {
+        $this->configuration['weight'] = 0;
+      }
+      drupal_set_message(t('The block configuration has been saved.'));
+      drupal_flush_all_caches();
+    }
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {}
+}
diff --git a/core/modules/block/lib/Drupal/block/BlockBundle.php b/core/modules/block/lib/Drupal/block/BlockBundle.php
new file mode 100644
index 0000000..a66ac60
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockBundle.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\block\BlockBundle.
+ */
+
+namespace Drupal\Block;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Block dependency injection container.
+ */
+class BlockBundle extends Bundle {
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle.
+   */
+  public function build(ContainerBuilder $container) {
+    // register the BlockManager class with the dependency injection container.
+    $container->register('plugin.manager.block', 'Drupal\block\Plugins\Type\BlockManager');
+  }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/BlockInterface.php b/core/modules/block/lib/Drupal/block/BlockInterface.php
new file mode 100644
index 0000000..1c0e827
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockInterface.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\block;
+
+use Drupal\Plugin\Access\AccessInterface;
+
+/**
+ * Interface definition for Block plugins.
+ */
+interface BlockInterface {
+
+  /**
+   * The default settings for the block.
+   *
+   * @return array().
+   */
+  //public function settings();
+
+  /**
+   * A simple access method. This should be replaced with access plugins before
+   * Drupal 8 is released.
+   *
+   * @return bool.
+   */
+  public function access();
+
+  /**
+   * The configuration form for the block.
+   *
+   * @return form array().
+   */
+  public function configure($form, &$form_state);
+
+  /**
+   * The validation for the configuration form.
+   */
+  public function configureValidate($form, &$form_state);
+
+  /**
+   * The submission for the configuration form.
+   */
+  public function configureSubmit($form, &$form_state);
+
+  /**
+   * A function for building renderable block arrays. A block theme wrapper
+   * will be wrapped around this by the caller.
+   *
+   * @return renderable array().
+   */
+  public function build();
+}
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php
new file mode 100644
index 0000000..13db3ac
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugin/Derivative/BlockPluginUI.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\block\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class BlockPluginUI implements DerivativeInterface {
+  protected $derivatives = array();
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    foreach (list_themes() as $key => $theme) {
+      $this->derivatives[$key] = $base_plugin_definition;
+    }
+    return $this->derivatives;
+  }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php
new file mode 100644
index 0000000..7118e0f
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Drupal\block\Plugin\system\plugin_ui;
+
+use Drupal\system\Plugin\PluginUIBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "block_plugin_ui",
+ *   title = @Translation("Block Library"),
+ *   title_attribute = "subject",
+ *   menu = TRUE,
+ *   all_plugins = @Translation("All Blocks"),
+ *   link_title = @Translation("Configure Block"),
+ *   config_path = "admin/structure/block/manage",
+ *   path = "admin/structure/block/list",
+ *   suffix = "add",
+ *   type = MENU_LOCAL_ACTION,
+ *   manager = "Drupal\block\Plugins\Type\BlockManager",
+ *   derivative = "Drupal\block\Plugin\Derivative\BlockPluginUI",
+ *   facets = {
+ *     "module" = @Translation("Modules")
+ *   }
+ * )
+ */
+class BlockPluginUI extends PluginUIBase {
+  public function form($form, &$form_state, $plugin, $facet) {
+    list($plugin_id, $theme) = explode(':', $plugin);
+    $form['theme'] = array(
+      '#type' => 'value',
+      '#value' => $theme,
+    );
+    return parent::form($form, $form_state, $plugin, $facet);
+  }
+
+  public function formSubmit($form, &$form_state) {
+    $form_state['redirect'] = 'admin/structure/block/manage/' . $form_state['values']['block'] . '/' . $form_state['values']['theme'];
+  }
+
+  public function access($plugin_id) {
+    list($plugin, $theme) = explode(':', $plugin_id);
+    return _block_themes_access($theme);
+  }
+
+  public function tableHeader() {
+    return array(t('Subject'), t('Operations'));
+  }
+
+  public function row($plugin, $plugin_definition, $plugin_id, $config) {
+    list($plugin, $theme) = explode(':', $plugin);
+    return array($config['subject'], l($plugin_definition['link_title'], $plugin_definition['config_path'] . '/' . $plugin_id . '/' . $theme));
+  }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Plugins/ConfigMapper.php b/core/modules/block/lib/Drupal/block/Plugins/ConfigMapper.php
new file mode 100644
index 0000000..126d12e
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugins/ConfigMapper.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\block\Plugins;
+
+use Drupal\Component\Plugin\Mapper\MapperInterface;
+
+class ConfigMapper implements MapperInterface {
+  protected $manager;
+
+  public function __construct($manager) {
+    $this->manager = $manager;
+  }
+
+  /**
+   * Implements MapperInterface::getInstance().
+   */
+  public function getInstance(array $options) {
+    $config = config($options['config']);
+    if ($config) {
+      $plugin_id = $config->get('id');
+      $settings = $config->get();
+      $settings['config_id'] = $options['config'];
+      return $this->manager->createInstance($plugin_id, $settings);
+    }
+    // @Todo throw an exception.
+  }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Plugins/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugins/Type/BlockManager.php
new file mode 100644
index 0000000..c852fda
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Plugins/Type/BlockManager.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Drupal\block\Plugins\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\block\Plugins\ConfigMapper;
+
+class BlockManager extends PluginManagerBase {
+  public function __construct() {
+    $this->discovery = new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('block', 'block'));
+    $this->factory = new DefaultFactory($this);
+    $this->mapper = new ConfigMapper($this);
+  }
+}
\ No newline at end of file
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
index 90327f9..2ebf579 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
@@ -49,8 +49,13 @@ function setUp() {
     $this->normal_user_alt->save();
 
     // Enable our test block.
-    $edit['blocks[block_test_test_cache][region]'] = 'sidebar_first';
-    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+    $block = array();
+    $block['id'] = 'test_cache';
+    $block['machine_name'] = $this->randomName(8);
+    $block['theme'] = variable_get('theme_default', 'stark');
+    $block['region'] = 'sidebar_first';
+    $this->block = $block;
+    $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
   }
 
   /**
@@ -192,14 +197,17 @@ function testCachePerPage() {
    * Private helper method to set the test block's cache mode.
    */
   private function setCacheMode($cache_mode) {
-    db_update('block')
-      ->fields(array('cache' => $cache_mode))
-      ->condition('module', 'block_test')
-      ->execute();
-
-    $current_mode = db_query("SELECT cache FROM {block} WHERE module = 'block_test'")->fetchField();
-    if ($current_mode != $cache_mode) {
-      $this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $current_mode)));
+    $block = $this->block;
+    $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+    $block_config = config($block['config_id']);
+    $block_config->set('cache', $cache_mode);
+    $block_config->save();
+
+    $instance = block_load($block['config_id']);
+    $config = $instance->getConfig();
+    if ($config['cache'] != $cache_mode) {
+      $this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $config['cache'])));
     }
+    $this->assertEqual($config['cache'], $cache_mode, t("Test block's database entry updated to DRUPAL_NO_CACHE."));
   }
 }
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
index fd28702..c80a881 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockHtmlIdTest.php
@@ -37,12 +37,19 @@ function setUp() {
     $this->drupalLogin($this->admin_user);
 
     // Enable our test block.
-    $edit['blocks[block_test_test_html_id][region]'] = 'sidebar_first';
-    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+    $block = array();
+    $block['id'] = 'test_html_id';
+    $block['machine_name'] = $this->randomName(8);
+    $block['theme'] = variable_get('theme_default', 'stark');
+    $block['region'] = 'sidebar_first';
+    $this->block = $block;
+    $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
 
     // Make sure the block has some content so it will appear
     $current_content = $this->randomName();
     variable_set('block_test_content', $current_content);
+    // TODO: This block is being cached. Should it be?
+    $this->resetAll();
   }
 
   /**
@@ -50,6 +57,6 @@ function setUp() {
    */
   function testHtmlId() {
     $this->drupalGet('');
-    $this->assertRaw('block-block-test-test-html-id', t('HTML id for test block is valid.'));
+    $this->assertRaw('block-' . strtr(strtolower($this->block['machine_name']), '-', '_'), t('HTML id for test block is valid.'));
   }
 }
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
index f98da2b..2802b0b 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
@@ -320,22 +320,24 @@ function testBlockVisibilityPerUser() {
    * Test configuring and moving a module-define block to specific regions.
    */
   function testBlock() {
-    // Select the Navigation block to be configured and moved.
+    // Select the 'Powered by Drupal' block to be configured and moved.
     $block = array();
-    $block['module'] = 'system';
-    $block['delta'] = 'management';
+    $block['id'] = 'system_powered_by_block';
     $block['title'] = $this->randomName(8);
+    $block['machine_name'] = $this->randomName(8);
+    $block['theme'] = variable_get('theme_default', 'stark');
+    $block['region'] = 'header';
 
     // Set block title to confirm that interface works and override any custom titles.
-    $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => $block['title']), t('Save block'));
+    $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('title' => $block['title'], 'machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
     $this->assertText(t('The block configuration has been saved.'), t('Block title set.'));
-    $bid = db_query("SELECT bid FROM {block} WHERE module = :module AND delta = :delta", array(
-      ':module' => $block['module'],
-      ':delta' => $block['delta'],
-    ))->fetchField();
 
-    // Check to see if the block was created by checking that it's in the database.
-    $this->assertNotNull($bid, t('Block found in database'));
+    // Check to see if the block was created by checking its configuration.
+    $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+    $instance = block_load($block['config_id']);
+    $config = $instance->getConfig();
+
+    $this->assertEqual($config['subject'], $block['title'], t('Stored block title found.'));
 
     // Check whether the block can be moved to all available regions.
     foreach ($this->regions as $region) {
@@ -344,31 +346,23 @@ function testBlock() {
 
     // Set the block to the disabled region.
     $edit = array();
-    $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = '-1';
+    $edit['blocks[0][region]'] = -1;
     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
 
     // Confirm that the block was moved to the proper region.
     $this->assertText(t('The block settings have been updated.'), t('Block successfully move to disabled region.'));
+    $this->drupalGet('node');
     $this->assertNoText(t($block['title']), t('Block no longer appears on page.'));
 
     // Confirm that the region's xpath is not available.
-    $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-block-' . $bid));
-    $this->assertNoFieldByXPath($xpath, FALSE, t('Custom block found in no regions.'));
-
-    // For convenience of developers, put the navigation block back.
-    $edit = array();
-    $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $this->regions[1];
-    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
-    $this->assertText(t('The block settings have been updated.'), t('Block successfully move to first sidebar region.'));
-
-    $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => 'Navigation'), t('Save block'));
-    $this->assertText(t('The block configuration has been saved.'), t('Block title set.'));
+    $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-' . strtr(strtolower($block['machine_name']), '-', '_')));
+    $this->assertNoFieldByXPath($xpath, FALSE, t('Block found in no regions.'));
   }
 
   function moveBlockToRegion($block, $region) {
     // Set the created block to a specific region.
     $edit = array();
-    $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
+    $edit['blocks[0][region]'] = $region;
     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
 
     // Confirm that the block was moved to the proper region.
@@ -380,10 +374,10 @@ function moveBlockToRegion($block, $region) {
 
     // Confirm that the custom block was found at the proper region.
     $xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
-      ':region-class' => 'region region-' . str_replace('_', '-', $region),
-      ':block-id' => 'block-' . $block['module'] . '-' . $block['delta'],
+      ':region-class' => 'region region-' . drupal_html_class($region),
+      ':block-id' => 'block-' . strtr(strtolower($block['machine_name']), '-', '_'),
     ));
-    $this->assertFieldByXPath($xpath, NULL, t('Custom block found in %region_name region.', array('%region_name' => $region)));
+    $this->assertFieldByXPath($xpath, NULL, t('Block found in %region_name region.', array('%region_name' => drupal_html_class($region))));
   }
 
   /**
@@ -393,19 +387,31 @@ function testBlockRehash() {
     module_enable(array('block_test'));
     $this->assertTrue(module_exists('block_test'), t('Test block module enabled.'));
 
-    // Our new block should be inserted in the database when we visit the
-    // block management page.
-    $this->drupalGet('admin/structure/block');
+    // Add a test block.
+    $plugin = block_manager()->getDefinition('test_cache');
+
+    $block = array();
+    $block['id'] = 'test_cache';
+    $block['machine_name'] = $this->randomName(8);
+    $block['theme'] = variable_get('theme_default', 'stark');
+    $block['region'] = 'header';
+    $this->drupalPost('admin/structure/block/manage/' . $block['id'] . '/' . $block['theme'], array('machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
+
     // Our test block's caching should default to DRUPAL_CACHE_PER_ROLE.
-    $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
-    $this->assertEqual($current_caching, DRUPAL_CACHE_PER_ROLE, t('Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.'));
+    $block['config_id'] = 'plugin.core.block.' . $block['theme'] . '.' . $block['machine_name'];
+    $instance = block_load($block['config_id']);
+    $config = $instance->getConfig();
+    $this->assertEqual($config['cache'], DRUPAL_CACHE_PER_ROLE, t('Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.'));
 
     // Disable caching for this block.
-    variable_set('block_test_caching', DRUPAL_NO_CACHE);
+    $block_config = config($block['config_id']);
+    $block_config->set('cache', DRUPAL_NO_CACHE);
+    $block_config->save();
     // Flushing all caches should call _block_rehash().
     $this->resetAll();
-    // Verify that the database is updated with the new caching mode.
-    $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
-    $this->assertEqual($current_caching, DRUPAL_NO_CACHE, t("Test block's database entry updated to DRUPAL_NO_CACHE."));
+    // Verify that block is updated with the new caching mode.
+    $instance = block_load($block['config_id']);
+    $config = $instance->getConfig();
+    $this->assertEqual($config['cache'], DRUPAL_NO_CACHE, t("Test block's database entry updated to DRUPAL_NO_CACHE."));
   }
 }
diff --git a/core/modules/block/tests/block_test.module b/core/modules/block/tests/block_test.module
index 5e06d5c..03f94b0 100644
--- a/core/modules/block/tests/block_test.module
+++ b/core/modules/block/tests/block_test.module
@@ -12,25 +12,3 @@ function block_test_system_theme_info() {
   $themes['block_test_theme'] = drupal_get_path('module', 'block_test') . '/themes/block_test_theme/block_test_theme.info';
   return $themes;
 }
-
-/**
- * Implements hook_block_info().
- */
-function block_test_block_info() {
-  $blocks['test_cache'] = array(
-    'info' => t('Test block caching'),
-    'cache' => variable_get('block_test_caching', DRUPAL_CACHE_PER_ROLE),
-  );
-
-  $blocks['test_html_id'] = array(
-    'info' => t('Test block html id'),
-  );
-  return $blocks;
-}
-
-/**
- * Implements hook_block_view().
- */
-function block_test_block_view($delta = 0) {
-  return array('content' => variable_get('block_test_content', ''));
-}
diff --git a/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestCacheBlock.php b/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestCacheBlock.php
new file mode 100644
index 0000000..5e77037
--- /dev/null
+++ b/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestCacheBlock.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\block_test\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Annotation\Evaluate;
+
+/**
+ * @Plugin(
+ *   id = "test_cache",
+ *   subject = @Translation("Test block caching"),
+ *   module = "block_test",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_CACHE_PER_ROLE
+ *   }
+ * )
+ */
+class TestCacheBlock extends BlockBase {
+  public function build() {
+    return array('#children' => variable_get('block_test_content', ''));
+  }
+}
diff --git a/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestHtmlIdBlock.php b/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestHtmlIdBlock.php
new file mode 100644
index 0000000..0c738d8
--- /dev/null
+++ b/core/modules/block/tests/lib/Drupal/block_test/Plugin/block/block/TestHtmlIdBlock.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\block_test\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "test_html_id",
+ *   subject = @Translation("Test block html id"),
+ *   module = "block_test",
+ *   settings = {
+ *     "status" = TRUE,
+ *   }
+ * )
+ */
+class TestHtmlIdBlock extends TestCacheBlock {}
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index fd4fac9..cb76abc 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -262,103 +262,6 @@ function book_entity_info_alter(&$info) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function book_block_info() {
-  $block = array();
-  $block['navigation']['info'] = t('Book navigation');
-  $block['navigation']['cache'] = DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;
-
-  return $block;
-}
-
-/**
- * Implements hook_block_view().
- *
- * Displays the book table of contents in a block when the current page is a
- * single-node view of a book node.
- */
-function book_block_view($delta = '') {
-  $block = array();
-  $current_bid = 0;
-  if ($node = menu_get_object()) {
-    $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
-  }
-
-  if (config('book.settings')->get('block.navigation.mode') == 'all pages') {
-    $block['subject'] = t('Book navigation');
-    $book_menus = array();
-    $pseudo_tree = array(0 => array('below' => FALSE));
-    foreach (book_get_books() as $book_id => $book) {
-      if ($book['bid'] == $current_bid) {
-        // If the current page is a node associated with a book, the menu
-        // needs to be retrieved.
-        $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book));
-      }
-      else {
-        // Since we know we will only display a link to the top node, there
-        // is no reason to run an additional menu tree query for each book.
-        $book['in_active_trail'] = FALSE;
-        // Check whether user can access the book link.
-        $book_node = node_load($book['nid']);
-        $book['access'] = node_access('view', $book_node);
-        $pseudo_tree[0]['link'] = $book;
-        $book_menus[$book_id] = menu_tree_output($pseudo_tree);
-      }
-    }
-    if ($block['content'] = $book_menus) {
-      $book_menus['#theme'] = 'book_all_books_block';
-    }
-  }
-  elseif ($current_bid) {
-    // Only display this block when the user is browsing a book.
-  $select = db_select('node', 'n')
-    ->fields('n', array('title'))
-    ->condition('n.nid', $node->book['bid'])
-    ->addTag('node_access');
-    $title = $select->execute()->fetchField();
-    // Only show the block if the user has view access for the top-level node.
-    if ($title) {
-      $tree = menu_tree_all_data($node->book['menu_name'], $node->book);
-      // There should only be one element at the top level.
-      $data = array_shift($tree);
-      $block['subject'] = theme('book_title_link', array('link' => $data['link']));
-      $block['content'] = ($data['below']) ? menu_tree_output($data['below']) : '';
-    }
-  }
-
-  return $block;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function book_block_configure($delta = '') {
-  $block = array();
-  $options = array(
-    'all pages' => t('Show block on all pages'),
-    'book pages' => t('Show block only on book pages'),
-  );
-  $form['book_block_mode'] = array(
-    '#type' => 'radios',
-    '#title' => t('Book navigation block display'),
-    '#options' => $options,
-    '#default_value' => config('book.settings')->get('block.navigation.mode'),
-    '#description' => t("If <em>Show block on all pages</em> is selected, the block will contain the automatically generated menus for all of the site's books. If <em>Show block only on book pages</em> is selected, the block will contain only the one menu corresponding to the current page's book. In this case, if the current page is not in a book, no block will be displayed. The <em>Page specific visibility settings</em> or other visibility settings can be used in addition to selectively display this block."),
-    );
-
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function book_block_save($delta = '', $edit = array()) {
-  $block = array();
-  config('book.settings')->set('block.navigation.mode', $edit['book_block_mode'])->save();
-}
-
-/**
  * Returns HTML for a link to a book title when used as a block title.
  *
  * @param $variables
diff --git a/core/modules/book/lib/Drupal/book/Plugin/block/block/BookNavigationBlock.php b/core/modules/book/lib/Drupal/book/Plugin/block/block/BookNavigationBlock.php
new file mode 100644
index 0000000..9e7360d
--- /dev/null
+++ b/core/modules/book/lib/Drupal/book/Plugin/block/block/BookNavigationBlock.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\book\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Annotation\Evaluate;
+
+/**
+ * @Plugin(
+ *   id = "book_navigation",
+ *   subject = @Translation("Book navigation"),
+ *   module = "book",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = @Evaluate("return DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;"),
+ *     "block_mode" = "all pages"
+ *   }
+ * )
+ */
+class BookNavigationBlock extends BlockBase {
+
+  function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $options = array(
+      'all pages' => t('Show block on all pages'),
+      'book pages' => t('Show block only on book pages'),
+    );
+    $form['book_block_mode'] = array(
+      '#type' => 'radios',
+      '#title' => t('Book navigation block display'),
+      '#options' => $options,
+      '#default_value' => $this->configuration['block_mode'],
+      '#description' => t("If <em>Show block on all pages</em> is selected, the block will contain the automatically generated menus for all of the site's books. If <em>Show block only on book pages</em> is selected, the block will contain only the one menu corresponding to the current page's book. In this case, if the current page is not in a book, no block will be displayed. The <em>Page specific visibility settings</em> or other visibility settings can be used in addition to selectively display this block."),
+      );
+
+    return $form;
+  }
+
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['block_mode'] = $form_state['values']['book_block_mode'];
+  }
+
+  public function build() {
+    $block = array();
+    $current_bid = 0;
+    if ($node = menu_get_object()) {
+      $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
+    }
+
+    if ($this->configuration['block_mode'] == 'all pages') {
+      $book_menus = array();
+      $pseudo_tree = array(0 => array('below' => FALSE));
+      foreach (book_get_books() as $book_id => $book) {
+        if ($book['bid'] == $current_bid) {
+          // If the current page is a node associated with a book, the menu
+          // needs to be retrieved.
+          $book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book));
+        }
+        else {
+          // Since we know we will only display a link to the top node, there
+          // is no reason to run an additional menu tree query for each book.
+          $book['in_active_trail'] = FALSE;
+          // Check whether user can access the book link.
+          $book_node = node_load($book['nid']);
+          $book['access'] = node_access('view', $book_node);
+          $pseudo_tree[0]['link'] = $book;
+          $book_menus[$book_id] = menu_tree_output($pseudo_tree);
+        }
+      }
+      if ($book_menus) {
+        return array(
+          '#theme' => 'book_all_books_block',
+          $book_menus
+        );
+      }
+    }
+    elseif ($current_bid) {
+      // Only display this block when the user is browsing a book.
+      $select = db_select('node', 'n')
+        ->fields('n', array('title'))
+        ->condition('n.nid', $node->book['bid'])
+        ->addTag('node_access');
+      $title = $select->execute()->fetchField();
+      // Only show the block if the user has view access for the top-level node.
+      if ($title) {
+        $tree = menu_tree_all_data($node->book['menu_name'], $node->book);
+        // There should only be one element at the top level.
+        $data = array_shift($tree);
+        return array(
+          '#title' => theme('book_title_link', array('link' => $data['link'])),
+          ($data['below']) ? menu_tree_output($data['below']) : '',
+        );
+      }
+    }
+    return array();
+  }
+}
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index bd9c107..dc9ab47 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -216,7 +216,7 @@ function comment_field_extra_fields() {
 function comment_theme() {
   return array(
     'comment_block' => array(
-      'variables' => array(),
+      'variables' => array('number' => NULL),
     ),
     'comment_preview' => array(
       'variables' => array('comment' => NULL),
@@ -442,51 +442,6 @@ function comment_permission() {
 }
 
 /**
- * Implements hook_block_info().
- */
-function comment_block_info() {
-  $blocks['recent']['info'] = t('Recent comments');
-  $blocks['recent']['properties']['administrative'] = TRUE;
-
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function comment_block_configure($delta = '') {
-  $form['comment_block_count'] = array(
-    '#type' => 'select',
-    '#title' => t('Number of recent comments'),
-    '#default_value' => variable_get('comment_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)),
-  );
-
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function comment_block_save($delta = '', $edit = array()) {
-  variable_set('comment_block_count', (int) $edit['comment_block_count']);
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates a block with the most recent comments.
- */
-function comment_block_view($delta = '') {
-  if (user_access('access comments')) {
-    $block['subject'] = t('Recent comments');
-    $block['content'] = theme('comment_block');
-
-    return $block;
-  }
-}
-
-/**
  * Redirects comment links to the correct page depending on comment settings.
  *
  * Since comments are paged there is no way to guarantee which page a comment
@@ -623,9 +578,9 @@ function comment_new_page_count($num_comments, $new_replies, Node $node) {
  *
  * @ingroup themeable
  */
-function theme_comment_block() {
+function theme_comment_block($variables) {
   $items = array();
-  $number = variable_get('comment_block_count', 10);
+  $number = $variables['number'];
   foreach (comment_get_recent($number) as $comment) {
     $items[] = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)) . '&nbsp;<span>' . t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->changed))) . '</span>';
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/block/block/RecentBlock.php b/core/modules/comment/lib/Drupal/comment/Plugin/block/block/RecentBlock.php
new file mode 100644
index 0000000..c98b8e4
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/block/block/RecentBlock.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Drupal\comment\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *  id = "recent_comments",
+ *  subject = @Translation("Recent comments"),
+ *  module = "comment",
+ *  settings = {
+ *    "status" = TRUE,
+ *    "block_count" = 10
+ *  }
+ * )
+ */
+class RecentBlock extends BlockBase {
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    return user_access('access comments');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of recent comments'),
+      '#default_value' => $this->configuration['block_count'],
+      '#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)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build();
+   */
+  public function build() {
+    return array(
+      '#theme' => 'comment_block',
+      '#number' => $this->configuration['block_count'],
+    );
+  }
+}
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 61b018a..de7eb83 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -640,81 +640,7 @@ function forum_form_node_form_alter(&$form, &$form_state, $form_id) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function forum_block_info() {
-  $blocks['active'] = array(
-    'info' => t('Active forum topics'),
-    'cache' => DRUPAL_CACHE_CUSTOM,
-    'properties' => array('administrative' => TRUE),
-  );
-  $blocks['new'] = array(
-    'info' => t('New forum topics'),
-    'cache' => DRUPAL_CACHE_CUSTOM,
-    'properties' => array('administrative' => TRUE),
-  );
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function forum_block_configure($delta = '') {
-  $form['block_' . $delta . '_limit'] = array(
-    '#type' => 'select',
-    '#title' => t('Number of topics'),
-    '#default_value' => config('forum.settings')->get('block.' . $delta . '.limit'),
-    '#options' => drupal_map_assoc(range(2, 20)),
-  );
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function forum_block_save($delta = '', $edit = array()) {
-  config('forum.settings')->set('block.' . $delta . '.limit', $edit['block_' . $delta . '_limit'])->save();
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates a block containing the currently active forum topics and the most
- * recently added forum topics.
- */
-function forum_block_view($delta = '') {
-  $config = config('forum.settings');
-  $query = db_select('forum_index', 'f')
-    ->fields('f')
-    ->addTag('node_access')
-    ->addMetaData('base_table', 'forum_index');
-  switch ($delta) {
-    case 'active':
-      $title = t('Active forum topics');
-      $query
-        ->orderBy('f.last_comment_timestamp', 'DESC')
-        ->range(0, $config->get('block.active.limit'));
-      break;
-
-    case 'new':
-      $title = t('New forum topics');
-      $query
-        ->orderBy('f.created', 'DESC')
-        ->range(0, $config->get('block.new.limit'));
-      break;
-  }
-
-  $block['subject'] = $title;
-  // Cache based on the altered query. Enables us to cache with node access enabled.
-  $block['content'] = drupal_render_cache_by_query($query, 'forum_block_view');
-  $block['content']['#access'] = user_access('access content');
-  return $block;
-}
-
-/**
- * Render API callback: Lists nodes based on the element's #query property.
- *
- * This function can be used as a #pre_render callback.
+ * A #pre_render callback. Lists nodes based on the element's #query property.
  *
  * @see forum_block_view()
  */
diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/block/block/ActiveBlock.php b/core/modules/forum/lib/Drupal/forum/Plugin/block/block/ActiveBlock.php
new file mode 100644
index 0000000..6d61cba
--- /dev/null
+++ b/core/modules/forum/lib/Drupal/forum/Plugin/block/block/ActiveBlock.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\forum\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "forum_active_block",
+ *   subject = @Translation("Active forum topics"),
+ *   module = "forum",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_CACHE_CUSTOM,
+ *     "properties" = {
+ *       "administrative" = TRUE
+ *     },
+ *     "block_count" = 5
+ *   }
+ * )
+ */
+class ActiveBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of topics'),
+      '#default_value' => $this->configuration['block_count'],
+      '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $query = db_select('forum_index', 'f')
+      ->fields('f')
+      ->addTag('node_access')
+      ->orderBy('f.last_comment_timestamp', 'DESC')
+      ->range(0, $this->configuration['block_count']);
+
+    return array(
+      drupal_render_cache_by_query($query, 'forum_block_view'),
+    );
+  }
+}
diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/block/block/NewBlock.php b/core/modules/forum/lib/Drupal/forum/Plugin/block/block/NewBlock.php
new file mode 100644
index 0000000..45222b2
--- /dev/null
+++ b/core/modules/forum/lib/Drupal/forum/Plugin/block/block/NewBlock.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\forum\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "forum_active_block",
+ *   subject = @Translation("New forum topics"),
+ *   module = "forum",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_CACHE_CUSTOM,
+ *     "properties" = {
+ *       "administrative" = TRUE
+ *     },
+ *     "block_count" = 5
+ *   }
+ * )
+ */
+class NewBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of topics'),
+      '#default_value' => $this->configuration['block_count'],
+      '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $query = db_select('forum_index', 'f')
+      ->fields('f')
+      ->addTag('node_access')
+      ->orderBy('f.created', 'DESC')
+      ->range(0, $this->configuration['block_count']);
+
+    return array(
+      drupal_render_cache_by_query($query, 'forum_block_view'),
+    );
+  }
+}
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index a74bf52..aac8636 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -71,7 +71,7 @@ function help_help($path, $arg) {
  * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
 function help_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'system' && $variables['block']->delta == 'help') {
+  if ($variables['block']->id == 'system_help_block') {
     $variables['attributes']['role'] = 'complementary';
   }
 }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 5b62382..e9ec0c3 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -509,45 +509,6 @@ function language_language_delete($language) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function language_block_info() {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-  $block = array();
-  $info = language_types_info();
-  foreach (language_types_get_configurable(FALSE) as $type) {
-    $block[$type] = array(
-      // We do not need to escape the language type name since the block 'info'
-      // value is supposed not to be sanitized. It is escaped later, if needed.
-      'info' => t('Language switcher (!type)', array('!type' => $info[$type]['name'])),
-      // Not worth caching.
-      'cache' => DRUPAL_NO_CACHE,
-    );
-  }
-  return $block;
-}
-
-/**
- * Implements hook_block_view().
- *
- * Displays a language switcher. Only show if we have at least two languages.
- */
-function language_block_view($type) {
-  if (language_multilingual()) {
-    $path = drupal_is_front_page() ? '<front>' : current_path();
-    $links = language_negotiation_get_switch_links($type, $path);
-
-    if (isset($links->links)) {
-      $class = "language-switcher-{$links->method_id}";
-      $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
-      $block['content'] = theme('links__language_block', $variables);
-      $block['subject'] = t('Languages');
-      return $block;
-    }
-  }
-}
-
-/**
  * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
 function language_preprocess_block(&$variables) {
diff --git a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php
new file mode 100644
index 0000000..e2e64cd
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\language\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class LanguageBlock implements DerivativeInterface {
+  protected $derivatives;
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    include_once DRUPAL_ROOT . '/core/includes/language.inc';
+    $info = language_types_info();
+    foreach (language_types_get_configurable(FALSE) as $type) {
+      $this->derivatives[$type] = $base_plugin_definition;
+      $this->derivatives[$type]['subject'] = t('Language switcher (!type)', array('!type' => $info[$type]['name']));
+      $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE;
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php
new file mode 100644
index 0000000..a935b05
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\language\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "language_block",
+ *   subject = @Translation("Language switcher"),
+ *   module = "language",
+ *   derivative = "Drupal\language\Plugin\Derivative\LanguageBlock",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class LanguageBlock extends BlockBase {
+  function access() {
+    return language_multilingual();
+  }
+
+  function build() {
+    $path = drupal_is_front_page() ? '<front>' : current_path();
+    list($plugin_id, $type) = explode(':', $this->getPluginId());
+    $links = language_negotiation_get_switch_links($type, $path);
+
+    if (isset($links->links)) {
+      $class = "language-switcher-{$links->method_id}";
+      $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
+      return array(
+        '#children' => theme('links__language_block', $variables)
+      );
+    }
+  }
+}
diff --git a/core/modules/menu/lib/Drupal/menu/Plugin/Derivative/MenuBlock.php b/core/modules/menu/lib/Drupal/menu/Plugin/Derivative/MenuBlock.php
new file mode 100644
index 0000000..6d0bf79
--- /dev/null
+++ b/core/modules/menu/lib/Drupal/menu/Plugin/Derivative/MenuBlock.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\menu\Plugin\Derivative;
+
+use Drupal\system\Plugin\Derivative\SystemMenuBlock;
+
+class MenuBlock extends SystemMenuBlock {
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    foreach (menu_get_menus(FALSE) as $menu => $name) {
+      $this->derivatives[$menu] = $base_plugin_definition;
+      $this->derivatives[$menu]['delta'] = $menu;
+      $this->derivatives[$menu]['subject'] = t('Menu: @menu', array('@menu' => $name));
+      $this->derivatives[$menu]['cache'] = DRUPAL_NO_CACHE;
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/menu/lib/Drupal/menu/Plugin/block/block/MenuBlock.php b/core/modules/menu/lib/Drupal/menu/Plugin/block/block/MenuBlock.php
new file mode 100644
index 0000000..49a8216
--- /dev/null
+++ b/core/modules/menu/lib/Drupal/menu/Plugin/block/block/MenuBlock.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\menu\Plugin\block\block;
+
+use Drupal\system\Plugin\block\block\SystemMenuBlock;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "menu_menu_block",
+ *   subject = @Translation("Menu"),
+ *   module = "menu",
+ *   derivative = "Drupal\menu\Plugin\Derivative\MenuBlock",
+ *   settings = {
+ *     "status" = "TRUE"
+ *   }
+ * )
+ */
+class MenuBlock extends SystemMenuBlock {
+}
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index cee4ba5..f8f4e51 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -467,36 +467,6 @@ function menu_reset_item($link) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function menu_block_info() {
-  $menus = menu_get_menus(FALSE);
-
-  $blocks = array();
-  foreach ($menus as $name => $title) {
-    $blocks[$name]['info'] = check_plain($title);
-    // Menu blocks can't be cached because each menu item can have
-    // a custom access callback. menu.inc manages its own caching.
-    $blocks[$name]['cache'] = DRUPAL_NO_CACHE;
-  }
-  return $blocks;
-}
-
-/**
- * Implements hook_block_view().
- */
-function menu_block_view($delta = '') {
-  $menus = menu_get_menus(FALSE);
-  $data['subject'] = check_plain($menus[$delta]);
-  $data['content'] = menu_tree($delta);
-  // Add contextual links for this block.
-  if (!empty($data['content'])) {
-    $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta));
-  }
-  return $data;
-}
-
-/**
  * Implements hook_block_view_alter().
  */
 function menu_block_view_alter(&$data, $block) {
diff --git a/core/modules/node/lib/Drupal/node/Plugin/block/block/RecentBlock.php b/core/modules/node/lib/Drupal/node/Plugin/block/block/RecentBlock.php
new file mode 100644
index 0000000..6cf4382
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/block/block/RecentBlock.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Drupal\node\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "node_recent_block",
+ *   subject = @Translation("Recent content"),
+ *   module = "node",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "block_count" = "10"
+ *   }
+ * )
+ */
+class RecentBlock extends BlockBase {
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['block_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of recent content items to display'),
+      '#default_value' => $this->configuration['block_count'],
+      '#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)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['block_count'] = $form_state['values']['block_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build();
+   */
+  public function build() {
+    if ($nodes = node_get_recent($this->configuration['block_count'])) {
+      return array(
+        '#theme' => 'node_recent_block',
+        '#nodes' => $nodes,
+      );
+    }
+    else {
+      return array(
+        '#children' => t('No content available.'),
+      );
+    }
+  }
+}
diff --git a/core/modules/node/lib/Drupal/node/Plugin/block/block/SyndicateBlock.php b/core/modules/node/lib/Drupal/node/Plugin/block/block/SyndicateBlock.php
new file mode 100644
index 0000000..3855c7b
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/block/block/SyndicateBlock.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\node\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "node_syndicate_block",
+ *   subject = @Translation("Syndicate"),
+ *   module = "node",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "block_count" = "10",
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class SyndicateBlock extends BlockBase {
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::build();
+   */
+  public function build() {
+    return array(
+      '#theme' => 'feed_icon',
+      '#url' => 'rss.xml',
+    );
+  }
+}
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index bfba2fd..a4c9bd1 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1341,11 +1341,11 @@ function node_is_page(Node $node) {
  */
 function node_preprocess_block(&$variables) {
   if ($variables['block']->module == 'node') {
-    switch ($variables['block']->delta) {
-      case 'syndicate':
+    switch ($variables['block']->id) {
+      case 'node_syndicate_block':
         $variables['attributes']['role'] = 'complementary';
         break;
-      case 'recent':
+      case 'node_recent_block':
         $variables['attributes']['role'] = 'navigation';
         break;
     }
@@ -2104,78 +2104,6 @@ function node_revision_list(Node $node) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function node_block_info() {
-  $blocks['syndicate']['info'] = t('Syndicate');
-  // Not worth caching.
-  $blocks['syndicate']['cache'] = DRUPAL_NO_CACHE;
-
-  $blocks['recent']['info'] = t('Recent content');
-  $blocks['recent']['properties']['administrative'] = TRUE;
-
-  return $blocks;
-}
-
-/**
- * Implements hook_block_view().
- */
-function node_block_view($delta = '') {
-  $block = array();
-
-  switch ($delta) {
-    case 'syndicate':
-      $block['subject'] = t('Syndicate');
-      $block['content'] = array(
-        '#theme' => 'feed_icon',
-        '#url' => 'rss.xml',
-        '#title' => t('Syndicate'),
-      );
-      break;
-
-    case 'recent':
-      if (user_access('access content')) {
-        $block['subject'] = t('Recent content');
-        if ($nodes = node_get_recent(variable_get('node_recent_block_count', 10))) {
-          $block['content'] = array(
-            '#theme' => 'node_recent_block',
-            '#nodes' => $nodes,
-          );
-        } else {
-          $block['content'] = t('No content available.');
-        }
-      }
-      break;
-  }
-  return $block;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function node_block_configure($delta = '') {
-  $form = array();
-  if ($delta == 'recent') {
-    $form['node_recent_block_count'] = array(
-      '#type' => 'select',
-      '#title' => t('Number of recent content items to display'),
-      '#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)),
-    );
-  }
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function node_block_save($delta = '', $edit = array()) {
-  if ($delta == 'recent') {
-    variable_set('node_recent_block_count', $edit['node_recent_block_count']);
-  }
-}
-
-/**
  * Finds the most recently changed nodes that are available to the current user.
  *
  * @param $number
@@ -2289,7 +2217,7 @@ function theme_node_recent_content($variables) {
  * Adds node-type specific visibility options to add block form.
  */
 function node_form_block_add_block_form_alter(&$form, &$form_state) {
-  node_form_block_admin_configure_alter($form, $form_state);
+  //node_form_block_admin_configure_alter($form, $form_state);
 }
 
 /**
@@ -2300,9 +2228,8 @@ function node_form_block_add_block_form_alter(&$form, &$form_state) {
  * @see node_form_block_admin_configure_submit()
  */
 function node_form_block_admin_configure_alter(&$form, &$form_state) {
-  $default_type_options = db_query("SELECT type FROM {block_node_type} WHERE module = :module AND delta = :delta", array(
+  $default_type_options = db_query("SELECT type FROM {block_node_type} WHERE module = :module", array(
     ':module' => $form['module']['#value'],
-    ':delta' => $form['delta']['#value'],
   ))->fetchCol();
   $form['visibility']['node_type'] = array(
     '#type' => 'fieldset',
@@ -2328,41 +2255,18 @@ function node_form_block_admin_configure_alter(&$form, &$form_state) {
 function node_form_block_admin_configure_submit($form, &$form_state) {
   db_delete('block_node_type')
     ->condition('module', $form_state['values']['module'])
-    ->condition('delta', $form_state['values']['delta'])
     ->execute();
-  $query = db_insert('block_node_type')->fields(array('type', 'module', 'delta'));
+  $query = db_insert('block_node_type')->fields(array('type', 'module'));
   foreach (array_filter($form_state['values']['types']) as $type) {
     $query->values(array(
       'type' => $type,
       'module' => $form_state['values']['module'],
-      'delta' => $form_state['values']['delta'],
     ));
   }
   $query->execute();
 }
 
 /**
- * Implements hook_form_FORM_ID_alter() for block_custom_block_delete().
- *
- * Adds node specific submit handler to delete custom block form.
- *
- * @see node_form_block_custom_block_delete_submit()
- */
-function node_form_block_custom_block_delete_alter(&$form, &$form_state) {
-  $form['#submit'][] = 'node_form_block_custom_block_delete_submit';
-}
-
-/**
- * Form submission handler for node_form_block_custom_block_delete_alter().
- */
-function node_form_block_custom_block_delete_submit($form, &$form_state) {
-  db_delete('block_node_type')
-    ->condition('module', 'block')
-    ->condition('delta', $form_state['values']['bid'])
-    ->execute();
-}
-
-/**
  * Implements hook_modules_uninstalled().
  *
  * Cleans up the {block_node_type} table from modules' blocks.
diff --git a/core/modules/poll/lib/Drupal/poll/Plugin/block/block/PollRecentBlock.php b/core/modules/poll/lib/Drupal/poll/Plugin/block/block/PollRecentBlock.php
new file mode 100644
index 0000000..2a30a9c
--- /dev/null
+++ b/core/modules/poll/lib/Drupal/poll/Plugin/block/block/PollRecentBlock.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\poll\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "poll_recent_block",
+ *   subject = @Translation("Most recent poll"),
+ *   module = "poll",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "properties" = {
+ *       "administrative" = TRUE
+ *     }
+ *   }
+ * )
+ */
+class PollRecentBlock extends BlockBase {
+  protected $record;
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  function access() {
+    if (user_access('access content')) {
+      // Retrieve the latest poll.
+      $select = db_select('node', 'n');
+      $select->join('poll', 'p', 'p.nid = n.nid');
+      $select->fields('n', array('nid'))
+        ->condition('n.status', 1)
+        ->condition('p.active', 1)
+        ->orderBy('n.created', 'DESC')
+        ->range(0, 1)
+        ->addTag('node_access');
+
+      $record = $select->execute()->fetchObject();
+      if ($record) {
+        $this->record = $record;
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  function build() {
+    $poll = node_load($this->record->nid);
+    if ($poll->nid) {
+      $poll = poll_block_latest_poll_view($poll);
+      return array(
+        $poll->content
+      );
+    }
+    return array();
+  }
+}
\ No newline at end of file
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index dc4722e..c9af940 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -113,45 +113,6 @@ function _poll_menu_access($node, $perm, $inspect_allowvotes) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function poll_block_info() {
-  $blocks['recent']['info'] = t('Most recent poll');
-  $blocks['recent']['properties']['administrative'] = TRUE;
-  return $blocks;
-}
-
-/**
- * Implements hook_block_view().
- *
- * Generates a block containing the latest poll.
- */
-function poll_block_view($delta = '') {
-  if (user_access('access content')) {
-    // Retrieve the latest poll.
-    $select = db_select('node', 'n');
-    $select->join('poll', 'p', 'p.nid = n.nid');
-    $select->fields('n', array('nid'))
-      ->condition('n.status', 1)
-      ->condition('p.active', 1)
-      ->orderBy('n.created', 'DESC')
-      ->range(0, 1)
-      ->addTag('node_access');
-
-    $record = $select->execute()->fetchObject();
-    if ($record) {
-      $poll = node_load($record->nid);
-      if ($poll->nid) {
-        $poll = poll_block_latest_poll_view($poll);
-        $block['subject'] = t('Poll');
-        $block['content'] = $poll->content;
-        return $block;
-      }
-    }
-  }
-}
-
-/**
  * Implements hook_cron().
  *
  * Closes polls that have exceeded their allowed runtime.
diff --git a/core/modules/search/lib/Drupal/search/Plugin/block/block/SearchBlock.php b/core/modules/search/lib/Drupal/search/Plugin/block/block/SearchBlock.php
new file mode 100644
index 0000000..1c0e471
--- /dev/null
+++ b/core/modules/search/lib/Drupal/search/Plugin/block/block/SearchBlock.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\search\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "search_form_block",
+ *   subject = @Translation("Search form"),
+ *   module = "search",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class SearchBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('search content');
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    return drupal_get_form('search_block_form');
+  }
+}
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 315ae7a..564c32e 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -134,32 +134,10 @@ function search_permission() {
 }
 
 /**
- * Implements hook_block_info().
- */
-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;
-}
-
-/**
- * Implements hook_block_view().
- */
-function search_block_view($delta = '') {
-  if (user_access('search content')) {
-    $block['subject'] = t('Search');
-    $block['content'] = drupal_get_form('search_block_form');
-    return $block;
-  }
-}
-
-/**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_block().
  */
 function search_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'search' && $variables['block']->delta == 'form') {
+  if ($variables['block']->id == 'search_form_block') {
     $variables['attributes']['role'] = 'search';
     $variables['content_attributes']['class'][] = 'container-inline';
   }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Plugin/block/block/ShortcutsBlock.php b/core/modules/shortcut/lib/Drupal/shortcut/Plugin/block/block/ShortcutsBlock.php
new file mode 100644
index 0000000..f9b7b41
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Plugin/block/block/ShortcutsBlock.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\shortcut\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *  id = "shortcuts",
+ *  subject = @Translation("Shortcuts"),
+ *  module = "shortcut",
+ *  settings = {
+ *    "status" = TRUE,
+ *    "cache" = DRUPAL_NO_CACHE
+ *  }
+ * )
+ */
+class ShortcutsBlock extends BlockBase {
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $shortcut_set = shortcut_current_displayed_set();
+    return array(
+      shortcut_renderable_links($shortcut_set),
+    );
+  }
+}
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 9bbb12a..a488a45 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -184,29 +184,6 @@ function shortcut_theme() {
 }
 
 /**
- * Implements hook_block_info().
- */
-function shortcut_block_info() {
-  $blocks['shortcuts']['info'] = t('Shortcuts');
-  // Shortcut blocks can't be cached because each menu item can have a custom
-  // access callback. menu.inc manages its own caching.
-  $blocks['shortcuts']['cache'] = DRUPAL_NO_CACHE;
-  return $blocks;
-}
-
-/**
- * Implements hook_block_view().
- */
-function shortcut_block_view($delta = '') {
-  if ($delta == 'shortcuts') {
-    $shortcut_set = shortcut_current_displayed_set();
-    $data['subject'] = t('@shortcut_set shortcuts', array('@shortcut_set' => $shortcut_set->title));
-    $data['content'] = shortcut_renderable_links($shortcut_set);
-    return $data;
-  }
-}
-
-/**
  * Access callback for editing a shortcut set.
  *
  * @param object $shortcut_set
diff --git a/core/modules/statistics/lib/Drupal/statistics/Plugin/block/block/StatisticsPopularBlock.php b/core/modules/statistics/lib/Drupal/statistics/Plugin/block/block/StatisticsPopularBlock.php
new file mode 100644
index 0000000..72354e1
--- /dev/null
+++ b/core/modules/statistics/lib/Drupal/statistics/Plugin/block/block/StatisticsPopularBlock.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Drupal\statistics\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "statistics_popular_block",
+ *   subject = @Translation("Popular content"),
+ *   module = "statistics",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE,
+ *     "top_day_num" = 0,
+ *     "top_all_num" = 0,
+ *     "top_last_num" = 0
+ *   }
+ * )
+ */
+class StatisticsPopularBlock extends BlockBase {
+  /**
+   * TODO add an access check to the plugin definition for
+   * variable_get('statistics_count_content_views', 0)
+   */
+
+  protected $day_list;
+  protected $all_time_list;
+  protected $last_list;
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    // Popular content block settings
+    $numbers = array('0' => t('Disabled')) + drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40));
+    $form['statistics_block_top_day_num'] = array(
+     '#type' => 'select',
+     '#title' => t("Number of day's top views to display"),
+     '#default_value' => $this->configuration['top_day_num'],
+     '#options' => $numbers,
+     '#description' => t('How many content items to display in "day" list.')
+    );
+    $form['statistics_block_top_all_num'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of all time views to display'),
+      '#default_value' => $this->configuration['top_all_num'],
+      '#options' => $numbers,
+      '#description' => t('How many content items to display in "all time" list.')
+    );
+    $form['statistics_block_top_last_num'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of most recent views to display'),
+      '#default_value' => $this->configuration['top_last_num'],
+      '#options' => $numbers,
+      '#description' => t('How many content items to display in "recently viewed" list.')
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['top_day_num'] = $form_state['values']['statistics_block_top_day_num'];
+    $this->configuration['top_all_num'] = $form_state['values']['statistics_block_top_all_num'];
+    $this->configuration['top_last_num'] = $form_state['values']['statistics_block_top_last_num'];
+  }
+
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    if (user_access('access content')) {
+      $daytop = $this->configuration['top_day_num'];
+      if (!$daytop || !($result = statistics_title_list('daycount', $daytop)) || !($this->day_list = node_title_list($result, t("Today's:")))) {
+        return FALSE;
+      }
+      $alltimetop = $this->configuration['top_all_num'];
+      if (!$alltimetop || !($result = statistics_title_list('totalcount', $alltimetop)) || !($this->all_time_list = node_title_list($result, t('All time:')))) {
+        return FALSE;
+      }
+      $lasttop = $this->configuration['top_last_num'];
+      if (!$lasttop || !($result = statistics_title_list('timestamp', $lasttop)) || !($this->last_list = node_title_list($result, t('Last viewed:')))) {
+        return FALSE;
+      }
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    $content = array();
+
+    if ($this->day_list) {
+      $content['top_day'] = $this->day_list;
+      $content['top_day']['#suffix'] = '<br />';
+    }
+
+    if ($this->all_time_list) {
+      $content['top_all'] = $this->all_time_list;
+      $content['top_all']['#suffix'] = '<br />';
+    }
+
+    if ($this->last_list) {
+      $content['top_last'] = $this->last_list;
+      $content['top_last']['#suffix'] = '<br />';
+    }
+
+    return $content;
+  }
+
+}
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index c07a1c8..5fdcaca 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -304,79 +304,6 @@ function statistics_get($nid) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function statistics_block_info() {
-  $blocks = array();
-
-  $statistics_count_content_views = config('statistics.settings')->get('count_content_views');
-  if (!empty($statistics_count_content_views)) {
-    $blocks['popular']['info'] = t('Popular content');
-    // Too dynamic to cache.
-    $blocks['popular']['cache'] = DRUPAL_NO_CACHE;
-  }
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function statistics_block_configure($delta = '') {
-  $config = config('statistics.settings');
-  // Popular content block settings
-  $numbers = array('0' => t('Disabled')) + drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40));
-  $form['statistics_block_popular_top_day_limit'] = array('#type' => 'select', '#title' => t("Number of day's top views to display"), '#default_value' => $config->get('block.popular.top_day_limit'), '#options' => $numbers, '#description' => t('How many content items to display in "day" list.'));
-  $form['statistics_block_popular_top_all_limit'] = array('#type' => 'select', '#title' => t('Number of all time views to display'), '#default_value' => $config->get('block.popular.top_all_limit'), '#options' => $numbers, '#description' => t('How many content items to display in "all time" list.'));
-  $form['statistics_block_popular_top_recent_limit'] = array('#type' => 'select', '#title' => t('Number of most recent views to display'), '#default_value' => $config->get('block.popular.top_recent_limit'), '#options' => $numbers, '#description' => t('How many content items to display in "recently viewed" list.'));
-  return $form;
-}
-
-/**
- * Implements hook_block_save().
- */
-function statistics_block_save($delta = '', $edit = array()) {
-  config('statistics.settings')
-    ->set('block.popular.top_day_limit', $edit['statistics_block_popular_top_day_limit'])
-    ->set('block.popular.top_all_limit', $edit['statistics_block_popular_top_all_limit'])
-    ->set('block.popular.top_recent_limit', $edit['statistics_block_popular_top_recent_limit'])
-    ->save();
-}
-
-/**
- * Implements hook_block_view().
- */
-function statistics_block_view($delta = '') {
-  if (user_access('access content')) {
-    $content = array();
-
-    $config = config('statistics.settings');
-    $daytop = $config->get('block.popular.top_day_limit');
-    if ($daytop && ($result = statistics_title_list('daycount', $daytop)) && ($node_title_list = node_title_list($result, t("Today's:")))) {
-      $content['top_day'] = $node_title_list;
-      $content['top_day']['#suffix'] = '<br />';
-    }
-
-    $alltimetop = $config->get('block.popular.top_all_limit');
-    if ($alltimetop && ($result = statistics_title_list('totalcount', $alltimetop)) && ($node_title_list = node_title_list($result, t('All time:')))) {
-      $content['top_all'] = $node_title_list;
-      $content['top_all']['#suffix'] = '<br />';
-    }
-
-    $lasttop = $config->get('block.popular.top_recent_limit');
-    if ($lasttop && ($result = statistics_title_list('timestamp', $lasttop)) && ($node_title_list = node_title_list($result, t('Last viewed:')))) {
-      $content['top_last'] = $node_title_list;
-      $content['top_last']['#suffix'] = '<br />';
-    }
-
-    if (count($content)) {
-      $block['content'] = $content;
-      $block['subject'] = t('Popular content');
-      return $block;
-    }
-  }
-}
-
-/**
  * Generates a link to a path, truncating the displayed text to a given width.
  *
  * @param string $path
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php
new file mode 100644
index 0000000..35df4bb
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\system\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+class SystemMenuBlock implements DerivativeInterface {
+  protected $derivatives;
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    foreach (menu_list_system_menus() as $menu => $name) {
+      $this->derivatives[$menu] = $base_plugin_definition;
+      $this->derivatives[$menu]['delta'] = $menu;
+      $this->derivatives[$menu]['subject'] = t('System Menu: @menu', array('@menu' => $name));
+      $this->derivatives[$menu]['cache'] = DRUPAL_NO_CACHE;
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/PluginUIBase.php b/core/modules/system/lib/Drupal/system/Plugin/PluginUIBase.php
new file mode 100644
index 0000000..ad3764d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/PluginUIBase.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\system\Plugin;
+
+use Drupal\Component\Plugin\PluginBase;
+
+abstract class PluginUIBase extends PluginBase {
+  public function form($form, &$form_state, $plugin, $facet) {
+    $plugin_definition = $this->getDefinition();
+    $manager = new $plugin_definition['manager']();
+    $plugins = $manager->getDefinitions();
+    $form['#theme'] = 'system_plugin_ui_form';
+    $form['instance'] = array(
+      '#type' => 'value',
+      '#value' => $this,
+    );
+    $form['right']['block'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Search'),
+      '#autocomplete_path' => 'system/autocomplete/' . $this->getPluginId(),
+    );
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Next'),
+    );
+    $rows = array();
+    foreach ($plugins as $plugin_id => $config) {
+      if (empty($facet) || $this->facetCompare($facet, $config)) {
+        $row = $this->row($plugin, $plugin_definition, $plugin_id, $config);
+        $rows[] = $row;
+      }
+      foreach ($plugin_definition['facets'] as $key => $title) {
+        $facets[$key][$config[$key]] = $this->facetLink($key, $plugin, $plugin_definition, $plugin_id, $config, $key);
+      }
+      $form['right']['all_plugins'] = array(
+        '#type' => 'markup',
+        '#markup' => l($plugin_definition['all_plugins'], $this->allPluginsUrl($plugin, $plugin_definition, $plugin_id, $config)),
+      );
+      foreach ($facets as $group => $values) {
+        $form['right'][$group] = array(
+          '#type' => 'markup',
+          '#markup' => theme('links', array('heading' => array('text' => $plugin_definition['facets'][$group], 'level' => 'h3'), 'links' => $values)),
+        );
+      }
+      $form['left']['plugin_library'] = array(
+        '#type' => 'markup',
+        '#markup' => theme('table', array('header' => $this->tableHeader(), 'rows' => $rows)),
+      );
+    }
+    return $form;
+  }
+
+  public function formValidate($form, &$form_state) {}
+
+  public function formSubmit($form, &$form_state) {}
+
+  public function access($plugin_id) {
+    $definition = $this->getDefinition($plugin_id);
+    return call_user_func_array('user_access', $definition['access_arguments']);
+  }
+
+  public function row($plugin, $plugin_definition, $plugin_id, $config) {
+    return array($config['title'], l($plugin_definition['link_title'], $plugin_definition['config_path'] . '/' . $plugin_id));
+  }
+
+  public function facetLink($facet, $plugin, $plugin_definition, $plugin_id, $config, $key) {
+    return array('title' => $config[$key], 'href' => $plugin_definition['path'] . '/' . $plugin . '/' . $facet . ':' . $config[$key]);
+  }
+
+  public function allPluginsUrl($plugin, $plugin_definition, $plugin_id, $config) {
+    return $plugin_definition['path'] . '/' . $plugin . '/add';
+  }
+
+  public function tableHeader() {
+    return array(t('Title'), t('Operations'));
+  }
+
+  public function facetCompare($facet, $config) {
+    list($facet_type, $option) = explode(':', $facet);
+    return $option == $config[$facet_type];
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/PluginUIInterface.php b/core/modules/system/lib/Drupal/system/Plugin/PluginUIInterface.php
new file mode 100644
index 0000000..e05e90f
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/PluginUIInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Drupal\system\Plugin;
+
+/**
+ * Interface definition for PluginUI plugins.
+ */
+interface PluginUIInterface {
+
+  /**
+   * Create a form array.
+   *
+   * @return array().
+   */
+  public function form($form, &$form_state, $plugin, $facet);
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php
new file mode 100644
index 0000000..d01c6ae
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/Type/PluginUIManager.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Drupal\system\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Component\Plugin\Factory\ReflectionFactory;
+
+class PluginUIManager extends PluginManagerBase {
+  public function __construct() {
+    $this->discovery = new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('system', 'plugin_ui'));
+    $this->factory = new ReflectionFactory($this);
+  }
+}
\ No newline at end of file
diff --git a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemHelpBlock.php b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemHelpBlock.php
new file mode 100644
index 0000000..53af16e
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemHelpBlock.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\system\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "system_help_block",
+ *   subject = @Translation("System Help"),
+ *   module = "system",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class SystemHelpBlock extends BlockBase {
+  private $help;
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    $this->help = menu_get_active_help();
+    return $this->help ? TRUE : FALSE;
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    if (empty($this->help)) {
+      $this->help = menu_get_active_help();
+    }
+    return array(
+      '#children' => $this->help,
+    );
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php
new file mode 100644
index 0000000..a4202f3
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMainBlock.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\system\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "system_main_block",
+ *   subject = @Translation("Main page content"),
+ *   module = "system",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class SystemMainBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    return array(
+      drupal_set_page_content()
+    );
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMenuBlock.php b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMenuBlock.php
new file mode 100644
index 0000000..4a7da7a
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemMenuBlock.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\system\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "system_menu_block",
+ *   subject = @Translation("System Menu"),
+ *   module = "system",
+ *   derivative = "Drupal\system\Plugin\Derivative\SystemMenuBlock",
+ *   settings = {
+ *     "status" = "TRUE"
+ *   }
+ * )
+ */
+class SystemMenuBlock extends BlockBase {
+
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    global $user;
+    if (!$user->uid) {
+      return FALSE;
+    }
+    return parent::access();
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    list($plugin, $derivative) = explode(':', $this->getPluginId());
+    return menu_tree($derivative);
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemPoweredByBlock.php b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemPoweredByBlock.php
new file mode 100644
index 0000000..b4b2612
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/block/block/SystemPoweredByBlock.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\system\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "system_powered_by_block",
+ *   subject = @Translation("Powered by Drupal"),
+ *   module = "system",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE
+ *   }
+ * )
+ */
+class SystemPoweredByBlock extends BlockBase {
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    return array(
+      '#children' => theme('system_powered_by'),
+    );
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/SystemBundle.php b/core/modules/system/lib/Drupal/system/SystemBundle.php
new file mode 100644
index 0000000..af2c029
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/SystemBundle.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\SystemBundle.
+ */
+
+namespace Drupal\system;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * System dependency injection container.
+ */
+class SystemBundle extends Bundle {
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle.
+   */
+  public function build(ContainerBuilder $container) {
+    // Register the various system plugin manager classes with the dependency
+    // injection container.
+    $container->register('plugin.manager.system.plugin_ui', 'Drupal\system\Plugin\Type\PluginUIManager');
+  }
+}
\ No newline at end of file
diff --git a/core/modules/system/system-plugin-ui-form.tpl.php b/core/modules/system/system-plugin-ui-form.tpl.php
new file mode 100644
index 0000000..8179076
--- /dev/null
+++ b/core/modules/system/system-plugin-ui-form.tpl.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to configure blocks.
+ *
+ * Available variables:
+ * - $left: Any form array elements that should appear in the left hand column.
+ * - $right: Any form array elements that should appear in the right hand column.
+ * - $form_submit: Form submit button.
+ *
+ * @see template_preprocess_block_library_form()
+ * @see theme_block_library_form()
+ *
+ * @ingroup themeable
+ */
+?>
+<div id="block-library" class="container">
+  <div class="left-col">
+    <div class="inside">
+      <?php print $left; ?>
+    </div>
+  </div>
+  <div class="right-col">
+    <div class="inside">
+      <?php print $right; ?>
+    </div>
+  </div>
+  <?php if ($form_submit) { ?>
+  <div class="bottom-bar"><?php print $form_submit; ?></div>
+  <?php } ?>
+</div>
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index e5dc768..063c840 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -205,6 +205,10 @@ function system_theme() {
     'system_date_format_localize_form' => array(
       'render element' => 'form',
     ),
+    'system_plugin_ui_form' => array(
+      'template' => 'system-plugin-ui-form',
+      'render element' => 'form',
+    ),
   ));
 }
 
@@ -1138,10 +1142,117 @@ function system_menu() {
     );
   }
 
+  foreach (system_plugin_manager('plugin_ui')->getDefinitions() as $plugin_id => $plugin) {
+    if ($plugin['menu'] === TRUE) {
+      $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']] = array(
+        'title' => $plugin['title'],
+        'page callback' => 'drupal_get_form',
+        'page arguments' => array($plugin['id'], $plugin_id),
+        'access callback' => 'system_plugin_ui_access',
+        'access arguments' => array($plugin_id),
+        'type' => $plugin['type'],
+      );
+      $items[$plugin['path'] . '/' . $plugin_id . '/%'] = array(
+        'title' => $plugin['title'],
+        'page callback' => 'drupal_get_form',
+        'page arguments' => array($plugin['id'], $plugin_id, (count(explode('/', $plugin['path'])) + 1)),
+        'access callback' => 'system_plugin_ui_access',
+        'access arguments' => array($plugin_id, (count(explode('/', $plugin['path'])) + 1)),
+        'type' => MENU_CALLBACK,
+      );
+      if (!empty($plugin['file'])) {
+        $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']]['file'] = $plugin['file'];
+        $items[$plugin['path'] . '/' . $plugin_id . '/%']['file'] = $plugin['file'];
+        if (!empty($plugin['file_path'])) {
+          $items[$plugin['path'] . '/' . $plugin_id . '/' . $plugin['suffix']]['file path'] = $plugin['file_path'];
+          $items[$plugin['path'] . '/' . $plugin_id . '/%']['file path'] = $plugin['file_path'];
+        }
+      }
+      $items['system/autocomplete/' . $plugin_id] = array(
+        'page callback' => 'system_plugin_autocomplete',
+        'page arguments' => array($plugin_id),
+        'access callback' => 'system_plugin_ui_access',
+        'access arguments' => array($plugin_id),
+        'type' => MENU_CALLBACK,
+      );
+    }
+  }
+
   return $items;
 }
 
 /**
+ * A simple wrapper function for proxying to the plugin class' form method.
+ */
+function system_plugin_ui_form($form, &$form_state, $plugin, $facet = NULL) {
+  $ui = system_plugin_manager('plugin_ui')->createInstance($plugin);
+  $form = $ui->form($form, $form_state, $plugin, $facet);
+  return $form;
+}
+
+/**
+ * A simple wrapper function for proxying to the plugin class' validate method.
+ */
+function system_plugin_ui_form_validate($form, &$form_state) {
+  $form_state['values']['instance']->formValidate($form, $form_state);
+}
+
+/**
+ * A simple wrapper function for proxying to the plugin class' submit method.
+ */
+function system_plugin_ui_form_submit($form, &$form_state) {
+  $form_state['values']['instance']->formSubmit($form, $form_state);
+}
+
+/**
+ * A generic autocomplete page callback for any plugin system tied to a plugin
+ * ui plugin.
+ */
+function system_plugin_autocomplete($plugin_id, $string = '') {
+  $matches = array();
+  $ui = system_plugin_manager('plugin_ui')->getDefinition($plugin_id);
+  $manager = new $ui['manager']();
+  if ($string) {
+    $plugins = $manager->getDefinitions();
+    $titles = array();
+    foreach($plugins as $plugin_id => $plugin) {
+      $titles[$plugin_id] = $plugin[$ui['title_attribute']];
+    }
+    $matches = preg_grep("/\b". $string . "/i", $titles);
+  }
+
+  return new JsonResponse($matches);
+}
+
+/**
+ * A simple wrapper function for proxying to the plugin class' access method.
+ */
+function system_plugin_ui_access($plugin, $facet = NULL) {
+  $ui = system_plugin_manager('plugin_ui')->createInstance($plugin);
+  return $ui->access($plugin, $facet);
+}
+
+/**
+ * A simple function for retreiving system plugin type managers from the DIC.
+ */
+function system_plugin_manager($plugin_type) {
+  return drupal_container()->get("plugin.manager.system.$plugin_type");
+}
+
+/**
+ * Implements hook_forms().
+ */
+function system_forms() {
+  $forms = array();
+  foreach (system_plugin_manager('plugin_ui')->getDefinitions() as $plugin_id => $plugin) {
+    if (empty($forms[$plugin['id']])) {
+      $forms[$plugin['id']]['callback'] = 'system_plugin_ui_form';
+    }
+  }
+  return $forms;
+}
+
+/**
  * Page callback; Execute cron tasks.
  *
  * @see system_cron_access().
@@ -2412,96 +2523,40 @@ function system_user_timezone(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_block_info().
+ * Implements hook_preprocess_HOOK() for block.tpl.php.
  */
-function system_block_info() {
-  $blocks['main'] = array(
-    'info' => t('Main page content'),
-    // Cached elsewhere.
-    'cache' => DRUPAL_NO_CACHE,
-    // Auto-enable in 'content' region by default, which always exists.
-    // @see system_themes_page(), drupal_render_page()
-    'region' => 'content',
-    'status' => 1,
-  );
-  $blocks['powered-by'] = array(
-    'info' => t('Powered by Drupal'),
-    'weight' => '10',
-    'cache' => DRUPAL_NO_CACHE,
-  );
-  $blocks['help'] = array(
-    'info' => t('System help'),
-    'weight' => '5',
-    'cache' => DRUPAL_NO_CACHE,
-    // Auto-enable in 'help' region by default, if the theme defines one.
-    'region' => 'help',
-    'status' => 1,
-  );
-  // System-defined menu blocks.
-  foreach (menu_list_system_menus() as $menu_name => $title) {
-    $blocks[$menu_name]['info'] = t($title);
-    // Menu blocks can't be cached because each menu item can have
-    // a custom access callback. menu.inc manages its own caching.
-    $blocks[$menu_name]['cache'] = DRUPAL_NO_CACHE;
-  }
-  return $blocks;
-}
+function system_preprocess_block(&$variables) {
+  switch ($variables['block']->id) {
+    case 'system_powered_by_block':
+      $variables['attributes_array']['role'] = 'complementary';
+      break;
+    case 'system_help_block':
+      $variables['attributes_array']['role'] = 'complementary';
+      break;
 
-/**
- * Implements hook_block_view().
- *
- * Generate a block with a promotional link to Drupal.org and
- * all system menu blocks.
- */
-function system_block_view($delta = '') {
-  $block = array();
-  switch ($delta) {
-    case 'main':
-      $block['subject'] = NULL;
-      $block['content'] = drupal_set_page_content();
-      return $block;
-    case 'powered-by':
-      $block['subject'] = NULL;
-      $block['content'] = theme('system_powered_by');
-      return $block;
-    case 'help':
-      $block['subject'] = NULL;
-      $block['content'] = menu_get_active_help();
-      return $block;
+    // System menu blocks should get the same class as menu module blocks.
     default:
-      // All system menu blocks.
-      $system_menus = menu_list_system_menus();
-      if (isset($system_menus[$delta])) {
-        $block['subject'] = t($system_menus[$delta]);
-        $block['content'] = menu_tree($delta);
-        return $block;
+      if ($variables['block']->class == 'Drupal\\system\\Plugin\\block\\block\\SystemMenuBlock') {
+        $variables['attributes_array']['role'] = 'navigation';
+        $variables['classes_array'][] = 'block-menu';
       }
-      break;
   }
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Processes variables for block-admin-display-form.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $form
+ *
+ * @see block-admin-display.tpl.php
+ * @see theme_block_admin_display()
  */
-function system_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'system') {
-
-    switch ($variables['block']->delta) {
-      case 'powered-by':
-        $variables['attributes']['role'] = 'complementary';
-        break;
-      case 'help':
-        $variables['attributes']['role'] = 'complementary';
-        break;
-
-      // System menu blocks should get the same class as menu module blocks.
-      default:
-        if (in_array($variables['block']->delta, array_keys(menu_list_system_menus()))) {
-          $variables['attributes']['role'] = 'navigation';
-          $variables['attributes']['class'][] = 'block-menu';
-        }
-    }
-  }
+function template_preprocess_system_plugin_ui_form(&$variables) {
+  drupal_add_css(drupal_get_path('module', 'system') . '/system.plugin.ui.css');
+  $variables['left'] = drupal_render($variables['form']['left']);
+  $variables['right'] = drupal_render($variables['form']['right']);
+  $variables['form_submit'] = drupal_render_children($variables['form']);
 }
 
 /**
diff --git a/core/modules/system/system.plugin.ui.css b/core/modules/system/system.plugin.ui.css
new file mode 100644
index 0000000..207a6ee
--- /dev/null
+++ b/core/modules/system/system.plugin.ui.css
@@ -0,0 +1,29 @@
+#block-library .left-col,
+#block-library .right-col {
+  float:left;
+  width:66%;
+  height:100%;
+  background-color:#ffffff;
+}
+
+#block-library .right-col {
+  width:34%;
+  background-color:#f7f7f7;
+}
+
+#block-library .right-col h3 {
+  margin: 1em -20px;
+  background-color:#d7d7d7;
+  color:#333333;
+  padding:8px 15px;
+  font-size:1.1em;
+}
+
+#block-library .inside {
+  margin:0 20px;
+}
+
+#block-library .bottom-bar {
+  width:100%;
+  clear:both;
+}
\ No newline at end of file
diff --git a/core/modules/user/lib/Drupal/user/Plugin/block/block/UserLoginBlock.php b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserLoginBlock.php
new file mode 100644
index 0000000..0c811d3
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserLoginBlock.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\user\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "user_login_block",
+ *   subject = @Translation("User login"),
+ *   module = "user",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE,
+ *     "whois_new_count" = 5
+ *   }
+ * )
+ */
+class UserLoginBlock extends BlockBase {
+  /**
+   * Implements AccessInterface::access().
+   */
+  public function access() {
+    global $user;
+    return (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1))));
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    return array(
+      drupal_get_form('user_login_block')
+    );
+  }
+}
diff --git a/core/modules/user/lib/Drupal/user/Plugin/block/block/UserNewBlock.php b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserNewBlock.php
new file mode 100644
index 0000000..2242f68
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserNewBlock.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\user\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "user_online_block",
+ *   subject = @Translation("Who's new"),
+ *   module = "user",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "properties" = {
+ *       "administrative" = TRUE
+ *     },
+ *     "whois_new_count" = 5
+ *   }
+ * )
+ */
+class UserNewBlock extends BlockBase {
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $form['user_block_whois_new_count'] = array(
+      '#type' => 'select',
+      '#title' => t('Number of users to display'),
+      '#default_value' => $this->configuration['whois_new_count'],
+      '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, $form_state);
+    $this->configuration['whois_new_count'] = $form_state['values']['user_block_whois_new_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    // 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', 0, $this->configuration['whois_new_count'])->fetchAll();
+    $build = array(
+      '#theme' => 'item_list__user__new',
+      '#items' => array(),
+    );
+    foreach ($items as $account) {
+      $build['#items'][] = theme('username', array('account' => $account));
+    }
+    return $build;
+  }
+}
diff --git a/core/modules/user/lib/Drupal/user/Plugin/block/block/UserOnlineBlock.php b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserOnlineBlock.php
new file mode 100644
index 0000000..2d5e513
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/block/block/UserOnlineBlock.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Drupal\user\Plugin\block\block;
+
+use Drupal\block\BlockBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @Plugin(
+ *   id = "user_online_block",
+ *   subject = @Translation("Who's online"),
+ *   module = "user",
+ *   settings = {
+ *     "status" = TRUE,
+ *     "cache" = DRUPAL_NO_CACHE,
+ *     "properties" = {
+ *       "administrative" = TRUE
+ *     },
+ *     "seconds_online" = 900,
+ *     "max_list_count" = 10
+ *   }
+ * )
+ */
+class UserOnlineBlock extends BlockBase {
+  /**
+   * Implements BlockInterface::access().
+   */
+  public function access() {
+    return user_access('access content');
+  }
+
+  /**
+   * Implements BlockInterface::configure().
+   */
+  public function configure($form, &$form_state) {
+    $form = parent::configure($form, $form_state);
+    $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
+    $form['user_block_seconds_online'] = array(
+      '#type' => 'select',
+      '#title' => t('User activity'),
+      '#default_value' => $this->configuration['seconds_online'],
+      '#options' => $period,
+      '#description' => t('A user is considered online for this long after they have last viewed a page.')
+    );
+    $form['user_block_max_list_count'] = array(
+      '#type' => 'select',
+      '#title' => t('User list length'),
+      '#default_value' => $this->configuration['max_list_count'],
+      '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)),
+      '#description' => t('Maximum number of currently online users to display.')
+    );
+    return $form;
+  }
+
+  /**
+   * Implements BlockInterface::configureSubmit().
+   */
+  public function configureSubmit($form, &$form_state) {
+    parent::configureSubmit($form, &$form_state);
+    $this->configuration['seconds_online'] = $form_state['values']['user_block_seconds_online'];
+    $this->configuration['max_list_count'] = $form_state['values']['user_block_max_list_count'];
+  }
+
+  /**
+   * Implements BlockInterface::build().
+   */
+  public function build() {
+    // Count users active within the defined period.
+    $interval = REQUEST_TIME - $this->configuration['max_list_count'];
+
+    // Perform database queries to gather online user lists. We use s.timestamp
+    // rather than u.access because it is much faster.
+    $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
+
+    $build = array(
+      '#theme' => 'item_list__user__online',
+      '#items' => array(),
+      '#prefix' => '<p>' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '</p>',
+    );
+
+    // Display a list of currently online users.
+    $max_users = $this->configuration['max_list_count'];
+    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', 0, $max_users, array(':interval' => $interval))->fetchAll();
+      foreach ($items as $account) {
+        $block['content']['#items'][] = theme('username', array('account' => $account));
+      }
+    }
+
+    return $build;
+  }
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 3b69966..47f8ea2 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -761,145 +761,18 @@ function user_login_block($form) {
 }
 
 /**
- * Implements hook_block_info().
- */
-function user_block_info() {
-  global $user;
-
-  $blocks['login']['info'] = t('User login');
-  // Not worth caching.
-  $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'] = TRUE;
-
-  return $blocks;
-}
-
-/**
- * Implements hook_block_configure().
- */
-function user_block_configure($delta = '') {
-  global $user;
-
-  switch ($delta) {
-    case 'new':
-      $form['user_block_whois_new_count'] = array(
-        '#type' => 'select',
-        '#title' => t('Number of users to display'),
-        '#default_value' => variable_get('user_block_whois_new_count', 5),
-        '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
-      );
-      return $form;
-
-    case 'online':
-      $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
-      $form['user_block_seconds_online'] = array('#type' => 'select', '#title' => t('User activity'), '#default_value' => variable_get('user_block_seconds_online', 900), '#options' => $period, '#description' => t('A user is considered online for this long after they have last viewed a page.'));
-      $form['user_block_max_list_count'] = array('#type' => 'select', '#title' => t('User list length'), '#default_value' => variable_get('user_block_max_list_count', 10), '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), '#description' => t('Maximum number of currently online users to display.'));
-      return $form;
-  }
-}
-
-/**
- * Implements hook_block_save().
- */
-function user_block_save($delta = '', $edit = array()) {
-  global $user;
-
-  switch ($delta) {
-    case 'new':
-      variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
-      break;
-
-    case 'online':
-      variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
-      variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
-      break;
-  }
-}
-
-/**
- * Implements 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 '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', 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
-
-        $block['subject'] = t('Who\'s new');
-        $block['content'] = array(
-          '#theme' => 'item_list__user__new',
-          '#items' => array(),
-        );
-        foreach ($items as $account) {
-          $block['content']['#items'][] = theme('username', array('account' => $account));
-        }
-      }
-      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.
-        $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
-
-        $block['subject'] = t('Who\'s online');
-        $block['content'] = array(
-          '#theme' => 'item_list__user__online',
-          '#items' => array(),
-          '#prefix' => '<p>' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '</p>',
-        );
-
-        // 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', 0, $max_users, array(':interval' => $interval))->fetchAll();
-
-          foreach ($items as $account) {
-            $block['content']['#items'][] = theme('username', array('account' => $account));
-          }
-        }
-      }
-      return $block;
-  }
-}
-
-/**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_block().
  */
 function user_preprocess_block(&$variables) {
   if ($variables['block']->module == 'user') {
-    switch ($variables['block']->delta) {
-      case 'login':
+    switch ($variables['block']->id) {
+      case 'user_login_block':
         $variables['attributes']['role'] = 'form';
         break;
-      case 'new':
+      case 'user_new_block':
         $variables['attributes']['role'] = 'complementary';
         break;
-      case 'online':
+      case 'user_online_block':
         $variables['attributes']['role'] = 'complementary';
         break;
     }
