diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index deed583..9542dc0 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -554,10 +554,10 @@ function aggregator_filter_xss($value) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function aggregator_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'aggregator') {
+function aggregator_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'aggregator') {
     $variables['attributes']['role'] = 'complementary';
   }
 }
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
index 1c6056d..b60dde4 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
@@ -50,7 +50,7 @@ public function testBlockLinks() {
     ));
     $this->drupalLogin($admin_user);
 
-    $block = $this->drupalPlaceBlock("aggregator_feed_block:{$feed->id()}", array('label' => 'feed-' . $feed->label()), array('block_count' => 2));
+    $block = $this->drupalPlaceBlock("aggregator_feed_block:{$feed->id()}", array('label' => 'feed-' . $feed->label(), 'block_count' => 2));
 
     // Confirm that the block is now being displayed on pages.
     $this->drupalGet('test-page');
diff --git a/core/modules/block/block.api.php b/core/modules/block/block.api.php
index c9b3b1f..14d226b 100644
--- a/core/modules/block/block.api.php
+++ b/core/modules/block/block.api.php
@@ -11,23 +11,24 @@
  */
 
 /**
- * Perform alterations to the content of a block.
+ * Perform alterations to the content of a block entity.
  *
  * This hook allows you to modify any data returned by hook_block_view().
  *
- * Note that instead of hook_block_view_alter(), which is called for all blocks,
- * you can also use hook_block_view_ID_alter() to alter a specific block, or
- * hook_block_view_NAME_alter() to alter a specific block instance.
+ * Note that instead of hook_block_view_alter(), which is called for all
+ * blocks, you can also use hook_block_view_ID_alter() to alter a specific
+ * block, or hook_block_plugin_view_NAME_alter() to alter a specific block
+ * instance.
  *
  * @param array $build
  *   A renderable array of data, as returned from the build() implementation of
  *   the plugin that defined the block:
  *   - #title: The default localized title of the block.
- * @param \Drupal\block\BlockInterface $block
- *   The block instance.
+ * @param \Drupal\block\Plugin\Core\Entity\Block $block
+ *   The block entity.
  *
  * @see hook_block_view_ID_alter()
- * @see hook_block_view_NAME_alter()
+ * @see hook_block_plugin_view_NAME_alter()
  */
 function hook_block_view_alter(array &$build, \Drupal\block\Plugin\Core\Entity\Block $block) {
   // Remove the contextual links on all blocks that provide them.
@@ -42,25 +43,25 @@ function hook_block_view_alter(array &$build, \Drupal\block\Plugin\Core\Entity\B
 }
 
 /**
- * Perform alterations to a specific block.
+ * Perform alterations to the content of a specific block entity.
  *
- * Modules can implement hook_block_view_ID_alter() to modify a specific block,
- * rather than implementing hook_block_view_alter().
+ * Modules can implement hook_block_view_ID_alter() to modify a specific block
+ * entity, rather than implementing hook_block_view_alter().
  *
  * @param array $build
  *   A renderable array of data, as returned from the build() implementation of
  *   the plugin that defined the block:
  *   - #title: The default localized title of the block.
- * @param \Drupal\block\BlockInterface $block
- *   The block instance.
+ * @param \Drupal\block\Plugin\Core\Entity\Block $block
+ *   The block entity.
  *
  * @todo Add a more specific example of a block ID, and illustrate how this is
- *   different from hook_block_view_NAME_alter().
+ *   different from hook_block_plugin_view_NAME_alter().
  *
  * @see hook_block_view_alter()
- * @see hook_block_view_NAME_alter()
+ * @see hook_block_plugin_view_NAME_alter()
  */
-function hook_block_view_ID_alter(array &$build, \Drupal\block\BlockInterface $block) {
+function hook_block_view_ID_alter(array &$build, \Drupal\block\Plugin\Core\Entity\Block $block) {
   // This code will only run for a specific block. For example, if ID
   // in the function definition above is set to "someid", the code
   // will only run on the "someid" block.
@@ -70,10 +71,27 @@ function hook_block_view_ID_alter(array &$build, \Drupal\block\BlockInterface $b
 }
 
 /**
- * Perform alterations to a specific block instance.
+ * Perform alterations to the content of a block plugin.
+ *
+ * @param array $build
+ *   A renderable array, as returned by the plugin that defined the block.
+ * @param \Drupal\block\BlockInterface $block
+ *   The block plugin.
+ */
+function hook_block_plugin_view_alter(array &$build, \Drupal\block\BlockInterface $block) {
+  if (isset($build['#content'])) {
+    foreach (element_children($build['#content']) as $key) {
+      $build['#content']['#contextual_links']['my_module'] = array('admin/structure/mymodule', array($build['#content'][$key]['some_value']));
+    }
+  }
+}
+
+/**
+ * Perform alterations to the content of a specific block plugin.
  *
- * Modules can implement hook_block_view_NAME_alter() to modify a specific block
- * instance, rather than implementing hook_block_view_alter().
+ * Modules can implement hook_block_plugin_view_NAME_alter() to modify a
+ * specific block plugin, rather than implementing
+ * hook_block_plugin_view_alter().
  *
  * @param array $build
  *   A renderable array of data, as returned from the build() implementation of
@@ -86,10 +104,11 @@ function hook_block_view_ID_alter(array &$build, \Drupal\block\BlockInterface $b
  *   example to illustrate what the block instance name will look like, and
  *   also illustrate how it is different from hook_block_view_ID().
  *
+ * @see hook_block_plugin_view_alter()
  * @see hook_block_view_alter()
  * @see hook_block_view_ID_alter()
  */
-function hook_block_view_NAME_alter(array &$build, \Drupal\block\BlockInterface $block) {
+function hook_block_plugin_view_NAME_alter(array &$build, \Drupal\block\BlockInterface $block) {
   // This code will only run for a specific block instance. For example, if NAME
   // in the function definition above is set to "someid", the code will only run
   // on the "someid" block.
@@ -99,14 +118,20 @@ function hook_block_view_NAME_alter(array &$build, \Drupal\block\BlockInterface
 }
 
 /**
- * Define access for a specific block instance.
+ * Define access for a block entity.
  *
  * This hook is invoked by the access methods of the block plugin system and
  * should be used to alter the block access rules defined by a module from
  * another module.
  *
  * @param \Drupal\block\Plugin\Core\Entity\Block $block
- *   The block instance.
+ *   The block entity.
+ * @param string $operation
+ *   The operation to be performed, e.g., 'view', 'create', 'delete', 'update'.
+ * @param \Drupal\user\Plugin\Core\Entity\User $account
+ *   The user object to perform the access check operation on.
+ * @param string $langcode
+ *   The language code to perform the access check operation on.
  *
  * @return bool
  *   TRUE will allow access whereas FALSE will deny access to the block.
@@ -114,7 +139,7 @@ function hook_block_view_NAME_alter(array &$build, \Drupal\block\BlockInterface
  * @see \Drupal\block\BlockBase::access()
  * @see \Drupal\block\BlockBase::blockAccess()
  */
-function hook_block_access(\Drupal\block\Plugin\Core\Entity\Block $block) {
+function hook_block_access(\Drupal\block\Plugin\Core\Entity\Block $block, $operation, \Drupal\user\Plugin\Core\Entity\User $account, $langcode) {
   // Example code that would prevent displaying the 'Powered by Drupal' block in
   // a region different than the footer.
   if ($block->get('plugin') == 'system_powered_by_block' && $block->get('region') != 'footer') {
@@ -123,5 +148,20 @@ function hook_block_access(\Drupal\block\Plugin\Core\Entity\Block $block) {
 }
 
 /**
+ * Allow or deny access to a specific block plugin.
+ *
+ * @param \Drupal\block\BlockInterface $block
+ *   The block plugin.
+ *
+ * @return bool
+ *   TRUE will allow access whereas FALSE will deny access to the block.
+ */
+function hook_block_plugin_access(\Drupal\block\BlockInterface $block) {
+  if ($block->getPluginID() == 'my_plugin_id') {
+    return FALSE;
+  }
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 42f22fa..db88ac5 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Component\Utility\NestedArray;
 
 /**
  * Denotes that a block is not enabled in any region and should not be shown.
@@ -88,6 +89,14 @@ function block_help($path, $arg) {
  */
 function block_theme() {
   return array(
+    'block_plugin' => array(
+      'variables' => array(
+        'content' => NULL,
+        'configuration' => NULL,
+        'plugin_id' => NULL,
+      ),
+      'template' => 'block-plugin',
+    ),
     'block' => array(
       'render element' => 'elements',
       'template' => 'block',
@@ -333,11 +342,10 @@ function _block_get_renderable_region($list = array()) {
       $id = array_pop($key_components);
       $build[$key] = array(
         '#block' => $block,
-        '#weight' => $block->get('weight'),
         '#theme_wrappers' => array('block'),
         '#pre_render' => array('_block_get_renderable_block'),
         '#cache' => array(
-          'keys' => array($id, $block->get('module')),
+          'keys' => array($id, $settings['module']),
           'granularity' => $settings['cache'],
           'bin' => 'block',
           'tags' => array('content' => TRUE),
@@ -350,8 +358,14 @@ 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 (!in_array($block->get('plugin'), array('system_help_block', 'system_main_block'))) {
+    if (isset($build[$key]) && !in_array($block->get('plugin'), array('system_help_block', 'system_main_block'))) {
       $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key));
+
+      // If there are any nested contextual links, move them to the top level.
+      if (isset($build[$key]['content']['#content']['#contextual_links'])) {
+        $build[$key]['#contextual_links'] += $build[$key]['content']['#content']['#contextual_links'];
+        unset($build[$key]['content']['#content']['#contextual_links']);
+      }
     }
   }
   return $build;
@@ -496,7 +510,7 @@ function _block_get_renderable_block($element) {
   $block = $element['#block'];
   // Don't bother to build blocks that aren't accessible.
   if ($element['#access'] = $block->access()) {
-    $element += entity_view($block, 'block');
+    $element = NestedArray::mergeDeep($element, entity_view($block, 'block'));
   }
   return $element;
 }
@@ -532,31 +546,26 @@ function block_rebuild() {
 function template_preprocess_block(&$variables) {
   $block_counter = &drupal_static(__FUNCTION__, array());
 
-  $variables['block'] = (object) $variables['elements']['#block_config'];
-  // If the block title is configured to be hidden, set it to an empty string.
-  if (empty($variables['elements']['#block']->label_display)) {
-    $variables['block']->label_hidden = $variables['block']->label;
-    $variables['block']->label = '';
-  }
-
   // All blocks get an independent counter for each region.
-  if (!isset($block_counter[$variables['block']->region])) {
-    $block_counter[$variables['block']->region] = 1;
+  $region = $variables['elements']['#block']->get('region');
+  if (!isset($block_counter[$region])) {
+    $block_counter[$region] = 1;
   }
   // Same with zebra striping.
-  $variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even';
-  $variables['block_id'] = $block_counter[$variables['block']->region]++;
+  $variables['block_zebra'] = ($block_counter[$region] % 2) ? 'odd' : 'even';
+  $variables['block_id'] = $block_counter[$region]++;
 
   // Create the $content variable that templates expect.
   $variables['content'] = $variables['elements']['#children'];
 
-  $variables['attributes']['class'][] = drupal_html_class('block-' . $variables['block']->module);
+  $settings = $variables['elements']['#block']->get('settings');
+  $variables['attributes']['class'][] = drupal_html_class('block-' . $settings['module']);
 
   // Add default class for block content.
   $variables['content_attributes']['class'][] = 'content';
 
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region;
-  $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module;
+  $variables['theme_hook_suggestions'][] = 'block__' . $region;
+  $variables['theme_hook_suggestions'][] = 'block__' . $settings['module'];
   // Hyphens (-) and underscores (_) play a special role in theme suggestions.
   // Theme suggestions should only contain underscores, because within
   // drupal_find_theme_templates(), underscores are converted to hyphens to
@@ -583,16 +592,33 @@ function template_preprocess_block(&$variables) {
 }
 
 /**
+ * Processes variables for block-plugin.tpl.php.
+ */
+function template_preprocess_block_plugin(&$variables) {
+  $configuration = $variables['configuration'];
+
+  // Add default class for block content.
+  $variables['content_attributes']['class'][] = 'content';
+
+  $variables['label'] = check_plain($configuration['label']);
+  if ($configuration['label_display'] != BLOCK_LABEL_VISIBLE) {
+    $variables['label_hidden'] = $variables['label'];
+    $variables['label'] = '';
+  }
+}
+
+/**
  * Implements hook_user_role_delete().
  *
  * Removes deleted role from blocks that use it.
  */
 function block_user_role_delete($role) {
   foreach (entity_load_multiple('block') as $block_id => $block) {
-    $visibility = $block->get('visibility');
+    $settings = $block->get('settings');
+    $visibility = $settings['visibility'];
     if (isset($visibility['roles']['roles'][$role->id()])) {
       unset($visibility['roles']['roles'][$role->id()]);
-      $block->set('visibility', $visibility);
+      $block->getPlugin()->setConfig('visibility', $visibility);
       $block->save();
     }
   }
@@ -629,10 +655,11 @@ function block_admin_paths() {
 function block_language_delete($language) {
   // Remove the block visibility settings for the deleted language.
   foreach (entity_load_multiple('block') as $block_id => $block) {
-    $visibility = $block->get('visibility');
+    $settings = $block->get('settings');
+    $visibility = $settings['visibility'];
     if (isset($visibility['language']['langcodes'][$language->langcode])) {
       unset($visibility['language']['langcodes'][$language->langcode]);
-      $block->set('visibility', $visibility);
+      $block->getPlugin()->setConfig('visibility', $visibility);
       $block->save();
     }
   }
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index a3ce18c..4244f02 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -8,7 +8,7 @@
 use Drupal\custom_block\Plugin\Core\Entity\CustomBlockType;
 use Drupal\custom_block\Plugin\Core\Entity\CustomBlock;
 use Drupal\custom_block\Plugin\block\block\CustomBlockBlock;
-use Drupal\block\Plugin\Core\Entity\Block;
+use Drupal\block\BlockInterface;
 
 /**
  * Implements hook_menu_local_tasks().
@@ -288,15 +288,3 @@ function custom_block_admin_paths() {
   );
   return $paths;
 }
-
-/**
- * Implements hook_block_view_alter().
- */
-function custom_block_block_view_alter(array &$build, Block $block) {
-  // Add contextual links for custom blocks.
-  if ($block->getPlugin() instanceof CustomBlockBlock) {
-    // Move contextual links from inner content to outer wrapper.
-    $build['#contextual_links']['custom_block'] = $build['content']['#contextual_links']['custom_block'];
-    unset($build['content']['#contextual_links']['custom_block']);
-  }
-}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Derivative/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Derivative/CustomBlock.php
index 592f150..71a50bd 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Derivative/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Derivative/CustomBlock.php
@@ -43,9 +43,6 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     $custom_blocks = entity_load_multiple('custom_block');
     foreach ($custom_blocks as $custom_block) {
       $this->derivatives[$custom_block->uuid->value] = $base_plugin_definition;
-      $this->derivatives[$custom_block->uuid->value]['settings'] = array(
-        'info' => $custom_block->info->value,
-      ) + $base_plugin_definition['settings'];
       $this->derivatives[$custom_block->uuid->value]['admin_label'] = $custom_block->info->value;
     }
     return $this->derivatives;
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlockBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlockBlock.php
index 9219264..cd398f0 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlockBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlockBlock.php
@@ -18,26 +18,20 @@
  *  id = "custom_block",
  *  admin_label = @Translation("Custom block"),
  *  module = "custom_block",
- *  derivative = "Drupal\custom_block\Plugin\Derivative\CustomBlock",
- *  settings = {
- *    "status" = TRUE,
- *    "info" = "",
- *    "view_mode" = "full"
- *   }
+ *  derivative = "Drupal\custom_block\Plugin\Derivative\CustomBlock"
  * )
  */
 class CustomBlockBlock extends BlockBase {
 
   /**
-   * Overrides \Drupal\block\BlockBase::getConfig().
+   * Overrides \Drupal\block\BlockBase::settings().
    */
-  public function getConfig() {
-    $definition = $this->getDefinition();
-    $this->configuration = parent::getConfig();
-    $this->configuration['status'] = $definition['settings']['status'];
-    $this->configuration['info'] = $definition['settings']['info'];
-    $this->configuration['view_mode'] = $definition['settings']['view_mode'];
-    return $this->configuration;
+  public function settings() {
+    return array(
+      'status' => TRUE,
+      'info' => '',
+      'view_mode' => 'full',
+    );
   }
 
   /**
diff --git a/core/modules/block/lib/Drupal/block/BlockAccessController.php b/core/modules/block/lib/Drupal/block/BlockAccessController.php
index 8e8a563..5aa8c6c 100644
--- a/core/modules/block/lib/Drupal/block/BlockAccessController.php
+++ b/core/modules/block/lib/Drupal/block/BlockAccessController.php
@@ -20,7 +20,17 @@ class BlockAccessController extends EntityAccessController {
    * Overrides \Drupal\Core\Entity\EntityAccessController::viewAccess().
    */
   public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
-    return $entity->getPlugin()->access();
+    // @todo \Drupal\Core\Entity\EntityAccessController::viewAccess() casts to a
+    //   Boolean, which incorrectly implies that access was always explicitly
+    //   granted or denied. To work around this call
+    //   \Drupal\Core\Entity\EntityAccessController::access() directly and check
+    //   the return type.
+    $access = $this->access($entity, $langcode, $account);
+    if ($access !== FALSE) {
+      // If entity access did not explicitly deny access, consult the plugin.
+      return $entity->getPlugin()->access();
+    }
+    return $access;
   }
 
 }
diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php
index 4de2a59..a31af4a 100644
--- a/core/modules/block/lib/Drupal/block/BlockBase.php
+++ b/core/modules/block/lib/Drupal/block/BlockBase.php
@@ -8,7 +8,6 @@
 namespace Drupal\block;
 
 use Drupal\Component\Plugin\PluginBase;
-use Drupal\block\Plugin\Core\Entity\Block;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 
 /**
@@ -20,20 +19,19 @@
  */
 abstract class BlockBase extends PluginBase implements BlockInterface {
 
-  /**
-   * The entity using this plugin.
-   *
-   * @var \Drupal\block\Plugin\Core\Entity\Block
-   */
-  protected $entity;
-
-  /**
-   * Overrides \Drupal\Component\Plugin\PluginBase::__construct().
-   */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, Block $entity) {
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
 
-    $this->entity = $entity;
+    $this->configuration += $this->settings() + array(
+      'label' => '',
+      'admin_label' => $plugin_definition['admin_label'],
+      'weight' => '',
+      'module' => $plugin_definition['module'],
+      'label_display' => BLOCK_LABEL_VISIBLE,
+      'visibility' => array(),
+      'cache' => DRUPAL_NO_CACHE,
+      'status' => TRUE,
+    );
   }
 
   /**
@@ -65,18 +63,6 @@ public function settings() {
    * @see \Drupal\Component\Plugin\PluginBase::$configuration
    */
   public function getConfig() {
-    if (empty($this->configuration)) {
-      // If the plugin configuration is not already set, initialize it with the
-      // default settings for the block plugin.
-      $this->configuration = $this->settings();
-
-      $definition = $this->getDefinition();
-      if (isset($definition['admin_label'])) {
-        $this->configuration += array('admin_label' => $definition['admin_label']);
-      }
-    }
-    // Ensure that the default cache mode is set.
-    $this->configuration += array('cache' => DRUPAL_NO_CACHE);
     return $this->configuration;
   }
 
@@ -142,7 +128,7 @@ public function access() {
     global $user;
 
     // Deny access to disabled blocks.
-    if (!$this->entity->get('status')) {
+    if (!$this->configuration['status']) {
       return FALSE;
     }
 
@@ -150,7 +136,7 @@ public function access() {
     // If a block has no roles associated, it is displayed for every role.
     // For blocks with roles associated, if none of the user's roles matches
     // the settings from this block, access is denied.
-    $visibility = $this->entity->get('visibility');
+    $visibility = $this->configuration['visibility'];
     if (!empty($visibility['role']['roles']) && !array_intersect(array_filter($visibility['role']['roles']), array_keys($user->roles))) {
       // No match.
       return FALSE;
@@ -201,8 +187,8 @@ public function access() {
     }
 
     // Check other modules for block access rules.
-    foreach (module_implements('block_access') as $module) {
-      if (module_invoke($module, 'block_access', $this->entity) === FALSE) {
+    foreach (module_implements('block_plugin_access') as $module) {
+      if (module_invoke($module, 'block_plugin_access', $this) === FALSE) {
         return FALSE;
       }
     }
@@ -222,12 +208,7 @@ public function access() {
    * @see \Drupal\block\BlockBase::blockForm()
    */
   public function form($form, &$form_state) {
-    $entity = $form_state['entity'];
     $definition = $this->getDefinition();
-    $form['id'] = array(
-      '#type' => 'value',
-      '#value' => $entity->id(),
-    );
     $form['module'] = array(
       '#type' => 'value',
       '#value' => $definition['module'],
@@ -237,38 +218,15 @@ public function form($form, &$form_state) {
       '#type' => 'textfield',
       '#title' => t('Title'),
       '#maxlength' => 255,
-      '#default_value' => !$entity->isNew() ? $entity->label() : $definition['admin_label'],
+      '#default_value' => !empty($this->configuration['label']) ? $this->configuration['label'] : $this->configuration['admin_label'],
       '#required' => TRUE,
     );
     $form['label_display'] = array(
       '#type' => 'checkbox',
       '#title' => t('Display title'),
-      '#default_value' => $entity->label_display == BLOCK_LABEL_VISIBLE ? TRUE : FALSE,
+      '#default_value' => $this->configuration['label_display'] == BLOCK_LABEL_VISIBLE,
       '#return_value' => BLOCK_LABEL_VISIBLE,
     );
-    $form['machine_name'] = array(
-      '#type' => 'machine_name',
-      '#title' => t('Machine name'),
-      '#maxlength' => 64,
-      '#description' => t('A unique name to save this block configuration. Must be alpha-numeric and be underscore separated.'),
-      '#default_value' => $entity->id(),
-      '#machine_name' => array(
-        'exists' => 'block_load',
-        'replace_pattern' => '[^a-z0-9_.]+',
-      ),
-      '#required' => TRUE,
-      '#disabled' => !$entity->isNew(),
-    );
-
-    // Region settings.
-    $form['region'] = array(
-      '#type' => 'select',
-      '#title' => t('Region'),
-      '#description' => t('Select the region where this block should be displayed.'),
-      '#default_value' => $entity->get('region'),
-      '#empty_value' => BLOCK_REGION_NONE,
-      '#options' => system_region_list($entity->get('theme'), REGIONS_VISIBLE),
-    );
 
     // Visibility settings.
     $form['visibility'] = array(
@@ -279,6 +237,7 @@ public function form($form, &$form_state) {
       ),
       '#tree' => TRUE,
       '#weight' => 10,
+      '#parents' => array('visibility'),
     );
 
     // Per-path visibility.
@@ -293,7 +252,7 @@ public function form($form, &$form_state) {
     // @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.
-    $visibility = $entity->get('visibility');
+    $visibility = $this->configuration['visibility'];
     $access = user_access('use PHP for settings');
     if (!empty($visibility['path']['visibility']) && $visibility['path']['visibility'] == BLOCK_VISIBILITY_PHP && !$access) {
       $form['visibility']['path']['visibility'] = array(
@@ -394,7 +353,7 @@ public function form($form, &$form_state) {
     );
 
     // Add plugin-specific settings for this block type.
-    $form['settings'] = $this->blockForm(array(), $form_state);
+    $form += $this->blockForm($form, $form_state);
     return $form;
   }
 
@@ -429,14 +388,12 @@ public function blockForm($form, &$form_state) {
    * @see \Drupal\block\BlockBase::blockValidate()
    */
   public function validate($form, &$form_state) {
-    if (!empty($form['machine_name']['#disabled'])) {
-      $config_id = explode('.', $form_state['values']['machine_name']);
-      $form_state['values']['machine_name'] = array_pop($config_id);
-    }
-    $form_state['values']['visibility']['role']['roles'] = array_filter($form_state['values']['visibility']['role']['roles']);
-    if ($form_state['entity']->isNew()) {
-      form_set_value($form['id'], $form_state['entity']->get('theme') . '.' . $form_state['values']['machine_name'], $form_state);
-    }
+    // @todo The final location of visibility is unresolved. This moves the
+    //   values into the plugin settings, out of the entity values.
+    $form_state['values']['settings']['visibility'] = $form_state['values']['visibility'];
+    unset($form_state['values']['visibility']);
+
+    $form_state['values']['settings']['visibility']['role']['roles'] = array_filter($form_state['values']['settings']['visibility']['role']['roles']);
     $this->blockValidate($form, $form_state);
   }
 
@@ -471,10 +428,7 @@ public function blockValidate($form, &$form_state) {}
   public function submit($form, &$form_state) {
     if (!form_get_errors()) {
       $this->blockSubmit($form, $form_state);
-
-      drupal_set_message(t('The block configuration has been saved.'));
-      cache_invalidate_tags(array('content' => TRUE));
-      $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $form_state['entity']->get('theme');
+      $this->configuration = $form_state['values']['settings'] + $this->configuration;
     }
   }
 
@@ -496,4 +450,20 @@ public function submit($form, &$form_state) {
    */
   public function blockSubmit($form, &$form_state) {}
 
+  public function blockBuild() {
+    $build = array();
+    $plugin_id = $this->getPluginId();
+    if ($content = $this->build()) {
+      $build = array(
+        '#theme' => 'block_plugin',
+        '#content' => $content,
+        '#configuration' => $this->configuration,
+        '#plugin_id' => $plugin_id,
+      );
+    }
+    list($name) = explode(':', $plugin_id . ':');
+    drupal_alter(array('block_plugin_view', "block_plugin_view_$name"), $build, $this);
+    return $build;
+  }
+
 }
diff --git a/core/modules/block/lib/Drupal/block/BlockFormController.php b/core/modules/block/lib/Drupal/block/BlockFormController.php
index 8e95248..6f95259 100644
--- a/core/modules/block/lib/Drupal/block/BlockFormController.php
+++ b/core/modules/block/lib/Drupal/block/BlockFormController.php
@@ -19,7 +19,38 @@ class BlockFormController extends EntityFormController {
    * Overrides \Drupal\Core\Entity\EntityFormController::form().
    */
   public function form(array $form, array &$form_state, EntityInterface $entity) {
-    return $entity->getPlugin()->form($form, $form_state);
+    $form['#tree'] = TRUE;
+    $form['id'] = array(
+      '#type' => 'value',
+      '#value' => $entity->id(),
+    );
+    $form['settings'] = $entity->getPlugin()->form(array(), $form_state);
+
+    $form['machine_name'] = array(
+      '#type' => 'machine_name',
+      '#title' => t('Machine name'),
+      '#maxlength' => 64,
+      '#description' => t('A unique name to save this block configuration. Must be alpha-numeric and be underscore separated.'),
+      '#default_value' => $entity->id(),
+      '#machine_name' => array(
+        'exists' => 'block_load',
+        'replace_pattern' => '[^a-z0-9_.]+',
+        'source' => array('settings', 'label'),
+      ),
+      '#required' => TRUE,
+      '#disabled' => !$entity->isNew(),
+    );
+
+    // Region settings.
+    $form['region'] = array(
+      '#type' => 'select',
+      '#title' => t('Region'),
+      '#description' => t('Select the region where this block should be displayed.'),
+      '#default_value' => $entity->get('region'),
+      '#empty_value' => BLOCK_REGION_NONE,
+      '#options' => system_region_list($entity->get('theme'), REGIONS_VISIBLE),
+    );
+    return $form;
   }
 
   /**
@@ -38,6 +69,13 @@ public function validate(array $form, array &$form_state) {
     parent::validate($form, $form_state);
 
     $entity = $this->getEntity($form_state);
+    if ($entity->isNew()) {
+      form_set_value($form['id'], $entity->get('theme') . '.' . $form_state['values']['machine_name'], $form_state);
+    }
+    if (!empty($form['machine_name']['#disabled'])) {
+      $config_id = explode('.', $form_state['values']['machine_name']);
+      $form_state['values']['machine_name'] = array_pop($config_id);
+    }
     $entity->getPlugin()->validate($form, $form_state);
   }
 
@@ -52,8 +90,11 @@ public function submit(array $form, array &$form_state) {
     $entity->getPlugin()->submit($form, $form_state);
 
     // Save the settings of the plugin.
-    $entity->set('settings', $entity->getPlugin()->getConfig());
     $entity->save();
+
+    drupal_set_message(t('The block configuration has been saved.'));
+    cache_invalidate_tags(array('content' => TRUE));
+    $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $entity->get('theme');
   }
 
 }
diff --git a/core/modules/block/lib/Drupal/block/BlockPluginBag.php b/core/modules/block/lib/Drupal/block/BlockPluginBag.php
new file mode 100644
index 0000000..73d50c0
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/BlockPluginBag.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\BlockPluginBag.
+ */
+
+namespace Drupal\block;
+
+use Drupal\block\Plugin\Core\Entity\Block;
+use Drupal\Component\Plugin\PluginBag;
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Component\Plugin\Exception\PluginException;
+
+/**
+ * Provides a collection of block plugins.
+ */
+class BlockPluginBag extends PluginBag {
+
+  /**
+   * The manager used to instantiate the plugins.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $manager;
+
+  /**
+   * Constructs a BlockPluginBag object.
+   *
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $manager
+   *   The manager to be used for instantiating plugins.
+   * @param array $instance_ids
+   *   The ids of the plugin instances with which we are dealing.
+   * @param Plugin\Core\Entity\Block $entity
+   *   The Block entity that holds our configuration.
+   */
+  public function __construct(PluginManagerInterface $manager, array $instance_ids, Block $entity) {
+    $this->manager = $manager;
+    $this->entity = $entity;
+
+    $this->instanceIDs = drupal_map_assoc($instance_ids);
+  }
+
+  /**
+   * Overrides \Drupal\Component\Plugin\PluginBag::initializePlugin().
+   */
+  protected function initializePlugin($instance_id) {
+    if (!$instance_id) {
+      throw new PluginException(format_string("The block '@block' did not specify a plugin.", array('@block' => $this->entity->id())));
+    }
+    if (isset($this->pluginInstances[$instance_id])) {
+      return;
+    }
+
+    $settings = $this->entity->get('settings');
+    try {
+      $this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, $settings);
+    }
+    catch (PluginException $e) {
+      $module = $settings['module'];
+      // Ignore blocks belonging to disabled modules, but re-throw valid
+      // exceptions when the module is enabled and the plugin is misconfigured.
+      if (!$module || module_exists($module)) {
+        throw $e;
+      }
+    }
+  }
+
+}
diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php
index e1c560e..ee2654c 100644
--- a/core/modules/block/lib/Drupal/block/BlockRenderController.php
+++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php
@@ -23,42 +23,6 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
   }
 
   /**
-   * Provides entity-specific defaults to the build process.
-   *
-   * @param Drupal\Core\Entity\EntityInterface $entity
-   *   The entity for which the defaults should be provided.
-   * @param string $view_mode
-   *   The view mode that should be used.
-   * @param string $langcode
-   *   (optional) For which language the entity should be prepared, defaults to
-   *   the current content language.
-   *
-   * @return array
-   *   An array of defaults to add into the entity render array.
-   */
-  protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
-    // @todo \Drupal\block\Tests\BlockTest::testCustomBlock() assuemes that a
-    //   block can be rendered without any of its wrappers. To do so, it uses a
-    //   custom view mode, and we choose to only add the wrappers on the default
-    //   view mode, 'block'.
-    if ($view_mode != 'block') {
-      return array();
-    }
-
-    return array(
-      '#block' => $entity,
-      '#weight' => $entity->get('weight'),
-      '#theme_wrappers' => array('block'),
-      '#block_config' => array(
-        'id' => $entity->get('plugin'),
-        'region' => $entity->get('region'),
-        'module' => $entity->get('module'),
-        'label' => check_plain($entity->label()),
-      ),
-    );
-  }
-
-  /**
    * Implements Drupal\Core\Entity\EntityRenderControllerInterface::view().
    */
   public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
@@ -73,15 +37,16 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
     $build = array();
     foreach ($entities as $entity_id => $entity) {
       // Allow blocks to be empty, do not add in the defaults.
-      if ($content = $entity->getPlugin()->build()) {
-        $build[$entity_id] = $this->getBuildDefaults($entity, $view_mode, $langcode);
+      $plugin = $entity->getPlugin();
+      if ($content = $plugin->blockBuild()) {
+        $build[$entity_id]['#block'] = $entity;
+        $build[$entity_id]['#theme_wrappers'][] = 'block';
       }
       $build[$entity_id]['content'] = $content;
 
       // All blocks, even when empty, should be available for altering.
-      $id = str_replace(':', '__', $entity->get('plugin'));
       list(, $name) = $entity->id();
-      drupal_alter(array('block_view', "block_view_$id", "block_view_$name"), $build[$entity_id], $entity);
+      drupal_alter(array('block_view', "block_view_$name"), $build[$entity_id], $plugin);
 
     }
     return $build;
diff --git a/core/modules/block/lib/Drupal/block/BlockStorageController.php b/core/modules/block/lib/Drupal/block/BlockStorageController.php
index a873c5d..a59854c 100644
--- a/core/modules/block/lib/Drupal/block/BlockStorageController.php
+++ b/core/modules/block/lib/Drupal/block/BlockStorageController.php
@@ -16,20 +16,6 @@
 class BlockStorageController extends ConfigStorageController {
 
   /**
-   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::create().
-   */
-  public function create(array $values) {
-    $entity = parent::create($values);
-
-    if (!$entity->get('module')) {
-      $definition = $entity->getPlugin()->getDefinition();
-      $entity->set('module', $definition['module']);
-    }
-
-    return $entity;
-  }
-
-  /**
    * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::load().
    */
   public function load(array $ids = NULL) {
@@ -53,4 +39,13 @@ public function loadByProperties(array $values = array()) {
     return $blocks;
   }
 
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::preSave().
+   */
+  protected function preSave(EntityInterface $entity) {
+    parent::preSave($entity);
+
+    $entity->set('settings', $entity->getPlugin()->getConfig());
+  }
+
 }
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
index 077a81e..c5736c1 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
@@ -10,7 +10,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
-use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\block\BlockPluginBag;
 
 /**
  * Defines a Block configuration entity class.
@@ -53,17 +53,6 @@ class Block extends ConfigEntityBase {
   public $label;
 
   /**
-   * Whether the block label is displayed to end users.
-   *
-   * When this is set to BLOCK_LABEL_VISIBLE (the default value), the label is
-   * rendered as header in the block markup. Otherwise, the label is passed
-   * to the block template as a separate $label_hidden variable.
-   *
-   * @var string
-   */
-  public $label_display = BLOCK_LABEL_VISIBLE;
-
-  /**
    * The block UUID.
    *
    * @var string
@@ -78,13 +67,6 @@ class Block extends ConfigEntityBase {
   protected $settings = array();
 
   /**
-   * The plugin instance.
-   *
-   * @var \Drupal\block\BlockInterface
-   */
-  protected $instance;
-
-  /**
    * The region this block is placed in.
    *
    * @var string
@@ -92,32 +74,27 @@ class Block extends ConfigEntityBase {
   protected $region = BLOCK_REGION_NONE;
 
   /**
-   * Settings to control the block visibility.
+   * The plugin instance ID.
    *
-   * @var array
+   * @var string
    */
-  protected $visibility = array();
+  protected $plugin;
 
   /**
-   * The weight of the block.
+   * The plugin bag that holds the block plugin for this entity.
    *
-   * @var int
+   * @var \Drupal\block\BlockPluginBag
    */
-  protected $weight;
+  protected $pluginBag;
 
   /**
-   * The module owning this plugin.
-   *
-   * @var string
+   * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::__construct();
    */
-  protected $module;
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
 
-  /**
-   * The plugin instance ID.
-   *
-   * @var string
-   */
-  protected $plugin;
+    $this->pluginBag = new BlockPluginBag(\Drupal::service('plugin.manager.block'), array($this->plugin), $this);
+  }
 
   /**
    * Returns the plugin instance.
@@ -126,26 +103,7 @@ class Block extends ConfigEntityBase {
    *   The plugin instance for this block.
    */
   public function getPlugin() {
-    if (!$this->instance) {
-      // Throw an exception if no plugin string was provided.
-      if (!$this->plugin) {
-        throw new PluginException(format_string("The block '@block' did not specify a plugin.", array('@block' => $this->id())));
-      }
-
-      // Create a plugin instance and store its configuration as settings.
-      try {
-        $this->instance = drupal_container()->get('plugin.manager.block')->createInstance($this->plugin, $this->settings, $this);
-        $this->settings += $this->instance->getConfig();
-      }
-      catch (PluginException $e) {
-        // Ignore blocks belonging to disabled modules, but re-throw valid
-        // exceptions when the module is enabled and the plugin is misconfigured.
-        if (empty($this->module) || module_exists($this->module)) {
-          throw $e;
-        }
-      }
-    }
-    return $this->instance;
+    return $this->pluginBag->get($this->plugin);
   }
 
   /**
@@ -162,6 +120,14 @@ public function uri() {
   }
 
   /**
+   * Overrides \Drupal\Core\Entity\Entity::label();
+   */
+  public function label($langcode = NULL) {
+    $settings = $this->get('settings');
+    return $settings['label'];
+  }
+
+  /**
    * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::get();
    */
   public function get($property_name, $langcode = NULL) {
@@ -177,21 +143,12 @@ public function get($property_name, $langcode = NULL) {
    * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::getExportProperties();
    */
   public function getExportProperties() {
+    $properties = parent::getExportProperties();
     $names = array(
-      'id',
-      'label',
-      'label_display',
-      'uuid',
       'region',
-      'weight',
-      'module',
-      'status',
-      'visibility',
       'plugin',
       'settings',
-      'langcode',
     );
-    $properties = array();
     foreach ($names as $name) {
       $properties[$name] = $this->get($name);
     }
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
index b102d4b..9ff342b 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
@@ -13,7 +13,6 @@
 use Drupal\Core\Plugin\Discovery\AlterDecorator;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\block\Plugin\Core\Entity\Block;
 
 /**
  * Manages discovery and instantiation of block plugins.
@@ -35,15 +34,8 @@ public function __construct(array $namespaces) {
     $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
     $this->discovery = new AlterDecorator($this->discovery, 'block');
     $this->discovery = new CacheDecorator($this->discovery, 'block_plugins:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'block', CacheBackendInterface::CACHE_PERMANENT, array('block'));
-  }
 
-  /**
-   * Overrides \Drupal\Component\Plugin\PluginManagerBase::createInstance().
-   */
-  public function createInstance($plugin_id, array $configuration = array(), Block $entity = NULL) {
-    $plugin_definition = $this->discovery->getDefinition($plugin_id);
-    $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
-    return new $plugin_class($configuration, $plugin_id, $plugin_definition, $entity);
+    $this->factory = new DefaultFactory($this->discovery);
   }
 
 }
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
index 96f9343..46454a6 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php
@@ -192,9 +192,7 @@ function testCachePerPage() {
    * Private helper method to set the test block's cache mode.
    */
   private function setCacheMode($cache_mode) {
-    $settings = $this->block->get('settings');
-    $settings['cache'] = $cache_mode;
-    $this->block->set('settings', $settings);
+    $this->block->getPlugin()->setConfig('cache', $cache_mode);
     $this->block->save();
   }
 
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php
new file mode 100644
index 0000000..bd332ad
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php
@@ -0,0 +1,219 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Tests\BlockInterfaceTest.
+ */
+
+namespace Drupal\block\Tests;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Test BlockInterface methods to ensure no external dependencies exist.
+ */
+class BlockInterfaceTest extends DrupalUnitTestBase {
+  public static $modules = array('system', 'user', 'block');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Block Plugins Tests',
+      'description' => 'Tests that the block plugin can work properly without a supporting entity.',
+      'group' => 'Block',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    $this->installSchema('user', 'users');
+  }
+
+  /**
+   * Test configuration and subsequent form() and build() method calls.
+   *
+   * This test is attempting to ensure the existing block plugin api and all
+   * functionality that is expected to remain consistent. The arrays that are
+   * used for comparison can change, but only to include elements that are
+   * contained within BlockBase or the plugin being tested. Likely these
+   * comparison arrays should get smaller, not larger, as more form/build
+   * elements are moved into a more suitably responsible class.
+   */
+  public function testBlockInterface() {
+    $manager = \Drupal::service('plugin.manager.block');
+    $configuration = array(
+      'label' => 'Users Currently Online',
+      'seconds_online' => 450,
+      'max_list_count' => 5,
+    );
+    $expected_configuration = array(
+      'label' => 'Users Currently Online',
+      'seconds_online' => 450,
+      'max_list_count' => 5,
+      'properties' =>
+      array (
+        'administrative' => TRUE,
+      ),
+      'admin_label' => 'Who\'s online',
+      'weight' => '',
+      'module' => 'user',
+      'label_display' => BLOCK_LABEL_VISIBLE,
+      'visibility' =>
+      array (
+      ),
+      'cache' => DRUPAL_NO_CACHE,
+      'status' => TRUE,
+    );
+    // Initial configuration of the block at construction time.
+    $user_online_block = $manager->createInstance('user_online_block', $configuration);
+    $this->assertIdentical($user_online_block->getConfig(), $expected_configuration, 'The block was configured correctly.');
+
+    // Updating an element of the configuration.
+    $user_online_block->setConfig('max_list_count', 6);
+    $expected_configuration['max_list_count'] = 6;
+    $this->assertIdentical($user_online_block->getConfig(), $expected_configuration, 'The block configuration was updated correctly.');
+
+    $expected_form = array(
+      'module' => array(
+        '#type' => 'value',
+        '#value' => 'user',
+      ),
+      'label' => array(
+        '#type' => 'textfield',
+        '#title' => 'Title',
+        '#maxlength' => 255,
+        '#default_value' => 'Users Currently Online',
+        '#required' => TRUE,
+      ),
+      'label_display' => array(
+        '#type' => 'checkbox',
+        '#title' => 'Display title',
+        '#default_value' => TRUE,
+        '#return_value' => 'visible',
+      ),
+      'visibility' => array(
+        '#type' => 'vertical_tabs',
+        '#title' => 'Visibility settings',
+        '#attached' => array(
+          'js' => array(
+            0 => 'core/modules/block/block.js',
+          ),
+        ),
+        '#tree' => TRUE,
+        '#weight' => 10,
+        '#parents' => array(
+          0 => 'visibility',
+        ),
+        'path' => array(
+          '#type' => 'details',
+          '#title' => 'Pages',
+          '#collapsed' => TRUE,
+          '#group' => 'visibility',
+          '#weight' => 0,
+          'visibility' => array(
+            '#type' => 'radios',
+            '#title' => 'Show block on specific pages',
+            '#options' => array(
+              0 => 'All pages except those listed',
+              1 => 'Only the listed pages',
+            ),
+            '#default_value' => 0,
+          ),
+          'pages' => array(
+            '#type' => 'textarea',
+            '#title' => '<span class="element-invisible">Pages</span>',
+            '#default_value' => '',
+            '#description' => 'Specify pages by using their paths. Enter one path per line. The \'*\' character is a wildcard. Example paths are <em class="placeholder">user</em> for the current user\'s page and <em class="placeholder">user/*</em> for every user page. <em class="placeholder">&lt;front&gt;</em> is the front page.',
+          ),
+        ),
+        'role' => array(
+          '#type' => 'details',
+          '#title' => 'Roles',
+          '#collapsed' => TRUE,
+          '#group' => 'visibility',
+          '#weight' => 10,
+          'roles' => array(
+            '#type' => 'checkboxes',
+            '#title' => 'Show block for specific roles',
+            '#default_value' => array(
+            ),
+            '#options' => array(
+            ),
+            '#description' => 'Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.',
+          ),
+        ),
+      ),
+      'user_block_seconds_online' => array(
+        '#type' => 'select',
+        '#title' => 'User activity',
+        '#default_value' => 450,
+        '#options' => array(
+          30 => '30 sec',
+          60 => '1 min',
+          120 => '2 min',
+          180 => '3 min',
+          300 => '5 min',
+          600 => '10 min',
+          900 => '15 min',
+          1800 => '30 min',
+          2700 => '45 min',
+          3600 => '1 hour',
+          5400 => '1 hour 30 min',
+          7200 => '2 hours',
+          10800 => '3 hours',
+          21600 => '6 hours',
+          43200 => '12 hours',
+          86400 => '1 day',
+        ),
+        '#description' => 'A user is considered online for this long after they have last viewed a page.',
+      ),
+      'user_block_max_list_count' => array(
+        '#type' => 'select',
+        '#title' => 'User list length',
+        '#default_value' => 6,
+        '#options' => array(
+          0 => 0,
+          5 => 5,
+          10 => 10,
+          15 => 15,
+          20 => 20,
+          25 => 25,
+          30 => 30,
+          40 => 40,
+          50 => 50,
+          75 => 75,
+          100 => 100,
+        ),
+        '#description' => 'Maximum number of currently online users to display.',
+      ),
+    );
+    $form_state = array();
+    // Ensure there are no form elements that do not belong to the plugin.
+    $this->assertIdentical($user_online_block->form(array(), $form_state), $expected_form, 'Only the expected form elements were present.');
+
+    $expected_build = array(
+      '#theme' => 'block_plugin',
+      '#content' => array(
+        '#theme' => 'item_list__user__online',
+        '#prefix' => '<p>There are currently 0 users online.</p>',
+      ),
+      '#configuration' => array(
+        'label' => 'Users Currently Online',
+        'seconds_online' => 450,
+        'max_list_count' => 6,
+        'properties' => array(
+          'administrative' => TRUE,
+        ),
+        'admin_label' => 'Who\'s online',
+        'weight' => '',
+        'module' => 'user',
+        'label_display' => BLOCK_LABEL_VISIBLE,
+        'visibility' => array(),
+        'cache' => DRUPAL_NO_CACHE,
+        'status' => TRUE,
+      ),
+      '#plugin_id' => 'user_online_block',
+    );
+    // Ensure the build array is proper.
+    $this->assertIdentical($user_online_block->blockBuild(), $expected_build, 'The plugin returned the appropriate build array.');
+  }
+}
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
index f247648..685448d 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
@@ -104,8 +104,8 @@ public function testLanguageBlockVisibilityLanguageDelete() {
     $block = $this->drupalPlaceBlock('system_powered_by_block', $edit);
 
     // Check that we have the language in config after saving the setting.
-    $visibility = $block->get('visibility');
-    $setting = $visibility['language']['langcodes']['fr'];
+    $settings = $block->get('settings');
+    $setting = $settings['visibility']['language']['langcodes']['fr'];
     $this->assertTrue('fr' === $setting, 'Language is set in the block configuration.');
 
     // Delete the language.
@@ -114,8 +114,8 @@ public function testLanguageBlockVisibilityLanguageDelete() {
     // Check that the language is no longer stored in the configuration after
     // it is deleted.
     $block = entity_load('block', $block->id());
-    $visibility = $block->get('visibility');
-    $this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.');
+    $settings = $block->get('settings');
+    $this->assertTrue(empty($settings['visibility']['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.');
   }
 
 }
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
index 490e434..8eef916 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
@@ -69,6 +69,7 @@ protected function createTests() {
     // Attempt to create a block without a plugin.
     try {
       $entity = $this->controller->create(array());
+      $entity->getPlugin();
       $this->fail('A block without a plugin was created with no exception thrown.');
     }
     catch (PluginException $e) {
@@ -93,18 +94,20 @@ protected function createTests() {
     $expected_properties = array(
       'id' => 'stark.test_block',
       'label' => '',
-      'label_display' => BLOCK_LABEL_VISIBLE,
-      'region' => '-1',
-      'weight' => '',
-      'module' => 'block_test',
       'status' => '1',
-      'visibility' => array(),
+      'langcode' => LANGUAGE_NOT_SPECIFIED,
+      'region' => '-1',
       'plugin' => 'test_html_id',
       'settings' => array(
         'cache' => '1',
+        'label' => '',
         'admin_label' => t('Test block html id'),
+        'weight' => '',
+        'module' => 'block_test',
+        'label_display' => BLOCK_LABEL_VISIBLE,
+        'visibility' => array(),
+        'status' => '1',
       ),
-      'langcode' => LANGUAGE_NOT_SPECIFIED,
     );
     $this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.');
 
@@ -124,7 +127,6 @@ protected function loadTests() {
     $this->assertEqual($entity->get('region'), '-1');
     $this->assertTrue($entity->get('status'));
     $this->assertEqual($entity->get('theme'), 'stark');
-    $this->assertEqual($entity->get('module'), 'block_test');
     $this->assertTrue($entity->uuid());
   }
 
@@ -132,19 +134,14 @@ protected function loadTests() {
    * Tests the rendering of blocks.
    */
   protected function renderTests() {
-    $entity = $this->controller->create(array(
-      'id' => 'stark.test_block',
-      'plugin' => 'test_html_id',
-    ));
-
     // Test the rendering of a block.
+    $entity = entity_load('block', 'stark.test_block');
     $output = entity_view($entity, 'block');
     $expected = array();
     $expected[] = '  <div id="block-test-block"  class="block block-block-test">';
-    $expected[] = '';
-    $expected[] = '    ';
-    $expected[] = '  <div class="content">';
-    $expected[] = '      </div>';
+    $expected[] = '      ';
+    $expected[] = '<div class="content">';
+    $expected[] = '  </div>';
     $expected[] = '</div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
@@ -154,19 +151,27 @@ protected function renderTests() {
     drupal_static_reset('drupal_html_id');
 
     // Test the rendering of a block with a given title.
-    $entity->set('label', 'Powered by Bananas');
+    $entity = $this->controller->create(array(
+      'id' => 'stark.test_block2',
+      'plugin' => 'test_html_id',
+      'settings' => array(
+        'label' => 'Powered by Bananas',
+      ),
+    ));
+    $entity->save();
     $output = entity_view($entity, 'block');
     $expected = array();
-    $expected[] = '  <div id="block-test-block"  class="block block-block-test">';
+    $expected[] = '  <div id="block-test-block2"  class="block block-block-test">';
+    $expected[] = '        <h2 class="">Powered by Bananas</h2>';
     $expected[] = '';
-    $expected[] = '    <h2 class="">Powered by Bananas</h2>';
-    $expected[] = '  ';
-    $expected[] = '  <div class="content">';
-    $expected[] = '      </div>';
+    $expected[] = '<div class="content">';
+    $expected[] = '  </div>';
     $expected[] = '</div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
     $this->assertEqual(drupal_render($output), $expected_output, 'The block rendered correctly.');
+    // Clean up this entity.
+    $entity->delete();
   }
 
   /**
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
index d707c80..0e474a9 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTemplateSuggestionsUnitTest.php
@@ -42,14 +42,9 @@ function testBlockThemeHookSuggestions() {
       'region' => 'footer',
       'id' => config('system.theme')->get('default') . '.machinename',
     ));
+    $block->save();
 
-    $variables = array();
     $variables['elements']['#block'] = $block;
-    $variables['elements']['#block_config'] = $block->getPlugin()->getConfig() + array(
-      'id' => $block->get('plugin'),
-      'region' => $block->get('region'),
-      'module' => $block->get('module'),
-    );
     $variables['elements']['#children'] = '';
     // Test adding a class to the block content.
     $variables['content_attributes']['class'][] = 'test-class';
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
index b746b21..553f464 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
@@ -34,7 +34,7 @@ function testBlockVisibility() {
     $edit = array(
       'machine_name' => strtolower($this->randomName(8)),
       'region' => 'sidebar_first',
-      'label' => $title,
+      'settings[label]' => $title,
     );
     // Set the block to be hidden on any user path, and to be shown only to
     // authenticated users.
@@ -74,7 +74,7 @@ function testBlockVisibilityListedEmpty() {
     $edit = array(
       'machine_name' => strtolower($this->randomName(8)),
       'region' => 'sidebar_first',
-      'label' => $title,
+      'settings[label]' => $title,
       'visibility[path][visibility]' => BLOCK_VISIBILITY_LISTED,
     );
     // Set the block to be hidden on any user path, and to be shown only to
@@ -101,18 +101,18 @@ function testBlock() {
     // Select the 'Powered by Drupal' block to be configured and moved.
     $block = array();
     $block['id'] = 'system_powered_by_block';
-    $block['label'] = $this->randomName(8);
+    $block['settings[label]'] = $this->randomName(8);
     $block['machine_name'] = strtolower($this->randomName(8));
     $block['theme'] = config('system.theme')->get('default');
     $block['region'] = 'header';
 
     // Set block title to confirm that interface works and override any custom titles.
-    $this->drupalPost('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], array('label' => $block['label'], 'machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
+    $this->drupalPost('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], array('settings[label]' => $block['settings[label]'], 'machine_name' => $block['machine_name'], 'region' => $block['region']), t('Save block'));
     $this->assertText(t('The block configuration has been saved.'), 'Block title set.');
     // Check to see if the block was created by checking its configuration.
     $instance = entity_load('block', $block['theme'] . '.' . $block['machine_name']);
 
-    $this->assertEqual($instance->label(), $block['label'], 'Stored block title found.');
+    $this->assertEqual($instance->label(), $block['settings[label]'], 'Stored block title found.');
 
     // Check whether the block can be moved to all available regions.
     foreach ($this->regions as $region) {
@@ -129,7 +129,7 @@ function testBlock() {
 
     // Confirm that the block instance title and markup are not displayed.
     $this->drupalGet('node');
-    $this->assertNoText(t($block['label']));
+    $this->assertNoText(t($block['settings[label]']));
     // Check for <div id="block-my-block-instance-name"> if the machine name
     // is my_block_instance_name.
     $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-' . strtr(strtolower($block['machine_name']), '-', '_')));
@@ -149,7 +149,7 @@ function testHideBlockTitle() {
     $edit = array(
       'machine_name' => $machine_name,
       'region' => 'sidebar_first',
-      'label' => $title,
+      'settings[label]' => $title,
     );
     $this->drupalPost('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
     $this->assertText('The block configuration has been saved.', 'Block was saved');
@@ -158,7 +158,7 @@ function testHideBlockTitle() {
     $this->assertText($title, 'Block title was displayed by default.');
 
     $edit = array(
-      'label_display' => FALSE,
+      'settings[label_display]' => FALSE,
     );
     $this->drupalPost('admin/structure/block/manage/' . $default_theme . '.' . $machine_name . '/configure', $edit, t('Save block'));
     $this->assertText('The block configuration has been saved.', 'Block was saved');
@@ -191,7 +191,7 @@ function moveBlockToRegion(array $block, $region) {
 
     // Confirm that the block is being displayed.
     $this->drupalGet('');
-    $this->assertText(t($block['label']), 'Block successfully being displayed on the page.');
+    $this->assertText(t($block['settings[label]']), 'Block successfully being displayed on the page.');
 
     // Confirm that the custom block was found at the proper region.
     $xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
@@ -224,8 +224,7 @@ function testBlockRehash() {
     $this->assertEqual($settings['cache'], DRUPAL_CACHE_PER_ROLE, 'Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.');
 
     // Disable caching for this block.
-    $settings['cache'] = DRUPAL_NO_CACHE;
-    $block->set('settings', $settings);
+    $block->getPlugin()->setConfig('cache', DRUPAL_NO_CACHE);
     $block->save();
     // Flushing all caches should call _block_rehash().
     $this->resetAll();
@@ -289,13 +288,13 @@ function testBlockModuleDisable() {
     // Emulate a POST submission rather than using drupalPlaceBlock() to ensure
     // that the form still functions as expected.
     $edit = array(
-      'label' => $this->randomName(8),
+      'settings[label]' => $this->randomName(8),
       'machine_name' => strtolower($this->randomName(8)),
       'region' => 'sidebar_first',
     );
     $this->drupalPost('admin/structure/block/add/system_powered_by_block/stark', $edit, t('Save block'));
     $this->assertText(t('The block configuration has been saved.'));
-    $this->assertText($edit['label']);
+    $this->assertText($edit['settings[label]']);
 
     // Update the weight of a block.
     $edit = array('blocks[stark.' . $edit['machine_name'] . '][weight]' => -1);
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php
index b04f9fc..7ac3648 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTitleXSSTest.php
@@ -32,10 +32,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->drupalPlaceBlock('test_xss_title', array(
-      'label' => '<script>alert("XSS label");</script>',
-      'machine_name' => 'test_xss_block',
-    ));
+    $this->drupalPlaceBlock('test_xss_title', array('label' => '<script>alert("XSS label");</script>'));
   }
 
   /**
diff --git a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
index 5f3b776..e6842d0 100644
--- a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php
@@ -90,13 +90,13 @@ protected function findBlockInstance(Block $block) {
   protected function testDeleteBlockDisplay() {
     // To test all combinations possible we first place create two instances
     // of the block display of the first view.
-    $block_1 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array(), array('title' => 'test_view_block-block_1:1'));
-    $block_2 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array(), array('title' => 'test_view_block-block_1:2'));
+    $block_1 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('title' => 'test_view_block-block_1:1'));
+    $block_2 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('title' => 'test_view_block-block_1:2'));
 
     // Then we add one instance of blocks for each of the two displays of the
     // second view.
-    $block_3 = $this->drupalPlaceBlock('views_block:test_view_block2-block_1', array(), array('title' => 'test_view_block2-block_1'));
-    $block_4 = $this->drupalPlaceBlock('views_block:test_view_block2-block_2', array(), array('title' => 'test_view_block2-block_2'));
+    $block_3 = $this->drupalPlaceBlock('views_block:test_view_block2-block_1', array('title' => 'test_view_block2-block_1'));
+    $block_4 = $this->drupalPlaceBlock('views_block:test_view_block2-block_2', array('title' => 'test_view_block2-block_2'));
 
     $this->drupalGet('test-page');
     $this->assertBlockAppears($block_1);
diff --git a/core/modules/block/templates/block-plugin.tpl.php b/core/modules/block/templates/block-plugin.tpl.php
new file mode 100644
index 0000000..5e4eb92
--- /dev/null
+++ b/core/modules/block/templates/block-plugin.tpl.php
@@ -0,0 +1,9 @@
+<?php print render($title_prefix); ?>
+<?php if ($label): ?>
+  <h2<?php print $title_attributes; ?>><?php print $label; ?></h2>
+<?php endif;?>
+<?php print render($title_suffix); ?>
+
+<div<?php print $content_attributes; ?>>
+  <?php print render($content); ?>
+</div>
diff --git a/core/modules/block/templates/block.tpl.php b/core/modules/block/templates/block.tpl.php
index 00b554a..d41c7d9 100644
--- a/core/modules/block/templates/block.tpl.php
+++ b/core/modules/block/templates/block.tpl.php
@@ -5,13 +5,6 @@
  * Default theme implementation to display a block.
  *
  * Available variables:
- * - $block->label: Block title.
- * - $block->label_hidden: The hidden block title value if the block was
- *    configured to hide the title ($block->label is empty in this case).
- * - $content: Block content.
- * - $block->module: Module that generated the block.
- * - $block->delta: An ID for the block, unique within each module.
- * - $block->region: The block region embedding the current block.
  * - $attributes: An instance of Attributes class that can be manipulated as an
  *    array and printed as a string.
  *    It includes the 'class' information, which includes:
@@ -48,14 +41,7 @@
 <?php else: ?>
   <div <?php print $attributes; ?>>
 <?php endif; ?>
-
   <?php print render($title_prefix); ?>
-<?php if ($block->label): ?>
-  <h2<?php print $title_attributes; ?>><?php print $block->label; ?></h2>
-<?php endif;?>
   <?php print render($title_suffix); ?>
-
-  <div<?php print $content_attributes; ?>>
-    <?php print $content ?>
-  </div>
+  <?php print $content ?>
 </div>
diff --git a/core/modules/block/tests/config/block.block.stark.test_block.yml b/core/modules/block/tests/config/block.block.stark.test_block.yml
index 59ba7b4..fb6856c 100644
--- a/core/modules/block/tests/config/block.block.stark.test_block.yml
+++ b/core/modules/block/tests/config/block.block.stark.test_block.yml
@@ -1,11 +1,11 @@
 id: stark.test_block
-label: ''
 region: '-1'
-weight: ''
-module: block_test
-status: '1'
-visibility: {  }
 plugin: test_html_id
 settings:
+  label: ''
+  weight: ''
+  module: block_test
+  status: '1'
+  visibility: {  }
   cache: '1'
   admin_label: 'Test block html id'
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 26f324e..fee74c0 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -962,10 +962,10 @@ function _book_link_defaults($nid) {
 }
 
  /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function book_preprocess_block(&$variables) {
-  if ($variables['block']-> module == 'book') {
+function book_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'book') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/book/lib/Drupal/book/Tests/BookTest.php b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
index 4601342..1385126 100644
--- a/core/modules/book/lib/Drupal/book/Tests/BookTest.php
+++ b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
@@ -338,7 +338,7 @@ function testBookNavigationBlock() {
    */
   function testNavigationBlockOnAccessModuleEnabled() {
     $this->drupalLogin($this->admin_user);
-    $block = $this->drupalPlaceBlock('book_navigation', array(), array('block_mode' => 'book pages'));
+    $block = $this->drupalPlaceBlock('book_navigation', array('block_mode' => 'book pages'));
 
     // Give anonymous users the permission 'node test view'.
     $edit = array();
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index dd87b29..0ca643f 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1588,10 +1588,10 @@ function comment_preview(Comment $comment) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function comment_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'comment') {
+function comment_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'comment') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
index 901d325..89f81b1 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
@@ -46,7 +46,7 @@ public static function getInfo() {
    */
   function testRecentCommentBlock() {
     $this->drupalLogin($this->admin_user);
-    $block = $this->drupalPlaceBlock('recent_comments', array(), array('block_count' => 2));
+    $block = $this->drupalPlaceBlock('recent_comments', array('block_count' => 2));
 
     // Add some test comments, one without a subject.
     $comment1 = $this->postComment($this->node, $this->randomName(), $this->randomName());
@@ -78,7 +78,7 @@ function testRecentCommentBlock() {
     $this->assertTrue(strpos($this->drupalGetContent(), $comment3->comment_body->value) < strpos($this->drupalGetContent(), $comment2->subject->value), 'Comments were ordered correctly in block.');
 
     // Set the number of recent comments to show to 10.
-    $block->set('settings', array('block_count' => 10));
+    $block->getPlugin()->setConfig('block_count', 10);
     $block->save();
 
     // Post an additional comment.
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 16eadd8..8a544b2 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -956,10 +956,10 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function forum_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'forum') {
+function forum_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'forum') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
index 02314e8..65dd7c2 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
@@ -71,7 +71,7 @@ public function testNewForumTopicsBlock() {
     }
 
     // Configure the new forum topics block to only show 2 topics.
-    $block->set('settings', array('block_count' => 2));
+    $block->getPlugin()->setConfig('block_count', 2);
     $block->save();
 
     $this->drupalGet('');
@@ -132,7 +132,7 @@ public function testActiveForumTopicsBlock() {
     }
 
     // Configure the active forum block to only show 2 topics.
-    $block->set('settings', array('block_count' => 2));
+    $block->getPlugin()->setConfig('block_count', 2);
     $block->save();
 
     $this->drupalGet('');
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index aac8636..38fd24e 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -68,10 +68,10 @@ function help_help($path, $arg) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function help_preprocess_block(&$variables) {
-  if ($variables['block']->id == 'system_help_block') {
+function help_preprocess_block_plugin(&$variables) {
+  if ($variables['plugin_id'] == 'system_help_block') {
     $variables['attributes']['role'] = 'complementary';
   }
 }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 6bbb5a7..d1acea5 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -760,10 +760,10 @@ function language_language_delete($language) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function language_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'language') {
+function language_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'language') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index addca0f..48027a6 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -12,7 +12,7 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\block\Plugin\Core\Entity\Block;
+use Drupal\block\BlockInterface;
 use Drupal\system\Plugin\Core\Entity\Menu;
 use Drupal\system\Plugin\block\block\SystemMenuBlock;
 use Symfony\Component\HttpFoundation\JsonResponse;
@@ -408,13 +408,13 @@ function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude,
 }
 
 /**
- * Implements hook_block_view_alter().
+ * Implements hook_block_plugin_view_NAME_alter() for 'system_menu_block'.
  */
-function menu_block_view_alter(array &$build, Block $block) {
+function menu_block_plugin_view_system_menu_block_alter(array &$build, BlockInterface $block) {
   // Add contextual links for system menu blocks.
-  if ($block->getPlugin() instanceof SystemMenuBlock) {
-    foreach (element_children($build['content']) as $key) {
-      $build['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($build['content'][$key]['#original_link']['menu_name']));
+  if (isset($build['#content'])) {
+    foreach (element_children($build['#content']) as $key) {
+      $build['#content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($build['#content'][$key]['#original_link']['menu_name']));
     }
   }
 }
@@ -723,10 +723,10 @@ function menu_get_menus($all = TRUE) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function menu_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'menu') {
+function menu_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'menu') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php
index 3beb969..ea2f7b3 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeBlockFunctionalTest.php
@@ -61,7 +61,7 @@ public function testRecentNodeBlock() {
     ));
 
     // Enable the recent content block with two items.
-    $block = $this->drupalPlaceBlock('node_recent_block', array('machine_name' => 'test_block'), array('block_count' => 2));
+    $block = $this->drupalPlaceBlock('node_recent_block', array('machine_name' => 'test_block', 'block_count' => 2));
 
     // Test that block is not visible without nodes.
     $this->drupalGet('');
@@ -106,7 +106,7 @@ public function testRecentNodeBlock() {
     $this->drupalLogin($this->adminUser);
 
     // Set the number of recent nodes to show to 10.
-    $block->set('settings', array('block_count' => 10));
+    $block->getPlugin()->setConfig('block_count', 10);
     $block->save();
 
     // Post an additional node.
@@ -132,8 +132,8 @@ public function testRecentNodeBlock() {
         ),
       ),
     ));
-    $visibility = $block->get('visibility');
-    $this->assertTrue(isset($visibility['node_type']['types']['article']), 'Visibility settings were saved to configuration');
+    $settings = $block->get('settings');
+    $this->assertTrue(isset($settings['visibility']['node_type']['types']['article']), 'Visibility settings were saved to configuration');
 
     // Create a page node.
     $node5 = $this->drupalCreateNode(array('uid' => $this->adminUser->uid, 'type' => 'page'));
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 9d5fe32..39827cb 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1113,11 +1113,11 @@ function node_is_page(EntityInterface $node) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function node_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'node') {
-    switch ($variables['block']->id) {
+function node_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'node') {
+    switch ($variables['plugin_id']) {
       case 'node_syndicate_block':
         $variables['attributes']['role'] = 'complementary';
         break;
@@ -1997,14 +1997,14 @@ function theme_node_recent_content($variables) {
 function node_form_block_form_alter(&$form, &$form_state) {
   $block = $form_state['entity'];
   $visibility = $block->get('visibility');
-  $form['visibility']['node_type'] = array(
+  $form['settings']['visibility']['node_type'] = array(
     '#type' => 'details',
     '#title' => t('Content types'),
     '#collapsed' => TRUE,
     '#group' => 'visibility',
     '#weight' => 5,
   );
-  $form['visibility']['node_type']['types'] = array(
+  $form['settings']['visibility']['node_type']['types'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Show block for specific content types'),
     '#default_value' => !empty($visibility['node_type']['types']) ? $visibility['node_type']['types'] : array(),
@@ -2020,7 +2020,8 @@ function node_form_block_form_alter(&$form, &$form_state) {
  * if the visibility conditions are not met.
  */
 function node_block_access($block) {
-  $visibility = $block->get('visibility');
+  $settings = $block->get('settings');
+  $visibility = $settings['visibility'];
   if (!empty($visibility)) {
     $allowed_types = array();
     $node = menu_get_object();
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
index 6d31124..ac1421c 100644
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -5,6 +5,8 @@
  * Implement OpenID Relying Party support for Drupal
  */
 
+use Drupal\block\BlockInterface;
+
 /**
  * Implements hook_menu().
  */
@@ -158,23 +160,23 @@ function openid_user_logout($account) {
 }
 
 /**
- * Implements hook_block_view_MODULE_DELTA_alter().
+ * Implements hook_block_plugin_view_NAME_alter() for 'user_login_block'.
  *
  * Adds the OpenID login form to the user login block.
  *
  * @see \Drupal\user\Plugin\block\block\UserLoginBlock
  */
-function openid_block_view_user_login_block_alter(&$build, $block) {
+function openid_block_plugin_view_user_login_block_alter(array &$build, BlockInterface $block) {
   // Only alter the block when it is non-empty, i.e. when no user is logged in.
-  if (!isset($build['content']['user_login_form'])) {
+  if (!isset($build['#content']['user_login_form'])) {
     return;
   }
 
-  $build['content']['openid_login_form'] = drupal_get_form('openid_login_form');
-  $build['content']['openid_login_form']['openid_identifier']['#size'] = $build['content']['user_login_form']['name']['#size'];
+  $build['#content']['openid_login_form'] = drupal_get_form('openid_login_form');
+  $build['#content']['openid_login_form']['openid_identifier']['#size'] = $build['#content']['user_login_form']['name']['#size'];
 
   // Put an OpenID link as a first element.
-  $build['content']['user_links']['#items'] = array(
+  $build['#content']['user_links']['#items'] = array(
     l(t('Log in using OpenID'), 'user/login/openid', array(
       'attributes' => array(
         'title' => t('Log in using OpenID.'),
@@ -183,10 +185,10 @@ function openid_block_view_user_login_block_alter(&$build, $block) {
         'tabindex' => 0,
       ),
     ))
-  ) + $build['content']['user_links']['#items'];
+  ) + $build['#content']['user_links']['#items'];
 
   // Move links under the openid form.
-  $build['content']['user_links']['#weight'] = 10;
+  $build['#content']['user_links']['#weight'] = 10;
 }
 
 /**
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 197d006..0a9e5ae 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -7,6 +7,8 @@
 
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Drupal\block\Plugin\Core\Entity\Block;
+use Drupal\user\Plugin\Core\Entity\User;
 
 /**
  * Implements hook_help().
@@ -479,7 +481,7 @@ function theme_overlay_disable_message($variables) {
 /**
  * Implements hook_block_access().
  */
-function overlay_block_access($block) {
+function overlay_block_access(Block $block, $operation, User $account, $langcode) {
   // If we are limiting rendering to a subset of page regions, hide all blocks
   // which appear in regions not on that list. Note that overlay_page_alter()
   // does a more comprehensive job of preventing unwanted regions from being
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 85e58d3..9fb241d 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -134,10 +134,10 @@ function search_permission() {
 }
 
 /**
- * Implements hook_preprocess_block().
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function search_preprocess_block(&$variables) {
-  if ($variables['block']->id == 'search_form_block') {
+function search_preprocess_block_plugin(&$variables) {
+  if ($variables['plugin_id'] == 'search_form_block') {
     $variables['attributes']['role'] = 'search';
     $variables['content_attributes']['class'][] = 'container-inline';
   }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 25a04c7..e9b3380 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -485,10 +485,10 @@ function shortcut_renderable_links($shortcut_set = NULL) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function shortcut_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'shortcut') {
+function shortcut_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'shortcut') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 5cca38a..0beffa9 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -341,8 +341,8 @@ protected function drupalCreateContentType($settings = array()) {
    *
    * @param string $plugin_id
    *   The plugin ID of the block type for this block instance.
-   * @param array $values
-   *   (optional) An associative array of values for the block entity.
+   * @param array $settings
+   *   (optional) An associative array of settings for the block entity.
    *   Override the defaults by specifying the key and value in the array, for
    *   example:
    *   @code
@@ -355,8 +355,6 @@ protected function drupalCreateContentType($settings = array()) {
    *   - machine_name: Random string.
    *   - region: 'sidebar_first'.
    *   - theme: The default theme.
-   * @param array $settings
-   *   (optional) An associative array of plugin-specific settings.
    *
    * @return \Drupal\block\Plugin\Core\Entity\Block
    *   The block entity.
@@ -364,15 +362,19 @@ protected function drupalCreateContentType($settings = array()) {
    * @todo
    *   Add support for creating custom block instances.
    */
-  protected function drupalPlaceBlock($plugin_id, array $values = array(), array $settings = array()) {
-    $values += array(
+  protected function drupalPlaceBlock($plugin_id, array $settings = array()) {
+    $settings += array(
       'plugin' => $plugin_id,
-      'label' => $this->randomName(8),
       'region' => 'sidebar_first',
-      'theme' => config('system.theme')->get('default'),
       'machine_name' => strtolower($this->randomName(8)),
-      'settings' => $settings,
+      'theme' => config('system.theme')->get('default'),
+      'label' => $this->randomName(8),
     );
+    foreach (array('region', 'machine_name', 'theme', 'plugin') as $key) {
+      $values[$key] = $settings[$key];
+      unset($settings[$key]);
+    }
+    $values['settings'] = $settings;
     // Build the ID out of the theme and machine_name.
     $values['id'] = $values['theme'] . '.' . $values['machine_name'];
     $block = entity_create('block', $values);
diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php
index 50cc99b..634a6ec 100644
--- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php
+++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php
@@ -39,7 +39,8 @@ function testPopularContentBlock() {
     drupal_http_request($stats_path, array('method' => 'POST', 'data' => $post, 'headers' => $headers, 'timeout' => 10000));
 
     // Configure and save the block.
-    $this->drupalPlaceBlock('statistics_popular_block', array('label' => 'Popular content'), array(
+    $this->drupalPlaceBlock('statistics_popular_block', array(
+      'label' => 'Popular content',
       'top_day_num' => 3,
       'top_all_num' => 3,
       'top_last_num' => 3,
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 66842a5..4dfd9d7 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -234,10 +234,10 @@ function statistics_update_index() {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function statistics_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'statistics') {
+function statistics_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'statistics') {
     $variables['attributes']['role'] = 'navigation';
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BlockUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BlockUpgradePathTest.php
index b9129e3..45f2efb 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BlockUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BlockUpgradePathTest.php
@@ -42,16 +42,16 @@ public function testBlockUpgradeTitleLength() {
     // Add a block instance with a 255-character title.
     // Confirm that the custom block has been created, and title matches input.
     $settings = array(
-      'label' => $this->randomName(255),
+      'settings[label]' => $this->randomName(255),
       'machine_name' => strtolower($this->randomName(8)),
       'region' => 'sidebar_first',
     );
     $this->drupalPost('admin/structure/block/add/system_powered_by_block/' . config('system.theme')->get('default'), $settings, t('Save block'));
-    $this->assertText($settings['label'], 'Block with title longer than 64 characters successfully created.');
+    $this->assertText($settings['settings[label]'], 'Block with title longer than 64 characters successfully created.');
 
     // Try to add a block with a title over 255 characters.
     $settings = array(
-      'label' => $this->randomName(256),
+      'settings[label]' => $this->randomName(256),
       'machine_name' => strtolower($this->randomName(8)),
       'region' => 'sidebar_first',
     );
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index c4304f4..4de999f 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2669,23 +2669,21 @@ function system_user_timezone(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function system_preprocess_block(&$variables) {
-  switch ($variables['block']->id) {
+function system_preprocess_block_plugin(&$variables) {
+  // Derive the base plugin ID.
+  list($plugin_id) = explode(':', $variables['plugin_id'] . ':');
+  switch ($plugin_id) {
     case 'system_powered_by_block':
       $variables['attributes_array']['role'] = 'complementary';
       break;
     case 'system_help_block':
       $variables['attributes_array']['role'] = 'complementary';
       break;
-
-    // System menu blocks should get the same class as menu module blocks.
-    default:
-      if ($variables['elements']['#block']->getPlugin() instanceof SystemMenuBlock) {
-        $variables['attributes_array']['role'] = 'navigation';
-        $variables['classes_array'][] = 'block-menu';
-      }
+    case 'system_menu_block':
+      $variables['attributes_array']['role'] = 'navigation';
+      $variables['classes_array'][] = 'block-menu';
   }
 }
 
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index bdf5862..e07bf0e 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -668,11 +668,11 @@ function user_validate_current_pass(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function user_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'user') {
-    switch ($variables['block']->id) {
+function user_preprocess_block_plugin(&$variables) {
+  if ($variables['configuration']['module'] == 'user') {
+    switch ($variables['plugin_id']) {
       case 'user_login_block':
         $variables['attributes']['role'] = 'form';
         break;
diff --git a/core/modules/views/lib/Drupal/views/Plugin/block/block/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/block/block/ViewsBlock.php
index 8a77f18..eed8c07 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/block/block/ViewsBlock.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/block/block/ViewsBlock.php
@@ -8,7 +8,6 @@
 namespace Drupal\views\Plugin\block\block;
 
 use Drupal\block\BlockBase;
-use Drupal\block\Plugin\Core\Entity\Block;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
 
@@ -41,8 +40,8 @@ class ViewsBlock extends BlockBase {
   /**
    * Overrides \Drupal\Component\Plugin\PluginBase::__construct().
    */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, Block $entity) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity);
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
 
     list($plugin, $delta) = explode(':', $this->getPluginId());
     list($name, $this->displayID) = explode('-', $delta, 2);
@@ -76,7 +75,7 @@ public function form($form, &$form_state) {
   public function build() {
     $output = $this->view->executeDisplay($this->displayID);
     // Set the label to the title configured in the view.
-    $this->entity->set('label', filter_xss_admin($this->view->getTitle()));
+    $this->configuration['label'] = filter_xss_admin($this->view->getTitle());
     // Before returning the block output, convert it to a renderable array
     // with contextual links.
     $this->addContextualLinks($output);
diff --git a/core/profiles/minimal/config/block.block.stark.admin.yml b/core/profiles/minimal/config/block.block.stark.admin.yml
index e84cdbe..d16ae8b 100644
--- a/core/profiles/minimal/config/block.block.stark.admin.yml
+++ b/core/profiles/minimal/config/block.block.stark.admin.yml
@@ -1,19 +1,19 @@
 id: stark.admin
-label: Administration
 region: sidebar_first
-weight: '1'
-module: system
-status: '1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types: {  }
-  visibility__active_tab: edit-visibility-path
 plugin: 'system_menu_block:menu-admin'
 settings:
+  label: Administration
+  weight: '1'
+  module: system
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types: {  }
+    visibility__active_tab: edit-visibility-path
   cache: '-1'
-label_display: visible
+  label_display: visible
diff --git a/core/profiles/minimal/config/block.block.stark.login.yml b/core/profiles/minimal/config/block.block.stark.login.yml
index ba8a66d..17a4e1d 100644
--- a/core/profiles/minimal/config/block.block.stark.login.yml
+++ b/core/profiles/minimal/config/block.block.stark.login.yml
@@ -1,19 +1,19 @@
 id: stark.login
-label: 'User login'
 region: sidebar_first
-weight: '0'
-module: user
-status: '1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types: {  }
-  visibility__active_tab: edit-visibility-path
 plugin: user_login_block
 settings:
+  label: 'User login'
+  weight: '0'
+  module: user
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types: {  }
+    visibility__active_tab: edit-visibility-path
   cache: '-1'
-label_display: visible
+  label_display: visible
diff --git a/core/profiles/minimal/config/block.block.stark.tools.yml b/core/profiles/minimal/config/block.block.stark.tools.yml
index dda5b98..cccbb5e 100644
--- a/core/profiles/minimal/config/block.block.stark.tools.yml
+++ b/core/profiles/minimal/config/block.block.stark.tools.yml
@@ -1,19 +1,19 @@
 id: stark.tools
-label: Tools
 region: sidebar_first
-weight: '0'
-module: system
-status: '1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types: {  }
-  visibility__active_tab: edit-visibility-path
 plugin: 'system_menu_block:menu-tools'
 settings:
+  label: Tools
+  weight: '0'
+  module: system
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types: {  }
+    visibility__active_tab: edit-visibility-path
   cache: '-1'
-label_display: visible
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.content.yml b/core/profiles/standard/config/block.block.bartik.content.yml
index 07c3cdd..c389bb4 100644
--- a/core/profiles/standard/config/block.block.bartik.content.yml
+++ b/core/profiles/standard/config/block.block.bartik.content.yml
@@ -1,22 +1,22 @@
 id: bartik.content
+region: content
+langcode: en
 plugin: system_main_block
-status: '1'
 settings:
+  status: '1'
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: ''
-module: system
-region: content
-weight: '0'
-langcode: en
-label_display: visible
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: ''
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.footer.yml b/core/profiles/standard/config/block.block.bartik.footer.yml
index 553edb7..c8ff530 100644
--- a/core/profiles/standard/config/block.block.bartik.footer.yml
+++ b/core/profiles/standard/config/block.block.bartik.footer.yml
@@ -1,22 +1,22 @@
 id: bartik.footer
+region: footer
 plugin: 'system_menu_block:menu-footer'
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-label: 'Footer menu'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-module: system
-region: footer
-weight: '0'
-langcode: en
-label_display: visible
+  label: 'Footer menu'
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.help.yml b/core/profiles/standard/config/block.block.bartik.help.yml
index 84395a6..bf8abb5 100644
--- a/core/profiles/standard/config/block.block.bartik.help.yml
+++ b/core/profiles/standard/config/block.block.bartik.help.yml
@@ -1,22 +1,22 @@
 id: bartik.help
+region: help
 plugin: system_help_block
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: ''
-module: system
-region: help
-weight: '0'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: ''
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.login.yml b/core/profiles/standard/config/block.block.bartik.login.yml
index aac6997..988234b 100644
--- a/core/profiles/standard/config/block.block.bartik.login.yml
+++ b/core/profiles/standard/config/block.block.bartik.login.yml
@@ -1,23 +1,23 @@
 id: bartik.login
-whois_new_count: '5'
-status: '1'
 settings:
+  whois_new_count: '5'
+  status: '1'
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: 'User login'
-module: user
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: 'User login'
+  module: user
+  weight: '0'
+  label_display: visible
 region: sidebar_first
-weight: '0'
 plugin: user_login_block
 langcode: en
-label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.powered.yml b/core/profiles/standard/config/block.block.bartik.powered.yml
index 48424f8..c6a3739 100644
--- a/core/profiles/standard/config/block.block.bartik.powered.yml
+++ b/core/profiles/standard/config/block.block.bartik.powered.yml
@@ -1,22 +1,22 @@
 id: bartik.powered
+region: footer
 plugin: system_powered_by_block
-status: '1'
+langcode: en
 settings:
+  status: '1'
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: ''
-module: system
-region: footer
-weight: '10'
-langcode: en
-label_display: visible
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: ''
+  module: system
+  weight: '10'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.search.yml b/core/profiles/standard/config/block.block.bartik.search.yml
index bcf0249..22f7c23 100644
--- a/core/profiles/standard/config/block.block.bartik.search.yml
+++ b/core/profiles/standard/config/block.block.bartik.search.yml
@@ -1,22 +1,22 @@
 id: bartik.search
+region: sidebar_first
 plugin: search_form_block
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: 'Search'
-module: search
-region: sidebar_first
-weight: '-1'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: 'Search'
+  module: search
+  weight: '-1'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.bartik.tools.yml b/core/profiles/standard/config/block.block.bartik.tools.yml
index 56c8ba4..c56d80f 100644
--- a/core/profiles/standard/config/block.block.bartik.tools.yml
+++ b/core/profiles/standard/config/block.block.bartik.tools.yml
@@ -1,22 +1,22 @@
 id: bartik.tools
+region: sidebar_first
 plugin: 'system_menu_block:menu-tools'
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: 'Tools'
-module: system
-region: sidebar_first
-weight: '0'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: 'Tools'
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.seven.content.yml b/core/profiles/standard/config/block.block.seven.content.yml
index f14f147..7c4b669 100644
--- a/core/profiles/standard/config/block.block.seven.content.yml
+++ b/core/profiles/standard/config/block.block.seven.content.yml
@@ -1,22 +1,22 @@
 id: seven.content
+region: content
 plugin: system_main_block
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: ''
-module: system
-region: content
-weight: '0'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: ''
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.seven.help.yml b/core/profiles/standard/config/block.block.seven.help.yml
index 075f518..b337339 100644
--- a/core/profiles/standard/config/block.block.seven.help.yml
+++ b/core/profiles/standard/config/block.block.seven.help.yml
@@ -1,22 +1,22 @@
 id: seven.help
+region: help
 plugin: system_help_block
-status: '1'
+langcode: en
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: ''
-module: system
-region: help
-weight: '0'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: ''
+  module: system
+  weight: '0'
+  label_display: visible
diff --git a/core/profiles/standard/config/block.block.seven.login.yml b/core/profiles/standard/config/block.block.seven.login.yml
index f0fb2b2..dd8d4d8 100644
--- a/core/profiles/standard/config/block.block.seven.login.yml
+++ b/core/profiles/standard/config/block.block.seven.login.yml
@@ -1,22 +1,22 @@
 id: seven.login
+region: content
 plugin: user_login_block
-status: '1'
 settings:
   cache: '-1'
-visibility:
-  path:
-    visibility: '0'
-    pages: ''
-  role:
-    roles: {  }
-  node_type:
-    types:
-      article: '0'
-      page: '0'
-  visibility__active_tab: edit-visibility-path
-label: 'User login'
-module: user
-region: content
-weight: '10'
-langcode: en
-label_display: visible
+  status: '1'
+  visibility:
+    path:
+      visibility: '0'
+      pages: ''
+    role:
+      roles: {  }
+    node_type:
+      types:
+        article: '0'
+        page: '0'
+    visibility__active_tab: edit-visibility-path
+  label: 'User login'
+  module: user
+  weight: '10'
+  langcode: en
+  label_display: visible
diff --git a/core/themes/bartik/template.php b/core/themes/bartik/template.php
index 9a6c8a9..21f36ee 100644
--- a/core/themes/bartik/template.php
+++ b/core/themes/bartik/template.php
@@ -111,11 +111,13 @@ function bartik_process_maintenance_page(&$variables) {
 }
 
 /**
- * Implements hook_preprocess_HOOK() for block.tpl.php.
+ * Implements hook_preprocess_HOOK() for block-plugin.tpl.php.
  */
-function bartik_preprocess_block(&$variables) {
+function bartik_preprocess_block_plugin(&$variables) {
+  // @todo A block plugin cannot not know what region it is in.
+  $region = '';
   // In the header and footer regions visually hide block titles.
-  if ($variables['block']->region == 'header' || $variables['block']->region == 'footer') {
+  if ($region == 'header' || $region == 'footer') {
     $variables['title_attributes']['class'][] = 'element-invisible';
   }
 }
