diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 37b93bd..ceffa1f 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -49,7 +49,7 @@ function block_help($route_name, Request $request) {
       $output .= '<dt>' . t('Controlling visibility') . '</dt>';
       $output .= '<dd>' . t('You can control the visibility of a block by restricting it to specific pages, content types, and/or roles by setting the appropriate options under <em>Visibility settings</em> of the block configuration.') . '</dd>';
       $output .= '<dt>' . t('Adding custom blocks') . '</dt>';
-      $output .= '<dd>' . t('You can add custom blocks, if the the <em>Custom Block</em> module is enabled on the <a href="!extend">Extend page</a>. For more information, see the <a href="!customblock-help">Custom Block help page</a>.', array('!extend' => \Drupal::url('system.modules_list'), '!customblock-help' => \Drupal::url('help.page', array('name' => 'custom_block')))) . '</dd>';
+      $output .= '<dd>' . t('You can add custom blocks, if the the <em>Custom Block</em> module is enabled on the <a href="!extend">Extend page</a>. For more information, see the <a href="!blockcontent-help">Custom Block help page</a>.', array('!extend' => \Drupal::url('system.modules_list'), '!blockcontent-help' => \Drupal::url('help.page', array('name' => 'block_content')))) . '</dd>';
       $output .= '</dl>';
       return $output;
   }
diff --git a/core/modules/block/custom_block/config/install/custom_block.type.basic.yml b/core/modules/block/custom_block/config/install/custom_block.type.basic.yml
deleted file mode 100644
index 02982e4..0000000
--- a/core/modules/block/custom_block/config/install/custom_block.type.basic.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-id: basic
-label: 'Basic block'
-revision: 0
-description: 'A basic block contains a title and a body.'
-langcode: en
diff --git a/core/modules/block/custom_block/config/install/entity.view_mode.custom_block.full.yml b/core/modules/block/custom_block/config/install/entity.view_mode.custom_block.full.yml
deleted file mode 100644
index 2987629..0000000
--- a/core/modules/block/custom_block/config/install/entity.view_mode.custom_block.full.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-id: custom_block.full
-label: Full
-status: false
-cache: true
-targetEntityType: custom_block
-dependencies:
-  module:
-    - custom_block
diff --git a/core/modules/block/custom_block/config/schema/custom_block.schema.yml b/core/modules/block/custom_block/config/schema/custom_block.schema.yml
deleted file mode 100644
index 1a9c04d..0000000
--- a/core/modules/block/custom_block/config/schema/custom_block.schema.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-# Schema for the configuration files of the Custom Block module.
-
-custom_block.type.*:
-  type: mapping
-  label: 'Custom block type settings'
-  mapping:
-    id:
-      type: string
-      label: 'Machine-readable name'
-    uuid:
-      type: string
-      label: 'UUID'
-    label:
-      type: label
-      label: 'Label'
-    revision:
-      type: integer
-      label: 'Create new revision'
-    description:
-      type: text
-      label: 'Description'
-    status:
-      type: boolean
-      label: 'Status'
-    langcode:
-      type: string
-      label: 'Default language'
diff --git a/core/modules/block/custom_block/custom_block.contextual_links.yml b/core/modules/block/custom_block/custom_block.contextual_links.yml
deleted file mode 100644
index acd117f..0000000
--- a/core/modules/block/custom_block/custom_block.contextual_links.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-custom_block.block_edit:
-  title: 'Edit'
-  group: custom_block
-  route_name: 'custom_block.edit'
-
-custom_block.block_delete:
-  title: 'Delete'
-  group: custom_block
-  route_name: 'custom_block.delete'
-  weight: 1
diff --git a/core/modules/block/custom_block/custom_block.info.yml b/core/modules/block/custom_block/custom_block.info.yml
deleted file mode 100644
index 692648f..0000000
--- a/core/modules/block/custom_block/custom_block.info.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: 'Custom Block'
-type: module
-description: 'Allows the creation of custom blocks through the user interface.'
-package: Core
-version: VERSION
-core: 8.x
-dependencies:
-  - block
-  - text
-configure: custom_block.list
diff --git a/core/modules/block/custom_block/custom_block.install b/core/modules/block/custom_block/custom_block.install
deleted file mode 100644
index 6f85a6a..0000000
--- a/core/modules/block/custom_block/custom_block.install
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update and uninstall functions for the custom block module.
- */
-
-use Drupal\Core\Entity\EntityTypeInterface;
-
-/**
- * Implements hook_schema().
- */
-function custom_block_schema() {
-  $schema = array();
-  $schema['custom_block'] = array(
-    'description' => 'Stores contents of custom-made blocks.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => "The block's {custom_block}.id.",
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'info' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Block description.',
-      ),
-      // Defaults to NULL in order to avoid a brief period of potential
-      // deadlocks on the index.
-      'revision_id' => array(
-        'description' => 'The current {block_custom_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-      ),
-      'type' => array(
-        'description' => 'The type of this custom block.',
-        'type' => 'varchar',
-        'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'changed' => array(
-        'description' => 'The Unix timestamp when the custom block was most recently saved.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this node.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('id'),
-    'indexes' => array(
-      'block_custom_type' => array('type'),
-    ),
-    'unique keys' => array(
-      'revision_id' => array('revision_id'),
-      'uuid' => array('uuid'),
-      'info' => array('info'),
-    ),
-    'foreign keys' => array(
-      'custom_block_revision' => array(
-        'table' => 'custom_block_revision',
-        'columns' => array('revision_id' => 'revision_id'),
-      ),
-    ),
-  );
-
-  $schema['custom_block_revision'] = array(
-    'description' => 'Stores contents of custom-made blocks.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => "The block's {custom_block}.id.",
-      ),
-      // Defaults to NULL in order to avoid a brief period of potential
-      // deadlocks on the index.
-      'revision_id' => array(
-        'description' => 'The current version identifier.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ),
-      'log' => array(
-        'description' => 'The log entry explaining the changes in this version.',
-        'type' => 'text',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
-      'info' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Block description.',
-      ),
-      'changed' => array(
-        'description' => 'The Unix timestamp when the version was most recently saved.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-    ),
-    'primary key' => array('revision_id'),
-  );
-  return $schema;
-}
diff --git a/core/modules/block/custom_block/custom_block.libraries.yml b/core/modules/block/custom_block/custom_block.libraries.yml
deleted file mode 100644
index 34539a8..0000000
--- a/core/modules/block/custom_block/custom_block.libraries.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-drupal.custom_block:
-  version: VERSION
-  js:
-    js/custom_block.js: {}
-  dependencies:
-    - core/jquery
-    - core/drupal
-    - core/drupal.form
diff --git a/core/modules/block/custom_block/custom_block.local_actions.yml b/core/modules/block/custom_block/custom_block.local_actions.yml
deleted file mode 100644
index e3b57c6..0000000
--- a/core/modules/block/custom_block/custom_block.local_actions.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-custom_block_type_add:
-  route_name: custom_block.type_add
-  title: 'Add custom block type'
-  appears_on:
-    - custom_block.type_list
-
-custom_block_add_action:
-  route_name: custom_block.add_page
-  title: 'Add custom block'
-  appears_on:
-    - block.admin_display
-    - block.admin_display_theme
-    - custom_block.list
-  class: \Drupal\custom_block\Plugin\Menu\LocalAction\CustomBlockAddLocalAction
diff --git a/core/modules/block/custom_block/custom_block.local_tasks.yml b/core/modules/block/custom_block/custom_block.local_tasks.yml
deleted file mode 100644
index 041abb7..0000000
--- a/core/modules/block/custom_block/custom_block.local_tasks.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-custom_block.list:
-  title: 'Custom block library'
-  route_name: custom_block.list
-  base_route: block.admin_display
-custom_block.list_sub:
-  title: Blocks
-  route_name: custom_block.list
-  parent_id: custom_block.list
-custom_block.type_list:
-  title: Types
-  route_name: custom_block.type_list
-  parent_id: custom_block.list
-
-custom_block.edit:
-  title: Edit
-  route_name: custom_block.edit
-  base_route: custom_block.edit
-custom_block.delete:
-  title: Delete
-  route_name: custom_block.delete
-  base_route: custom_block.edit
-
-# Default tab for custom block type editing.
-custom_block.type_edit:
-  title: 'Edit'
-  route_name: custom_block.type_edit
-  base_route: custom_block.type_edit
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
deleted file mode 100644
index 00caf8e..0000000
--- a/core/modules/block/custom_block/custom_block.module
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-/**
- * @file
- * Allows the creation of custom blocks through the user interface.
- */
-
-use Symfony\Component\HttpFoundation\Request;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldInstanceConfig;
-
-/**
- * Implements hook_help().
- */
-function custom_block_help($route_name, Request $request) {
-  switch ($route_name) {
-    case 'help.page.custom_block':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Custom Block module allows you to create blocks of content, which can be placed in regions throughout the website. Custom blocks can have fields; see the <a href="!field-help">Field module help</a> for more information. Once created, custom blocks can be placed like blocks provided by other modules; see the <a href="!blocks">Block module help page</a> for details. For more information, see <a href="!online-help">the online documentation for the Custom Block module</a>.', array('!custom-blocks' => \Drupal::url('custom_block.list'), '!field-help' => \Drupal::url('help.page', array('name' => 'field')), '!blocks' => \Drupal::url('help.page', array('name' => 'block')), '!online-help' => 'https://drupal.org/documentation/modules/custom_block')) . '</p>';
-      $output .= '<h3>' . t('Uses') . '</h3>';
-      $output .= '<dl>';
-      $output .= '<dt>' . t('Creating and managing custom block types') . '</dt>';
-      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can create different custom block types, each with different fields and display settings, from the <a href="!types">Custom block types</a> page. The Custom block types page lists all of your created custom block types, and allows you to edit and manage them. For more information about managing fields and display settings, see the <a href="!field-ui">Field UI module help</a>.', array('!types' => \Drupal::url('custom_block.type_list'), '!field-ui' => \Drupal::url('help.page', array('name' => 'field_ui')))) . '</dd>';
-      $output .= '<dt>' . t('Creating custom blocks') . '</dt>';
-      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can <a href="!block-add">add custom blocks</a> of each of their defined custom block types. Created custom blocks are then listed on the <a href="!blocks">Blocks administration page</a>.', array('!blocks' => \Drupal::url('block.admin_display'), '!block-add' => \Drupal::url('custom_block.add_page'))) . '</dd>';
-      $output .= '</dl>';
-      return $output;
-
-    case 'custom_block.list':
-      $output = '<p>' . t('This page lists user-created blocks. These blocks are derived from block types. A block type can consist of different fields and display settings. From the block types tab you can manage these fields as well as create new block types.') . '</p>';
-      return $output;
-
-    case 'custom_block.type_list':
-      $output = '<p>' . t('This page lists block types. A block type can consist of different fields and display settings. From here you can manage these fields as well as create new block types.') . '</p>';
-      return $output;
-
-  }
-}
-
-/**
- * Implements hook_theme().
- */
-function custom_block_theme($existing, $type, $theme, $path) {
-  return array(
-    'custom_block_add_list' => array(
-      'variables' => array('content' => NULL),
-      'file' => 'custom_block.pages.inc',
-      'template' => 'custom-block-add-list',
-    ),
-  );
-}
-
-/**
- * Implements hook_entity_type_alter().
- */
-function custom_block_entity_type_alter(array &$entity_types) {
-  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
-  // Add a translation handler for fields if the language module is enabled.
-  if (\Drupal::moduleHandler()->moduleExists('language')) {
-    $translation = $entity_types['custom_block']->get('translation');
-    $translation['custom_block'] = TRUE;
-    $entity_types['custom_block']->set('translation', $translation);
-  }
-}
-
-/**
- * Adds the default body field to a custom block type.
- *
- * @param string $block_type_id
- *   Id of the block type.
- * @param string $label
- *   (optional) The label for the body instance. Defaults to 'Body'
- *
- * @return array()
- *   Body field instance.
- */
-function custom_block_add_body_field($block_type_id, $label = 'Body') {
-  // Add or remove the body field, as needed.
-  $field = FieldConfig::loadByName('custom_block', 'body');
-  $instance = FieldInstanceConfig::loadByName('custom_block', $block_type_id, 'body');
-  if (empty($field)) {
-    $field = entity_create('field_config', array(
-      'name' => 'body',
-      'entity_type' => 'custom_block',
-      'type' => 'text_with_summary',
-    ));
-    $field->save();
-  }
-  if (empty($instance)) {
-    $instance = entity_create('field_instance_config', array(
-      'field_name' => 'body',
-      'entity_type' => 'custom_block',
-      'bundle' => $block_type_id,
-      'label' => $label,
-      'settings' => array('display_summary' => FALSE),
-    ));
-    $instance->save();
-
-    // Assign widget settings for the 'default' form mode.
-    entity_get_form_display('custom_block', $block_type_id, 'default')
-      ->setComponent('body', array(
-        'type' => 'text_textarea_with_summary',
-      ))
-      ->save();
-
-    // Assign display settings for 'default' view mode.
-    entity_get_display('custom_block', $block_type_id, 'default')
-      ->setComponent('body', array(
-        'label' => 'hidden',
-        'type' => 'text_default',
-      ))
-      ->save();
-  }
-
-  return $instance;
-}
diff --git a/core/modules/block/custom_block/custom_block.pages.inc b/core/modules/block/custom_block/custom_block.pages.inc
deleted file mode 100644
index f24e16b..0000000
--- a/core/modules/block/custom_block/custom_block.pages.inc
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides page callbacks for custom blocks.
- */
-
-use Drupal\Component\Utility\Xss;
-use Drupal\custom_block\Entity\CustomBlockType;
-use Drupal\custom_block\Entity\CustomBlock;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-
-/**
- * Prepares variables for a custom block type creation list templates.
- *
- * Default template: custom-block-add-list.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - content: An array of block types.
- *
- * @see custom_block_add_page()
- */
-function template_preprocess_custom_block_add_list(&$variables) {
-  $variables['types'] = array();
-  $query = \Drupal::request()->query->all();
-  foreach ($variables['content'] as $type) {
-    $variables['types'][$type->id()] = array(
-      'link' => \Drupal::l($type->label(), 'custom_block.add_form', array('custom_block_type' => $type->id()), array('query' => $query)),
-      'description' => Xss::filterAdmin($type->description),
-      'title' => $type->label(),
-      'localized_options' => array(
-        'query' => $query,
-      ),
-    );
-  }
-}
diff --git a/core/modules/block/custom_block/custom_block.routing.yml b/core/modules/block/custom_block/custom_block.routing.yml
deleted file mode 100644
index cad1f09..0000000
--- a/core/modules/block/custom_block/custom_block.routing.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-custom_block.type_list:
-  path: '/admin/structure/block/custom-blocks/types'
-  defaults:
-    _entity_list: 'custom_block_type'
-    _title: 'Edit'
-  requirements:
-    _permission: 'administer blocks'
-
-custom_block.add_page:
-  path: '/block/add'
-  defaults:
-    _content: '\Drupal\custom_block\Controller\CustomBlockController::add'
-    _title: 'Add custom block'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _permission: 'administer blocks'
-
-custom_block.add_form:
-  path: '/block/add/{custom_block_type}'
-  defaults:
-    _content: '\Drupal\custom_block\Controller\CustomBlockController::addForm'
-    _title_callback: 'Drupal\custom_block\Controller\CustomBlockController::getAddFormTitle'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _permission: 'administer blocks'
-
-custom_block.type_delete:
-  path: '/admin/structure/block/custom-blocks/manage/{custom_block_type}/delete'
-  defaults:
-    _entity_form: 'custom_block_type.delete'
-    _title: 'Delete'
-  requirements:
-    _entity_access: 'custom_block_type.delete'
-  options:
-    _admin_route: TRUE
-
-custom_block.edit:
-  path: '/block/{custom_block}'
-  defaults:
-    _entity_form: 'custom_block.edit'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'custom_block.update'
-
-custom_block.delete:
-  path: '/block/{custom_block}/delete'
-  defaults:
-    _entity_form: 'custom_block.delete'
-    _title: 'Delete'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'custom_block.delete'
-
-custom_block.type_add:
-  path: '/admin/structure/block/custom-blocks/types/add'
-  defaults:
-    _entity_form: 'custom_block_type.add'
-    _title: 'Add'
-  requirements:
-    _permission: 'administer blocks'
-
-custom_block.type_edit:
-  path: '/admin/structure/block/custom-blocks/manage/{custom_block_type}'
-  defaults:
-    _entity_form: 'custom_block_type.edit'
-    _title: 'Edit'
-  requirements:
-    _entity_access: 'custom_block_type.update'
-
-custom_block.list:
-  path: '/admin/structure/block/custom-blocks'
-  defaults:
-    _title: 'Custom block library'
-    _entity_list: 'custom_block'
-  requirements:
-    _permission: 'administer blocks'
diff --git a/core/modules/block/custom_block/js/custom_block.js b/core/modules/block/custom_block/js/custom_block.js
deleted file mode 100644
index be0ea7d..0000000
--- a/core/modules/block/custom_block/js/custom_block.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * @file
- * Defines Javascript behaviors for the custom_block module.
- */
-
-(function ($) {
-
-  "use strict";
-
-  Drupal.behaviors.customBlockDetailsSummaries = {
-    attach: function (context) {
-      var $context = $(context);
-      $context.find('.custom-block-form-revision-information').drupalSetSummary(function (context) {
-        var $context = $(context);
-        var revisionCheckbox = $context.find('.form-item-revision input');
-
-        // Return 'New revision' if the 'Create new revision' checkbox is checked,
-        // or if the checkbox doesn't exist, but the revision log does. For users
-        // without the "Administer content" permission the checkbox won't appear,
-        // but the revision log will if the content type is set to auto-revision.
-        if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $context.find('.form-item-log textarea').length)) {
-          return Drupal.t('New revision');
-        }
-
-        return Drupal.t('No revision');
-      });
-
-      $context.find('fieldset.custom-block-translation-options').drupalSetSummary(function (context) {
-        var $context = $(context);
-        var translate;
-        var $checkbox = $context.find('.form-item-translation-translate input');
-
-        if ($checkbox.size()) {
-          translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
-        }
-        else {
-          $checkbox = $context.find('.form-item-translation-retranslate input');
-          translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
-        }
-
-        return translate;
-      });
-    }
-  };
-
-})(jQuery);
diff --git a/core/modules/block/custom_block/src/Controller/CustomBlockController.php b/core/modules/block/custom_block/src/Controller/CustomBlockController.php
deleted file mode 100644
index e30c42d..0000000
--- a/core/modules/block/custom_block/src/Controller/CustomBlockController.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Controller\CustomBlockController
- */
-
-namespace Drupal\custom_block\Controller;
-
-use Drupal\Component\Plugin\PluginManagerInterface;
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\custom_block\CustomBlockTypeInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\Request;
-
-class CustomBlockController extends ControllerBase {
-
-  /**
-   * The custom block storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $customBlockStorage;
-
-  /**
-   * The custom block type storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $customBlockTypeStorage;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $entity_manager = $container->get('entity.manager');
-    return new static(
-      $entity_manager->getStorage('custom_block'),
-      $entity_manager->getStorage('custom_block_type')
-    );
-  }
-
-  /**
-   * Constructs a CustomBlock object.
-   *
-   * @param \Drupal\Core\Entity\EntityStorageInterface $custom_block_storage
-   *   The custom block storage.
-   * @param \Drupal\Core\Entity\EntityStorageInterface $custom_block_type_storage
-   *   The custom block type storage.
-   */
-  public function __construct(EntityStorageInterface $custom_block_storage, EntityStorageInterface $custom_block_type_storage) {
-    $this->customBlockStorage = $custom_block_storage;
-    $this->customBlockTypeStorage = $custom_block_type_storage;
-  }
-
-  /**
-   * Displays add custom block links for available types.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The current request object.
-   *
-   * @return array
-   *   A render array for a list of the custom block types that can be added or
-   *   if there is only one custom block type defined for the site, the function
-   *   returns the custom block add page for that custom block type.
-   */
-  public function add(Request $request) {
-    $types = $this->customBlockTypeStorage->loadMultiple();
-    if ($types && count($types) == 1) {
-      $type = reset($types);
-      return $this->addForm($type, $request);
-    }
-
-    return array('#theme' => 'custom_block_add_list', '#content' => $types);
-  }
-
-  /**
-   * Presents the custom block creation form.
-   *
-   * @param \Drupal\custom_block\CustomBlockTypeInterface $custom_block_type
-   *   The custom block type to add.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The current request object.
-   *
-   * @return array
-   *   A form array as expected by drupal_render().
-   */
-  public function addForm(CustomBlockTypeInterface $custom_block_type, Request $request) {
-    $block = $this->customBlockStorage->create(array(
-      'type' => $custom_block_type->id()
-    ));
-    if (($theme = $request->query->get('theme')) && in_array($theme, array_keys(list_themes()))) {
-      // We have navigated to this page from the block library and will keep track
-      // of the theme for redirecting the user to the configuration page for the
-      // newly created block in the given theme.
-      $block->setTheme($theme);
-    }
-    return $this->entityFormBuilder()->getForm($block);
-  }
-
-  /**
-   * Provides the page title for this controller.
-   *
-   * @param \Drupal\custom_block\CustomBlockTypeInterface $custom_block_type
-   *   The custom block type being added.
-   *
-   * @return string
-   *   The page title.
-   */
-  public function getAddFormTitle(CustomBlockTypeInterface $custom_block_type) {
-    return $this->t('Add %type custom block', array('%type' => $custom_block_type->label()));
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockAccessController.php b/core/modules/block/custom_block/src/CustomBlockAccessController.php
deleted file mode 100644
index c1d65be..0000000
--- a/core/modules/block/custom_block/src/CustomBlockAccessController.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockAccessController.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityAccessController;
-use Drupal\Core\Session\AccountInterface;
-
-/**
- * Defines the access controller for the custom block entity type.
- */
-class CustomBlockAccessController extends EntityAccessController {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
-    if ($operation === 'view') {
-      return TRUE;
-    }
-    return parent::checkAccess($entity, $operation, $langcode, $account);
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockForm.php b/core/modules/block/custom_block/src/CustomBlockForm.php
deleted file mode 100644
index f495c4f..0000000
--- a/core/modules/block/custom_block/src/CustomBlockForm.php
+++ /dev/null
@@ -1,254 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockForm.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Cache\Cache;
-use Drupal\Core\Entity\ContentEntityForm;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Form controller for the custom block edit forms.
- */
-class CustomBlockForm extends ContentEntityForm {
-
-  /**
-   * The custom block storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface
-   */
-  protected $customBlockStorage;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManager
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a CustomBlockForm object.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\Entity\EntityStorageInterface $custom_block_storage
-   *   The custom block storage.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
-   *   The language manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, EntityStorageInterface $custom_block_storage, LanguageManager $language_manager) {
-    parent::__construct($entity_manager);
-    $this->customBlockStorage = $custom_block_storage;
-    $this->languageManager = $language_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    $entity_manager = $container->get('entity.manager');
-    return new static(
-      $entity_manager,
-      $entity_manager->getStorage('custom_block'),
-      $container->get('language_manager')
-    );
-  }
-
-  /**
-   * Overrides \Drupal\Core\Entity\EntityForm::prepareEntity().
-   *
-   * Prepares the custom block object.
-   *
-   * Fills in a few default values, and then invokes hook_custom_block_prepare()
-   * on all modules.
-   */
-  protected function prepareEntity() {
-    $block = $this->entity;
-    // Set up default values, if required.
-    $block_type = entity_load('custom_block_type', $block->bundle());
-    if (!$block->isNew()) {
-      $block->setRevisionLog(NULL);
-    }
-    // Always use the default revision setting.
-    $block->setNewRevision($block_type->revision);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, array &$form_state) {
-    $block = $this->entity;
-    $account = $this->currentUser();
-
-    if ($this->operation == 'edit') {
-      $form['#title'] = $this->t('Edit custom block %label', array('%label' => $block->label()));
-    }
-    // Override the default CSS class name, since the user-defined custom block
-    // type name in 'TYPE-block-form' potentially clashes with third-party class
-    // names.
-    $form['#attributes']['class'][0] = drupal_html_class('block-' . $block->bundle() . '-form');
-
-    if ($this->moduleHandler->moduleExists('language')) {
-      $language_configuration = language_get_default_configuration('custom_block', $block->bundle());
-
-      // Set the correct default language.
-      if ($block->isNew()) {
-        $language_default = $this->languageManager->getCurrentLanguage($language_configuration['langcode']);
-        $block->langcode->value = $language_default->id;
-      }
-    }
-
-    $form['langcode'] = array(
-      '#title' => $this->t('Language'),
-      '#type' => 'language_select',
-      '#default_value' => $block->getUntranslated()->language()->id,
-      '#languages' => Language::STATE_ALL,
-      '#access' => isset($language_configuration['language_show']) && $language_configuration['language_show'],
-    );
-
-    $form['advanced'] = array(
-      '#type' => 'vertical_tabs',
-      '#weight' => 99,
-    );
-
-    // Add a log field if the "Create new revision" option is checked, or if the
-    // current user has the ability to check that option.
-    $form['revision_information'] = array(
-      '#type' => 'details',
-      '#title' => $this->t('Revision information'),
-      // Open by default when "Create new revision" is checked.
-      '#open' => $block->isNewRevision(),
-      '#group' => 'advanced',
-      '#attributes' => array(
-        'class' => array('custom-block-form-revision-information'),
-      ),
-      '#attached' => array(
-        'library' => array('custom_block/drupal.custom_block'),
-      ),
-      '#weight' => 20,
-      '#access' => $block->isNewRevision() || $account->hasPermission('administer blocks'),
-    );
-
-    $form['revision_information']['revision'] = array(
-      '#type' => 'checkbox',
-      '#title' => $this->t('Create new revision'),
-      '#default_value' => $block->isNewRevision(),
-      '#access' => $account->hasPermission('administer blocks'),
-    );
-
-    // Check the revision log checkbox when the log textarea is filled in.
-    // This must not happen if "Create new revision" is enabled by default,
-    // since the state would auto-disable the checkbox otherwise.
-    if (!$block->isNewRevision()) {
-      $form['revision_information']['revision']['#states'] = array(
-        'checked' => array(
-          'textarea[name="log"]' => array('empty' => FALSE),
-        ),
-      );
-    }
-
-    $form['revision_information']['log'] = array(
-      '#type' => 'textarea',
-      '#title' => $this->t('Revision log message'),
-      '#rows' => 4,
-      '#default_value' => $block->getRevisionLog(),
-      '#description' => $this->t('Briefly desribe the changes you have made.'),
-    );
-
-    return parent::form($form, $form_state, $block);
-  }
-
-  /**
-   * Overrides \Drupal\Core\Entity\EntityForm::submit().
-   *
-   * Updates the custom block object by processing the submitted values.
-   *
-   * This function can be called by a "Next" button of a wizard to update the
-   * form state's entity with the current step's values before proceeding to the
-   * next step.
-   */
-  public function submit(array $form, array &$form_state) {
-    // Build the block object from the submitted values.
-    $block = parent::submit($form, $form_state);
-
-    // Save as a new revision if requested to do so.
-    if (!empty($form_state['values']['revision'])) {
-      $block->setNewRevision();
-    }
-
-    return $block;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save(array $form, array &$form_state) {
-    $block = $this->entity;
-    $insert = $block->isNew();
-    $block->save();
-    $watchdog_args = array('@type' => $block->bundle(), '%info' => $block->label());
-    $block_type = entity_load('custom_block_type', $block->bundle());
-    $t_args = array('@type' => $block_type->label(), '%info' => $block->label());
-
-    if ($insert) {
-      watchdog('content', '@type: added %info.', $watchdog_args, WATCHDOG_NOTICE);
-      drupal_set_message($this->t('@type %info has been created.', $t_args));
-    }
-    else {
-      watchdog('content', '@type: updated %info.', $watchdog_args, WATCHDOG_NOTICE);
-      drupal_set_message($this->t('@type %info has been updated.', $t_args));
-    }
-
-    if ($block->id()) {
-      $form_state['values']['id'] = $block->id();
-      $form_state['id'] = $block->id();
-      if ($insert) {
-        if (!$theme = $block->getTheme()) {
-          $theme = $this->config('system.theme')->get('default');
-        }
-        $form_state['redirect_route'] = array(
-          'route_name' => 'block.admin_add',
-          'route_parameters' => array(
-            'plugin_id' => 'custom_block:' . $block->uuid(),
-            'theme' => $theme,
-          ),
-        );
-      }
-      else {
-        $form_state['redirect_route']['route_name'] = 'custom_block.list';
-      }
-    }
-    else {
-      // In the unlikely case something went wrong on save, the block will be
-      // rebuilt and block form redisplayed.
-      drupal_set_message($this->t('The block could not be saved.'), 'error');
-      $form_state['rebuild'] = TRUE;
-    }
-
-    // Clear the page and block caches.
-    Cache::invalidateTags(array('content' => TRUE));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, array &$form_state) {
-    if ($this->entity->isNew()) {
-      $exists = $this->customBlockStorage->loadByProperties(array('info' => $form_state['values']['info']));
-      if (!empty($exists)) {
-        $this->setFormError('info', $form_state, $this->t('A block with description %name already exists.', array(
-          '%name' => $form_state['values']['info'][0]['value'],
-        )));
-      }
-    }
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockInterface.php b/core/modules/block/custom_block/src/CustomBlockInterface.php
deleted file mode 100644
index 00cac2c..0000000
--- a/core/modules/block/custom_block/src/CustomBlockInterface.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Entity\CustomBlockInterface.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\EntityChangedInterface;
-
-/**
- * Provides an interface defining a custom block entity.
- */
-interface CustomBlockInterface extends ContentEntityInterface, EntityChangedInterface {
-
-  /**
-   * Returns the block revision log message.
-   *
-   * @return string
-   *   The revision log message.
-   */
-  public function getRevisionLog();
-
-  /**
-   * Sets the block description.
-   *
-   * @param string $info
-   *   The block description.
-   *
-   * @return \Drupal\custom_block\CustomBlockInterface
-   *   The class instance that this method is called on.
-   */
-  public function setInfo($info);
-
-  /**
-   * Sets the block revision log message.
-   *
-   * @param string $log
-   *   The revision log message.
-   *
-   * @return \Drupal\custom_block\CustomBlockInterface
-   *   The class instance that this method is called on.
-   */
-  public function setRevisionLog($log);
-
-  /**
-   * Sets the theme value.
-   *
-   * When creating a new custom block from the block library, the user is
-   * redirected to the configure form for that block in the given theme. The
-   * theme is stored against the block when the custom block add form is shown.
-   *
-   * @param string $theme
-   *   The theme name.
-   *
-   * @return \Drupal\custom_block\CustomBlockInterface
-   *   The class instance that this method is called on.
-   */
-  public function setTheme($theme);
-
-  /**
-   * Gets the theme value.
-   *
-   * When creating a new custom block from the block library, the user is
-   * redirected to the configure form for that block in the given theme. The
-   * theme is stored against the block when the custom block add form is shown.
-   *
-   * @return string
-   *   The theme name.
-   */
-  public function getTheme();
-
-  /**
-   * Gets the configured instances of this custom block.
-   *
-   * @return array
-   *   Array of Drupal\block\Core\Plugin\Entity\Block entities.
-   */
-  public function getInstances();
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockListBuilder.php b/core/modules/block/custom_block/src/CustomBlockListBuilder.php
deleted file mode 100644
index 5df5a19..0000000
--- a/core/modules/block/custom_block/src/CustomBlockListBuilder.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockListBuilder.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityListBuilder;
-
-/**
- * Defines a class to build a listing of custom block entities.
- *
- * @see \Drupal\custom_block\Entity\CustomBlock
- */
-class CustomBlockListBuilder extends EntityListBuilder {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildHeader() {
-    $header['label'] = t('Block description');
-    return $header + parent::buildHeader();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildRow(EntityInterface $entity) {
-    $row['label'] = $this->getLabel($entity);
-    return $row + parent::buildRow($entity);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefaultOperations(EntityInterface $entity) {
-    $operations = parent::getDefaultOperations($entity);
-    if (isset($operations['edit'])) {
-      $operations['edit']['query']['destination'] = 'admin/structure/block/custom-blocks';
-    }
-    return $operations;
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockTranslationHandler.php b/core/modules/block/custom_block/src/CustomBlockTranslationHandler.php
deleted file mode 100644
index 2bdd7f0..0000000
--- a/core/modules/block/custom_block/src/CustomBlockTranslationHandler.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockTranslationHandler.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\content_translation\ContentTranslationHandler;
-
-/**
- * Defines the translation handler for custom blocks.
- */
-class CustomBlockTranslationHandler extends ContentTranslationHandler {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function entityFormAlter(array &$form, array &$form_state, EntityInterface $entity) {
-    parent::entityFormAlter($form, $form_state, $entity);
-    // Move the translation fieldset to a vertical tab.
-    if (isset($form['translation'])) {
-      $form['translation'] += array(
-        '#group' => 'additional_settings',
-        '#weight' => 100,
-        '#attributes' => array(
-          'class' => array('custom-block-translation-options'),
-        ),
-      );
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function entityFormTitle(EntityInterface $entity) {
-    $block_type = entity_load('custom_block_type', $entity->bundle());
-    return t('<em>Edit @type</em> @title', array('@type' => $block_type->label(), '@title' => $entity->label()));
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockTypeForm.php b/core/modules/block/custom_block/src/CustomBlockTypeForm.php
deleted file mode 100644
index 61567f8..0000000
--- a/core/modules/block/custom_block/src/CustomBlockTypeForm.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockTypeForm.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\EntityForm;
-use Drupal\Core\Entity\EntityTypeInterface;
-
-/**
- * Base form for category edit forms.
- */
-class CustomBlockTypeForm extends EntityForm {
-
-  /**
-   * Overrides \Drupal\Core\Entity\EntityForm::form().
-   */
-  public function form(array $form, array &$form_state) {
-    $form = parent::form($form, $form_state);
-
-    $block_type = $this->entity;
-
-    $form['label'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Label'),
-      '#maxlength' => 255,
-      '#default_value' => $block_type->label(),
-      '#description' => t("Provide a label for this block type to help identify it in the administration pages."),
-      '#required' => TRUE,
-    );
-    $form['id'] = array(
-      '#type' => 'machine_name',
-      '#default_value' => $block_type->id(),
-      '#machine_name' => array(
-        'exists' => '\Drupal\custom_block\Entity\CustomBlockType::load',
-      ),
-      '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
-      '#disabled' => !$block_type->isNew(),
-    );
-
-    $form['description'] = array(
-      '#type' => 'textarea',
-      '#default_value' => $block_type->description,
-      '#description' => t('Enter a description for this block type.'),
-      '#title' => t('Description'),
-    );
-
-    $form['revision'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Create new revision'),
-      '#default_value' => $block_type->revision,
-      '#description' => t('Create a new revision by default for this block type.')
-    );
-
-    if ($this->moduleHandler->moduleExists('content_translation')) {
-      $form['language'] = array(
-        '#type' => 'details',
-        '#title' => t('Language settings'),
-        '#group' => 'additional_settings',
-      );
-
-      $language_configuration = language_get_default_configuration('custom_block', $block_type->id());
-      $form['language']['language_configuration'] = array(
-        '#type' => 'language_configuration',
-        '#entity_information' => array(
-          'entity_type' => 'custom_block',
-          'bundle' => $block_type->id(),
-        ),
-        '#default_value' => $language_configuration,
-      );
-
-      $form['#submit'][] = 'language_configuration_element_submit';
-    }
-
-    $form['actions'] = array('#type' => 'actions');
-    $form['actions']['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Save'),
-    );
-
-    return $form;
-  }
-
-  /**
-   * Overrides \Drupal\Core\Entity\EntityForm::save().
-   */
-  public function save(array $form, array &$form_state) {
-    $block_type = $this->entity;
-    $status = $block_type->save();
-
-    $edit_link = \Drupal::linkGenerator()->generateFromUrl($this->t('Edit'), $this->entity->urlInfo());
-    if ($status == SAVED_UPDATED) {
-      drupal_set_message(t('Custom block type %label has been updated.', array('%label' => $block_type->label())));
-      watchdog('custom_block', 'Custom block type %label has been updated.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, $edit_link);
-    }
-    else {
-      drupal_set_message(t('Custom block type %label has been added.', array('%label' => $block_type->label())));
-      watchdog('custom_block', 'Custom block type %label has been added.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, $edit_link);
-    }
-
-    $form_state['redirect_route']['route_name'] = 'custom_block.type_list';
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockTypeInterface.php b/core/modules/block/custom_block/src/CustomBlockTypeInterface.php
deleted file mode 100644
index 53bfe1b..0000000
--- a/core/modules/block/custom_block/src/CustomBlockTypeInterface.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Entity\CustomBlockTypeInterface.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Config\Entity\ConfigEntityInterface;
-
-/**
- * Provides an interface defining a custom block type entity.
- */
-interface CustomBlockTypeInterface extends ConfigEntityInterface {
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockTypeListBuilder.php b/core/modules/block/custom_block/src/CustomBlockTypeListBuilder.php
deleted file mode 100644
index 723ea71..0000000
--- a/core/modules/block/custom_block/src/CustomBlockTypeListBuilder.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockTypeListBuilder.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Component\Utility\Xss;
-use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * Defines a class to build a listing of custom block type entities.
- *
- * @see \Drupal\custom_block\Entity\CustomBlockType
- */
-class CustomBlockTypeListBuilder extends ConfigEntityListBuilder {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefaultOperations(EntityInterface $entity) {
-    $operations = parent::getDefaultOperations($entity);
-    // Place the edit operation after the operations added by field_ui.module
-    // which have the weights 15, 20, 25.
-    if (isset($operations['edit'])) {
-      $operations['edit']['weight'] = 30;
-    }
-    return $operations;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildHeader() {
-    $header['type'] = t('Block type');
-    $header['description'] = t('Description');
-    return $header + parent::buildHeader();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildRow(EntityInterface $entity) {
-    $row['type'] = \Drupal::linkGenerator()->generateFromUrl($entity->label(), $entity->urlInfo());
-    $row['description'] = Xss::filterAdmin($entity->description);
-    return $row + parent::buildRow($entity);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getTitle() {
-    return $this->t('Custom block types');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/CustomBlockViewBuilder.php b/core/modules/block/custom_block/src/CustomBlockViewBuilder.php
deleted file mode 100644
index 16e11ee..0000000
--- a/core/modules/block/custom_block/src/CustomBlockViewBuilder.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockViewBuilder.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityViewBuilder;
-
-/**
- * Render controller for custom blocks.
- */
-class CustomBlockViewBuilder extends EntityViewBuilder {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) {
-    parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
-    // Add contextual links for this custom block.
-    if (!$entity->isNew() && $view_mode == 'full') {
-      $build['#contextual_links']['custom_block'] = array(
-        'route_parameters' => array('custom_block' => $entity->id()),
-        'metadata' => array('changed' => $entity->getChangedTime()),
-      );
-    }
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Entity/CustomBlock.php b/core/modules/block/custom_block/src/Entity/CustomBlock.php
deleted file mode 100644
index 71913b7..0000000
--- a/core/modules/block/custom_block/src/Entity/CustomBlock.php
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Entity\CustomBlock.
- */
-
-namespace Drupal\custom_block\Entity;
-
-use Drupal\Core\Entity\ContentEntityBase;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Field\FieldDefinition;
-use Drupal\custom_block\CustomBlockInterface;
-
-/**
- * Defines the custom block entity class.
- *
- * @ContentEntityType(
- *   id = "custom_block",
- *   label = @Translation("Custom Block"),
- *   bundle_label = @Translation("Custom Block type"),
- *   controllers = {
- *     "access" = "Drupal\custom_block\CustomBlockAccessController",
- *     "list_builder" = "Drupal\custom_block\CustomBlockListBuilder",
- *     "view_builder" = "Drupal\custom_block\CustomBlockViewBuilder",
- *     "form" = {
- *       "add" = "Drupal\custom_block\CustomBlockForm",
- *       "edit" = "Drupal\custom_block\CustomBlockForm",
- *       "delete" = "Drupal\custom_block\Form\CustomBlockDeleteForm",
- *       "default" = "Drupal\custom_block\CustomBlockForm"
- *     },
- *     "translation" = "Drupal\custom_block\CustomBlockTranslationHandler"
- *   },
- *   admin_permission = "administer blocks",
- *   base_table = "custom_block",
- *   revision_table = "custom_block_revision",
- *   links = {
- *     "canonical" = "custom_block.edit",
- *     "delete-form" = "custom_block.delete",
- *     "edit-form" = "custom_block.edit",
- *     "admin-form" = "custom_block.type_edit"
- *   },
- *   fieldable = TRUE,
- *   translatable = TRUE,
- *   entity_keys = {
- *     "id" = "id",
- *     "revision" = "revision_id",
- *     "bundle" = "type",
- *     "label" = "info",
- *     "uuid" = "uuid"
- *   },
- *   bundle_entity_type = "custom_block_type"
- * )
- */
-class CustomBlock extends ContentEntityBase implements CustomBlockInterface {
-
-  /**
-   * The theme the block is being created in.
-   *
-   * When creating a new custom block from the block library, the user is
-   * redirected to the configure form for that block in the given theme. The
-   * theme is stored against the block when the custom block add form is shown.
-   *
-   * @var string
-   */
-  protected $theme;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function createDuplicate() {
-    $duplicate = parent::createDuplicate();
-    $duplicate->revision_id->value = NULL;
-    $duplicate->id->value = NULL;
-    return $duplicate;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setTheme($theme) {
-    $this->theme = $theme;
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getTheme() {
-    return $this->theme;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    parent::postSave($storage, $update);
-
-    // Invalidate the block cache to update custom block-based derivatives.
-    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getInstances() {
-    return entity_load_multiple_by_properties('block', array('plugin' => 'custom_block:' . $this->uuid()));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
-    parent::preSaveRevision($storage, $record);
-
-    if ($this->isNewRevision()) {
-      // When inserting either a new custom block or a new custom_block
-      // revision, $entity->log must be set because {block_custom_revision}.log
-      // is a text column and therefore cannot have a default value. However,
-      // it might not be set at this point (for example, if the user submitting
-      // the form does not have permission to create revisions), so we ensure
-      // that it is at least an empty string in that case.
-      // @todo: Make the {block_custom_revision}.log column nullable so that we
-      // can remove this check.
-      if (!isset($record->log)) {
-        $record->log = '';
-      }
-    }
-    elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
-      // If we are updating an existing custom_block without adding a new
-      // revision and the user did not supply a log, keep the existing one.
-      $record->log = $this->original->getRevisionLog();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function delete() {
-    foreach ($this->getInstances() as $instance) {
-      $instance->delete();
-    }
-    parent::delete();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
-    $fields['id'] = FieldDefinition::create('integer')
-      ->setLabel(t('Custom block ID'))
-      ->setDescription(t('The custom block ID.'))
-      ->setReadOnly(TRUE)
-      ->setSetting('unsigned', TRUE);
-
-    $fields['uuid'] = FieldDefinition::create('uuid')
-      ->setLabel(t('UUID'))
-      ->setDescription(t('The custom block UUID.'))
-      ->setReadOnly(TRUE);
-
-    $fields['revision_id'] = FieldDefinition::create('integer')
-      ->setLabel(t('Revision ID'))
-      ->setDescription(t('The revision ID.'))
-      ->setReadOnly(TRUE)
-      ->setSetting('unsigned', TRUE);
-
-    $fields['langcode'] = FieldDefinition::create('language')
-      ->setLabel(t('Language code'))
-      ->setDescription(t('The custom block language code.'));
-
-    $fields['info'] = FieldDefinition::create('string')
-      ->setLabel(t('Block description'))
-      ->setDescription(t('A brief description of your block.'))
-      ->setRevisionable(TRUE)
-      ->setRequired(TRUE)
-      ->setDisplayOptions('form', array(
-        'type' => 'string',
-        'weight' => -5,
-      ))
-      ->setDisplayConfigurable('form', TRUE);
-
-    $fields['type'] = FieldDefinition::create('entity_reference')
-      ->setLabel(t('Block type'))
-      ->setDescription(t('The block type.'))
-      ->setSetting('target_type', 'custom_block_type')
-      ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
-
-    $fields['log'] = FieldDefinition::create('string')
-      ->setLabel(t('Revision log message'))
-      ->setDescription(t('The revision log message.'))
-      ->setRevisionable(TRUE);
-
-    $fields['changed'] = FieldDefinition::create('changed')
-      ->setLabel(t('Changed'))
-      ->setDescription(t('The time that the custom block was last edited.'))
-      ->setRevisionable(TRUE);
-
-    return $fields;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getChangedTime() {
-    return $this->get('changed')->value;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRevisionLog() {
-    return $this->get('log')->value;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setInfo($info) {
-    $this->set('info', $info);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setRevisionLog($log) {
-    $this->set('log', $log);
-    return $this;
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Entity/CustomBlockType.php b/core/modules/block/custom_block/src/Entity/CustomBlockType.php
deleted file mode 100644
index 65dd974..0000000
--- a/core/modules/block/custom_block/src/Entity/CustomBlockType.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Entity\CustomBlockType.
- */
-
-namespace Drupal\custom_block\Entity;
-
-use Drupal\Core\Config\Entity\ConfigEntityBase;
-use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\custom_block\CustomBlockTypeInterface;
-
-/**
- * Defines the custom block type entity.
- *
- * @ConfigEntityType(
- *   id = "custom_block_type",
- *   label = @Translation("Custom block type"),
- *   controllers = {
- *     "form" = {
- *       "default" = "Drupal\custom_block\CustomBlockTypeForm",
- *       "add" = "Drupal\custom_block\CustomBlockTypeForm",
- *       "edit" = "Drupal\custom_block\CustomBlockTypeForm",
- *       "delete" = "Drupal\custom_block\Form\CustomBlockTypeDeleteForm"
- *     },
- *     "list_builder" = "Drupal\custom_block\CustomBlockTypeListBuilder"
- *   },
- *   admin_permission = "administer blocks",
- *   config_prefix = "type",
- *   bundle_of = "custom_block",
- *   entity_keys = {
- *     "id" = "id",
- *     "label" = "label"
- *   },
- *   links = {
- *     "delete-form" = "custom_block.type_delete",
- *     "edit-form" = "custom_block.type_edit"
- *   }
- * )
- */
-class CustomBlockType extends ConfigEntityBundleBase implements CustomBlockTypeInterface {
-
-  /**
-   * The custom block type ID.
-   *
-   * @var string
-   */
-  public $id;
-
-  /**
-   * The custom block type label.
-   *
-   * @var string
-   */
-  public $label;
-
-  /**
-   * The default revision setting for custom blocks of this type.
-   *
-   * @var bool
-   */
-  public $revision;
-
-  /**
-   * The description of the block type.
-   *
-   * @var string
-   */
-  public $description;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    parent::postSave($storage, $update);
-
-    if (!$update && !$this->isSyncing()) {
-      if (!$this->isSyncing()) {
-        custom_block_add_body_field($this->id);
-      }
-    }
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Form/CustomBlockDeleteForm.php b/core/modules/block/custom_block/src/Form/CustomBlockDeleteForm.php
deleted file mode 100644
index 11d3935..0000000
--- a/core/modules/block/custom_block/src/Form/CustomBlockDeleteForm.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\block\Form\CustomBlockDeleteForm.
- */
-
-namespace Drupal\custom_block\Form;
-
-use Drupal\Core\Entity\ContentEntityConfirmFormBase;
-use Drupal\Core\Url;
-
-/**
- * Provides a confirmation form for deleting a custom block entity.
- */
-class CustomBlockDeleteForm extends ContentEntityConfirmFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelRoute() {
-    return new Url('block.admin_display');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, array &$form_state) {
-    $instances = $this->entity->getInstances();
-
-    $form['message'] = array(
-      '#markup' => format_plural(count($instances), 'This will also remove 1 placed block instance.', 'This will also remove @count placed block instances.'),
-      '#access' => !empty($instances),
-    );
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submit(array $form, array &$form_state) {
-    $this->entity->delete();
-    drupal_set_message($this->t('Custom block %label has been deleted.', array('%label' => $this->entity->label())));
-    watchdog('custom_block', 'Custom block %label has been deleted.', array('%label' => $this->entity->label()), WATCHDOG_NOTICE);
-    $form_state['redirect_route'] = new Url('custom_block.list');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Form/CustomBlockTypeDeleteForm.php b/core/modules/block/custom_block/src/Form/CustomBlockTypeDeleteForm.php
deleted file mode 100644
index c57d2ad..0000000
--- a/core/modules/block/custom_block/src/Form/CustomBlockTypeDeleteForm.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Form\CustomBlockTypeDeleteForm.
- */
-
-namespace Drupal\custom_block\Form;
-
-use Drupal\Core\Entity\EntityConfirmFormBase;
-use Drupal\Core\Entity\Query\QueryFactory;
-use Drupal\Core\Url;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a confirmation form for deleting a custom block type entity.
- */
-class CustomBlockTypeDeleteForm extends EntityConfirmFormBase {
-
-  /**
-   * The query factory to create entity queries.
-   *
-   * @var \Drupal\Core\Entity\Query\QueryFactory
-   */
-  public $queryFactory;
-
-  /**
-   * Constructs a query factory object.
-   *
-   * @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
-   *   The entity query object.
-   */
-  public function __construct(QueryFactory $query_factory) {
-    $this->queryFactory = $query_factory;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.query')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete %label?', array('%label' => $this->entity->label()));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelRoute() {
-    return new Url('custom_block.type_list');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, array &$form_state) {
-    $blocks = $this->queryFactory->get('custom_block')->condition('type', $this->entity->id())->execute();
-    if (!empty($blocks)) {
-      $caption = '<p>' . format_plural(count($blocks), '%label is used by 1 custom block on your site. You can not remove this block type until you have removed all of the %label blocks.', '%label is used by @count custom blocks on your site. You may not remove %label until you have removed all of the %label custom blocks.', array('%label' => $this->entity->label())) . '</p>';
-      $form['description'] = array('#markup' => $caption);
-      return $form;
-    }
-    else {
-      return parent::buildForm($form, $form_state);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submit(array $form, array &$form_state) {
-    $this->entity->delete();
-    drupal_set_message(t('Custom block type %label has been deleted.', array('%label' => $this->entity->label())));
-    watchdog('custom_block', 'Custom block type %label has been deleted.', array('%label' => $this->entity->label()), WATCHDOG_NOTICE);
-    $form_state['redirect_route'] = $this->getCancelRoute();
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Plugin/Block/CustomBlockBlock.php b/core/modules/block/custom_block/src/Plugin/Block/CustomBlockBlock.php
deleted file mode 100644
index 5ef3ec2..0000000
--- a/core/modules/block/custom_block/src/Plugin/Block/CustomBlockBlock.php
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Plugin\Block\CustomBlockBlock.
- */
-
-namespace Drupal\custom_block\Plugin\Block;
-
-use Drupal\block\BlockBase;
-use Drupal\block\BlockManagerInterface;
-use Drupal\Core\Entity\EntityManager;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines a generic custom block type.
- *
- * @Block(
- *  id = "custom_block",
- *  admin_label = @Translation("Custom block"),
- *  category = @Translation("Custom"),
- *  derivative = "Drupal\custom_block\Plugin\Derivative\CustomBlock"
- * )
- */
-class CustomBlockBlock extends BlockBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * The Plugin Block Manager.
-   *
-   * @var \Drupal\block\BlockManagerInterface.
-   */
-  protected $blockManager;
-
-  /**
-   * The entity manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The Module Handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface.
-   */
-  protected $moduleHandler;
-
-  /**
-   * The Drupal account to use for checking for access to block.
-   *
-   * @var \Drupal\Core\Session\AccountInterface.
-   */
-  protected $account;
-
-  /**
-   * Constructs a new CustomBlockBlock.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin ID for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\block\BlockManagerInterface
-   *   The Plugin Block Manager.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager service.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface
-   *   The Module Handler.
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The account for which view access should be checked.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockManagerInterface $block_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $account) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-
-    $this->blockManager = $block_manager;
-    $this->entityManager = $entity_manager;
-    $this->moduleHandler = $module_handler;
-    $this->account = $account;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.block'),
-      $container->get('entity.manager'),
-      $container->get('module_handler'),
-      $container->get('current_user')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return array(
-      'status' => TRUE,
-      'info' => '',
-      'view_mode' => 'full',
-      // Modify the default max age for custom block blocks: modifications made
-      // to them will automatically invalidate corresponding cache tags, thus
-      // allowing us to cache custom block blocks forever.
-      'cache' => array(
-        'max_age' => \Drupal\Core\Cache\Cache::PERMANENT,
-      ),
-    );
-  }
-
-  /**
-   * Overrides \Drupal\block\BlockBase::blockForm().
-   *
-   * Adds body and description fields to the block configuration form.
-   */
-  public function blockForm($form, &$form_state) {
-    $form['custom_block']['view_mode'] = array(
-      '#type' => 'select',
-      '#options' => $this->entityManager->getViewModeOptions('custom_block'),
-      '#title' => t('View mode'),
-      '#description' => t('Output the block in this view mode.'),
-      '#default_value' => $this->configuration['view_mode']
-    );
-    $form['title']['#description'] = t('The title of the block as shown to the user.');
-    return $form;
-  }
-
-  /**
-   * Overrides \Drupal\block\BlockBase::blockSubmit().
-   */
-  public function blockSubmit($form, &$form_state) {
-    // Invalidate the block cache to update custom block-based derivatives.
-    if ($this->moduleHandler->moduleExists('block')) {
-      $this->configuration['view_mode'] = $form_state['values']['custom_block']['view_mode'];
-      $this->blockManager->clearCachedDefinitions();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function build() {
-    $uuid = $this->getDerivativeId();
-    if ($block = entity_load_by_uuid('custom_block', $uuid)) {
-      return entity_view($block, $this->configuration['view_mode']);
-    }
-    else {
-      return array(
-        '#markup' => t('Block with uuid %uuid does not exist. <a href="!url">Add custom block</a>.', array(
-          '%uuid' => $uuid,
-          '!url' => url('block/add')
-        )),
-        '#access' => $this->account->hasPermission('administer blocks')
-      );
-    }
-  }
-}
diff --git a/core/modules/block/custom_block/src/Plugin/Derivative/CustomBlock.php b/core/modules/block/custom_block/src/Plugin/Derivative/CustomBlock.php
deleted file mode 100644
index f93b413..0000000
--- a/core/modules/block/custom_block/src/Plugin/Derivative/CustomBlock.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Plugin\Derivative\CustomBlock.
- */
-
-namespace Drupal\custom_block\Plugin\Derivative;
-
-use Drupal\Component\Plugin\Derivative\DerivativeBase;
-use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
-
-/**
- * Retrieves block plugin definitions for all custom blocks.
- */
-class CustomBlock extends DerivativeBase {
-  /**
-   * {@inheritdoc}
-   */
-  public function getDerivativeDefinitions($base_plugin_definition) {
-    $custom_blocks = entity_load_multiple('custom_block');
-    foreach ($custom_blocks as $custom_block) {
-      $this->derivatives[$custom_block->uuid()] = $base_plugin_definition;
-      $this->derivatives[$custom_block->uuid()]['admin_label'] = $custom_block->label();
-    }
-    return parent::getDerivativeDefinitions($base_plugin_definition);
-  }
-}
diff --git a/core/modules/block/custom_block/src/Plugin/Menu/LocalAction/CustomBlockAddLocalAction.php b/core/modules/block/custom_block/src/Plugin/Menu/LocalAction/CustomBlockAddLocalAction.php
deleted file mode 100644
index af9b359..0000000
--- a/core/modules/block/custom_block/src/Plugin/Menu/LocalAction/CustomBlockAddLocalAction.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Plugin\Menu\LocalAction\CustomBlockAddLocalAction.
- */
-
-namespace Drupal\custom_block\Plugin\Menu\LocalAction;
-
-use Drupal\Core\Menu\LocalActionDefault;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Modifies the 'Add custom block' local action.
- */
-class CustomBlockAddLocalAction extends LocalActionDefault {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOptions(Request $request) {
-    $options = parent::getOptions($request);
-    // If the route specifies a theme, append it to the query string.
-    if ($request->attributes->has('theme')) {
-      $options['query']['theme'] = $request->attributes->get('theme');
-    }
-    // Adds a destination on custom block listing.
-    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'custom_block.list') {
-      $options['query']['destination'] = 'admin/structure/block/custom-blocks';
-    }
-    // Adds a destination on custom block listing.
-    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'custom_block.list') {
-      $options['query']['destination'] = 'admin/structure/block/custom-blocks';
-    }
-    return $options;
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockCacheTagsTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockCacheTagsTest.php
deleted file mode 100644
index cfd87a9..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockCacheTagsTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockCacheTagsTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
-
-/**
- * Tests the Custom Block entity's cache tags.
- */
-class CustomBlockCacheTagsTest extends EntityCacheTagsTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = array('custom_block');
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getInfo() {
-    return parent::generateStandardizedInfo('Custom Block', 'Custom Block');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function createEntity() {
-    // Create a "Llama" custom block.
-    $custom_block = entity_create('custom_block', array(
-      'info' => 'Llama',
-      'type' => 'basic',
-      'body' => array(
-        'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
-        'format' => 'plain_text',
-      ),
-    ));
-    $custom_block->save();
-
-    return $custom_block;
-  }
-
-  /**
-   * {@inheritdoc}
-   *
-   * Each comment must have a comment body, which always has a text format.
-   */
-  protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
-    return array('filter_format:plain_text');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockCreationTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockCreationTest.php
deleted file mode 100644
index 0ae892a..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockCreationTest.php
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockCreationTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\Core\Database\Database;
-use Drupal\custom_block\Entity\CustomBlock;
-
-/**
- * Tests creating and saving a block.
- */
-class CustomBlockCreationTest extends CustomBlockTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * Enable dummy module that implements hook_block_insert() for exceptions.
-   *
-   * @var array
-   */
-  public static $modules = array('custom_block_test', 'dblog');
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block creation',
-      'description' => 'Create a block and test saving it.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Sets the test up.
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->drupalLogin($this->adminUser);
-  }
-
-  /**
-   * Creates a "Basic page" block and verifies its consistency in the database.
-   */
-  public function testCustomBlockCreation() {
-    // Add a new view mode and verify if it is selected as expected.
-    $this->drupalLogin($this->drupalCreateUser(array('administer display modes')));
-    $this->drupalGet('admin/structure/display-modes/view/add/custom_block');
-    $edit = array(
-      'id' => 'test_view_mode',
-      'label' => 'Test View Mode',
-    );
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $this->assertRaw(t('Saved the %label view mode.', array('%label' => $edit['label'])));
-
-    $this->drupalLogin($this->adminUser);
-
-    // Create a block.
-    $edit = array();
-    $edit['info[0][value]'] = 'Test Block';
-    $edit['body[0][value]'] = $this->randomName(16);
-    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
-
-    // Check that the Basic block has been created.
-    $this->assertRaw(format_string('!block %name has been created.', array(
-      '!block' => 'Basic block',
-      '%name' => $edit['info[0][value]']
-    )), 'Basic block created.');
-
-    // Change the view mode.
-    $view_mode['settings[custom_block][view_mode]'] = 'test_view_mode';
-    $this->drupalPostForm(NULL, $view_mode, t('Save block'));
-
-    // Go to the configure page and verify that the new view mode is correct.
-    $this->drupalGet('admin/structure/block/manage/testblock');
-    $this->assertFieldByXPath('//select[@name="settings[custom_block][view_mode]"]/option[@selected="selected"]/@value', 'test_view_mode', 'View mode changed to Test View Mode');
-
-    // Test the available view mode options.
-    $this->assertOption('edit-settings-custom-block-view-mode', 'default', 'The default view mode is available.');
-
-    // Check that the block exists in the database.
-    $blocks = entity_load_multiple_by_properties('custom_block', array('info' => $edit['info[0][value]']));
-    $block = reset($blocks);
-    $this->assertTrue($block, 'Custom Block found in database.');
-
-    // Check that attempting to create another block with the same value for
-    // 'info' returns an error.
-    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
-
-    // Check that the Basic block has been created.
-    $this->assertRaw(format_string('A block with description %name already exists.', array(
-      '%name' => $edit['info[0][value]']
-    )));
-    $this->assertResponse(200);
-  }
-
-  /**
-   * Create a default custom block.
-   *
-   * Creates a custom block from defaults and ensures that the 'basic block'
-   * type is being used.
-   */
-  public function testDefaultCustomBlockCreation() {
-    $edit = array();
-    $edit['info[0][value]'] = $this->randomName(8);
-    $edit['body[0][value]'] = $this->randomName(16);
-    // Don't pass the custom block type in the url so the default is forced.
-    $this->drupalPostForm('block/add', $edit, t('Save'));
-
-    // Check that the block has been created and that it is a basic block.
-    $this->assertRaw(format_string('!block %name has been created.', array(
-      '!block' => 'Basic block',
-      '%name' => $edit['info[0][value]'],
-    )), 'Basic block created.');
-
-    // Check that the block exists in the database.
-    $blocks = entity_load_multiple_by_properties('custom_block', array('info' => $edit['info[0][value]']));
-    $block = reset($blocks);
-    $this->assertTrue($block, 'Default Custom Block found in database.');
-  }
-
-  /**
-   * Verifies that a transaction rolls back the failed creation.
-   */
-  public function testFailedBlockCreation() {
-    // Create a block.
-    try {
-      $this->createCustomBlock('fail_creation');
-      $this->fail('Expected exception has not been thrown.');
-    }
-    catch (\Exception $e) {
-      $this->pass('Expected exception has been thrown.');
-    }
-
-    if (Database::getConnection()->supportsTransactions()) {
-      // Check that the block does not exist in the database.
-      $id = db_select('custom_block', 'b')
-        ->fields('b', array('id'))
-        ->condition('info', 'fail_creation')
-        ->execute()
-        ->fetchField();
-      $this->assertFalse($id, 'Transactions supported, and block not found in database.');
-    }
-    else {
-      // Check that the block exists in the database.
-      $id = db_select('custom_block', 'b')
-        ->fields('b', array('id'))
-        ->condition('info', 'fail_creation')
-        ->execute()
-        ->fetchField();
-      $this->assertTrue($id, 'Transactions not supported, and block found in database.');
-
-      // Check that the failed rollback was logged.
-      $records = db_query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll();
-      $this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.');
-    }
-  }
-
-  /**
-   * Test deleting a block.
-   */
-  public function testBlockDelete() {
-    // Create a block.
-    $edit = array();
-    $edit['info[0][value]'] = $this->randomName(8);
-    $body = $this->randomName(16);
-    $edit['body[0][value]'] = $body;
-    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
-
-    // Place the block.
-    $instance = array(
-      'id' => drupal_strtolower($edit['info[0][value]']),
-      'settings[label]' => $edit['info[0][value]'],
-      'region' => 'sidebar_first',
-    );
-    $block = entity_load('custom_block', 1);
-    $url = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
-    $this->drupalPostForm($url, $instance, t('Save block'));
-
-    $block = CustomBlock::load(1);
-
-    // Test getInstances method.
-    $this->assertEqual(1, count($block->getInstances()));
-
-    // Navigate to home page.
-    $this->drupalGet('');
-    $this->assertText($body);
-
-    // Delete the block.
-    $this->drupalGet('block/1/delete');
-    $this->assertText(format_plural(1, 'This will also remove 1 placed block instance.', 'This will also remove @count placed block instance.'));
-
-    $this->drupalPostForm(NULL, array(), 'Delete');
-    $this->assertRaw(t('Custom block %name has been deleted.', array('%name' => $edit['info[0][value]'])));
-
-    // Create another block and force the plugin cache to flush.
-    $edit2 = array();
-    $edit2['info[0][value]'] = $this->randomName(8);
-    $body2 = $this->randomName(16);
-    $edit2['body[0][value]'] = $body2;
-    $this->drupalPostForm('block/add/basic', $edit2, t('Save'));
-
-    $this->assertNoRaw('Error message');
-
-    // Create another block with no instances, and test we don't get a
-    // confirmation message about deleting instances.
-    $edit3 = array();
-    $edit3['info[0][value]'] = $this->randomName(8);
-    $body = $this->randomName(16);
-    $edit3['body[0][value]'] = $body;
-    $this->drupalPostForm('block/add/basic', $edit3, t('Save'));
-
-    // Show the delete confirm form.
-    $this->drupalGet('block/3/delete');
-    $this->assertNoText('This will also remove');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockFieldTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockFieldTest.php
deleted file mode 100644
index 5c7d46b..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockFieldTest.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockFieldTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-/**
- * Tests the block edit functionality.
- *
- * @todo Consider removing this test when https://drupal.org/node/1822000 is
- * fixed.
- */
-class CustomBlockFieldTest extends CustomBlockTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('block', 'custom_block', 'link');
-
-  /**
-   * The created field.
-   *
-   * @var \Drupal\field\Entity\FieldConfig
-   */
-  protected $field;
-
-  /**
-   * The created instance.
-   *
-   * @var \Drupal\field\Entity\FieldInstanceConfig
-   */
-  protected $instance;
-
-  /**
-   * The block type.
-   *
-   * @var \Drupal\custom_block\Entity\CustomBlockType
-   */
-  protected $blockType;
-
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block field test',
-      'description' => 'Test block fieldability.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Checks block edit functionality.
-   */
-  public function testBlockFields() {
-    $this->drupalLogin($this->adminUser);
-
-    $this->blockType = $this->createCustomBlockType('link');
-
-    // Create a field with settings to validate.
-    $this->field = entity_create('field_config', array(
-      'name' => drupal_strtolower($this->randomName()),
-      'entity_type' => 'custom_block',
-      'type' => 'link',
-      'cardinality' => 2,
-    ));
-    $this->field->save();
-    $this->instance = entity_create('field_instance_config', array(
-      'field_name' => $this->field->getName(),
-      'entity_type' => 'custom_block',
-      'bundle' => 'link',
-      'settings' => array(
-        'title' => DRUPAL_OPTIONAL,
-      ),
-    ));
-    $this->instance->save();
-    entity_get_form_display('custom_block', 'link', 'default')
-      ->setComponent($this->field->getName(), array(
-        'type' => 'link_default',
-      ))
-      ->save();
-    entity_get_display('custom_block', 'link', 'default')
-      ->setComponent($this->field->getName(), array(
-        'type' => 'link',
-        'label' => 'hidden',
-      ))
-      ->save();
-
-    // Create a block.
-    $this->drupalGet('block/add/link');
-    $edit = array(
-      'info[0][value]' => $this->randomName(8),
-      $this->field->getName() . '[0][url]' => 'http://example.com',
-      $this->field->getName() . '[0][title]' => 'Example.com'
-    );
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $block = entity_load('custom_block', 1);
-    $url = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
-    // Place the block.
-    $instance = array(
-      'id' => drupal_strtolower($edit['info[0][value]']),
-      'settings[label]' => $edit['info[0][value]'],
-      'region' => 'sidebar_first',
-    );
-    $this->drupalPostForm($url, $instance, t('Save block'));
-    // Navigate to home page.
-    $this->drupalGet('<front>');
-    $this->assertLinkByHref('http://example.com');
-    $this->assertText('Example.com');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockListTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockListTest.php
deleted file mode 100644
index 8bded49..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockListTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockListTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests the listing of custom blocks.
- *
- * @see \Drupal\block\CustomBlockListBuilder
- */
-class CustomBlockListTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('block', 'custom_block', 'config_translation');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block listing',
-      'description' => 'Tests the listing of custom blocks.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Tests the custom block listing page.
-   */
-  public function testListing() {
-    $this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'translate configuration')));
-    $this->drupalGet('admin/structure/block/custom-blocks');
-
-    // Test for the page title.
-    $this->assertTitle(t('Custom block library') . ' | Drupal');
-
-    // Test for the table.
-    $element = $this->xpath('//div[@class="l-content"]//table');
-    $this->assertTrue($element, 'Configuration entity list table found.');
-
-    // Test the table header.
-    $elements = $this->xpath('//div[@class="l-content"]//table/thead/tr/th');
-    $this->assertEqual(count($elements), 2, 'Correct number of table header cells found.');
-
-    // Test the contents of each th cell.
-    $expected_items = array(t('Block description'), t('Operations'));
-    foreach ($elements as $key => $element) {
-      $this->assertIdentical((string) $element[0], $expected_items[$key]);
-    }
-
-    $label = 'Antelope';
-    $new_label = 'Albatross';
-    // Add a new entity using the operations link.
-    $link_text = t('Add custom block');
-    $this->assertLink($link_text);
-    $this->clickLink($link_text);
-    $this->assertResponse(200);
-    $edit = array();
-    $edit['info[0][value]'] = $label;
-    $edit['body[0][value]'] = $this->randomName(16);
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-
-    // Confirm that once the user returns to the listing, the text of the label
-    // (versus elsewhere on the page).
-    $this->assertFieldByXpath('//td', $label, 'Label found for added block.');
-
-    // Check the number of table row cells.
-    $elements = $this->xpath('//div[@class="l-content"]//table/tbody/tr[@class="odd"]/td');
-    $this->assertEqual(count($elements), 2, 'Correct number of table row cells found.');
-    // Check the contents of each row cell. The first cell contains the label,
-    // the second contains the machine name, and the third contains the
-    // operations list.
-    $this->assertIdentical((string) $elements[0], $label);
-
-    // Edit the entity using the operations link.
-    $blocks = $this->container
-      ->get('entity.manager')
-      ->getStorage('custom_block')
-      ->loadByProperties(array('info' => $label));
-    $block = reset($blocks);
-    if (!empty($block)) {
-      $this->assertLinkByHref('block/' . $block->id());
-      $this->clickLink(t('Edit'));
-      $this->assertResponse(200);
-      $this->assertTitle(strip_tags(t('Edit custom block %label', array('%label' => $label)) . ' | Drupal'));
-      $edit = array('info[0][value]' => $new_label);
-      $this->drupalPostForm(NULL, $edit, t('Save'));
-    }
-    else {
-      $this->fail('Did not find Albatross block in the database.');
-    }
-
-    // Confirm that once the user returns to the listing, the text of the label
-    // (versus elsewhere on the page).
-    $this->assertFieldByXpath('//td', $new_label, 'Label found for updated custom block.');
-
-    // Delete the added entity using the operations link.
-    $this->assertLinkByHref('block/' . $block->id() . '/delete');
-    $delete_text = t('Delete');
-    $this->clickLink($delete_text);
-    $this->assertResponse(200);
-    $this->assertTitle(strip_tags(t('Are you sure you want to delete %label?', array('%label' => $new_label)) . ' | Drupal'));
-    $this->drupalPostForm(NULL, array(), $delete_text);
-
-    // Verify that the text of the label and machine name does not appear in
-    // the list (though it may appear elsewhere on the page).
-    $this->assertNoFieldByXpath('//td', $new_label, 'No label found for deleted custom block.');
-
-    // Confirm that the empty text is displayed.
-    $this->assertText(t('There is no Custom Block yet.'));
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockPageViewTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockPageViewTest.php
deleted file mode 100644
index fafb357..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockPageViewTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockPageViewTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-/**
- * Tests the block edit functionality.
- */
-class CustomBlockPageViewTest extends CustomBlockTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('block', 'custom_block', 'custom_block_test');
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block page view',
-      'description' => 'Create a block and test block access by attempting to view the block.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Checks block edit functionality.
-   */
-  public function testPageEdit() {
-    $this->drupalLogin($this->adminUser);
-    $block = $this->createCustomBlock();
-
-    // Attempt to view the block.
-    $this->drupalGet('custom-block/' . $block->id());
-
-    // Assert response was '200' and not '403 Access denied'.
-    $this->assertResponse('200', 'User was able the view the block');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockRevisionsTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockRevisionsTest.php
deleted file mode 100644
index 75f19aa..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockRevisionsTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockRevisionsTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-/**
- * Tests the block revision functionality.
- */
-class CustomBlockRevisionsTest extends CustomBlockTestBase {
-
-  /**
-   * Stores blocks created during the test.
-   * @var array
-   */
-  protected $blocks;
-
-  /**
-   * Stores log messages used during the test.
-   * @var array
-   */
-  protected $logs;
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block revisions',
-      'description' => 'Create a block with revisions.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Sets the test up.
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    // Create initial block.
-    $block = $this->createCustomBlock('initial');
-
-    $blocks = array();
-    $logs = array();
-
-    // Get original block.
-    $blocks[] = $block->getRevisionId();
-    $logs[] = '';
-
-    // Create three revisions.
-    $revision_count = 3;
-    for ($i = 0; $i < $revision_count; $i++) {
-      $block->setNewRevision(TRUE);
-      $block->setRevisionLog($this->randomName(32));
-      $logs[] = $block->getRevisionLog();
-      $block->save();
-      $blocks[] = $block->getRevisionId();
-    }
-
-    $this->blocks = $blocks;
-    $this->logs = $logs;
-  }
-
-  /**
-   * Checks block revision related operations.
-   */
-  public function testRevisions() {
-    $blocks = $this->blocks;
-    $logs = $this->logs;
-
-    foreach ($blocks as $delta => $revision_id) {
-      // Confirm the correct revision text appears.
-      $loaded = entity_revision_load('custom_block', $revision_id);
-      // Verify log is the same.
-      $this->assertEqual($loaded->getRevisionLog(), $logs[$delta], format_string('Correct log message found for revision !revision', array(
-        '!revision' => $loaded->getRevisionId(),
-      )));
-    }
-
-    // Confirm that this is the default revision.
-    $this->assertTrue($loaded->isDefaultRevision(), 'Third block revision is the default one.');
-
-    // Make a new revision and set it to not be default.
-    // This will create a new revision that is not "front facing".
-    // Save this as a non-default revision.
-    $loaded->setNewRevision();
-    $loaded->isDefaultRevision(FALSE);
-    $loaded->body = $this->randomName(8);
-    $loaded->save();
-
-    $this->drupalGet('block/' . $loaded->id());
-    $this->assertNoText($loaded->body->value, 'Revision body text is not present on default version of block.');
-
-    // Verify that the non-default revision id is greater than the default
-    // revision id.
-    $default_revision = entity_load('custom_block', $loaded->id());
-    $this->assertTrue($loaded->getRevisionId() > $default_revision->getRevisionId(), 'Revision id is greater than default revision id.');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockSaveTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockSaveTest.php
deleted file mode 100644
index bae766a..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockSaveTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockSaveTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\Core\Language\Language;
-use Drupal\custom_block\Entity\CustomBlock;
-
-/**
- * Tests block save related functionality.
- */
-class CustomBlockSaveTest extends CustomBlockTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('custom_block_test');
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block save',
-      'description' => 'Test $custom_block->save() for saving content.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Sets the test up.
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    $this->drupalLogin($this->adminUser);
-  }
-
-  /**
-   * Checks whether custom block IDs are saved properly during an import.
-   */
-  public function testImport() {
-    // Custom block ID must be a number that is not in the database.
-    $max_id = db_query('SELECT MAX(id) FROM {custom_block}')->fetchField();
-    $test_id = $max_id + mt_rand(1000, 1000000);
-    $info = $this->randomName(8);
-    $block_array = array(
-      'info' => $info,
-      'body' => array('value' => $this->randomName(32)),
-      'type' => 'basic',
-      'id' => $test_id
-    );
-    $block = entity_create('custom_block', $block_array);
-    $block->enforceIsNew(TRUE);
-    $block->save();
-
-    // Verify that block_submit did not wipe the provided id.
-    $this->assertEqual($block->id(), $test_id, 'Block imported using provide id');
-
-    // Test the import saved.
-    $block_by_id = CustomBlock::load($test_id);
-    $this->assertTrue($block_by_id, 'Custom block load by block ID.');
-    $this->assertIdentical($block_by_id->body->value, $block_array['body']['value']);
-  }
-
-  /**
-   * Tests determing changes in hook_block_presave().
-   *
-   * Verifies the static block load cache is cleared upon save.
-   */
-  public function testDeterminingChanges() {
-    // Initial creation.
-    $block = $this->createCustomBlock('test_changes');
-    $this->assertEqual($block->getChangedTime(), REQUEST_TIME, 'Creating a block sets default "changed" timestamp.');
-
-    // Update the block without applying changes.
-    $block->save();
-    $this->assertEqual($block->label(), 'test_changes', 'No changes have been determined.');
-
-    // Apply changes.
-    $block->setInfo('updated');
-    $block->save();
-
-    // The hook implementations custom_block_test_custom_block_presave() and
-    // custom_block_test_custom_block_update() determine changes and change the
-    // title as well as programatically set the 'changed' timestamp.
-    $this->assertEqual($block->label(), 'updated_presave_update', 'Changes have been determined.');
-    $this->assertEqual($block->getChangedTime(), 979534800, 'Saving a custom block uses "changed" timestamp set in presave hook.');
-
-    // Test the static block load cache to be cleared.
-    $block = CustomBlock::load($block->id());
-    $this->assertEqual($block->label(), 'updated_presave', 'Static cache has been cleared.');
-  }
-
-  /**
-   * Tests saving a block on block insert.
-   *
-   * This test ensures that a block has been fully saved when
-   * hook_custom_block_insert() is invoked, so that the block can be saved again
-   * in a hook implementation without errors.
-   *
-   * @see block_test_block_insert()
-   */
-  public function testCustomBlockSaveOnInsert() {
-    // custom_block_test_custom_block_insert() triggers a save on insert if the
-    // title equals 'new'.
-    $block = $this->createCustomBlock('new');
-    $this->assertEqual($block->label(), 'CustomBlock ' . $block->id(), 'Custom block saved on block insert.');
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockTestBase.php b/core/modules/block/custom_block/src/Tests/CustomBlockTestBase.php
deleted file mode 100644
index cf731fc..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockTestBase.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockTestBase.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Sets up page and article content types.
- */
-abstract class CustomBlockTestBase extends WebTestBase {
-
-  /**
-   * Profile to use.
-   */
-  protected $profile = 'testing';
-
-  /**
-   * Admin user
-   *
-   * @var object
-   */
-  protected $adminUser;
-
-  /**
-   * Permissions to grant admin user.
-   *
-   * @var array
-   */
-  protected $permissions = array(
-    'administer blocks'
-  );
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('block', 'custom_block');
-
-  /**
-   * Sets the test up.
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->adminUser = $this->drupalCreateUser($this->permissions);
-  }
-
-  /**
-   * Creates a custom block.
-   *
-   * @param string $title
-   *   (optional) Title of block. When no value is given uses a random name.
-   *   Defaults to FALSE.
-   * @param string $bundle
-   *   (optional) Bundle name. Defaults to 'basic'.
-   *
-   * @return \Drupal\custom_block\Entity\CustomBlock
-   *   Created custom block.
-   */
-  protected function createCustomBlock($title = FALSE, $bundle = 'basic') {
-    $title = ($title ? : $this->randomName());
-    if ($custom_block = entity_create('custom_block', array(
-      'info' => $title,
-      'type' => $bundle,
-      'langcode' => 'en'
-    ))) {
-      $custom_block->save();
-    }
-    return $custom_block;
-  }
-
-  /**
-   * Creates a custom block type (bundle).
-   *
-   * @param string $label
-   *   The block type label.
-   *
-   * @return \Drupal\custom_block\Entity\CustomBlockType
-   *   Created custom block type.
-   */
-  protected function createCustomBlockType($label) {
-    $bundle = entity_create('custom_block_type', array(
-      'id' => $label,
-      'label' => $label,
-      'revision' => FALSE
-    ));
-    $bundle->save();
-    return $bundle;
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockTranslationUITest.php b/core/modules/block/custom_block/src/Tests/CustomBlockTranslationUITest.php
deleted file mode 100644
index 761936e..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockTranslationUITest.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockTranslationUITest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\content_translation\Tests\ContentTranslationUITest;
-use Drupal\custom_block\Entity\CustomBlock;
-
-/**
- * Tests the Custom Block Translation UI.
- */
-class CustomBlockTranslationUITest extends ContentTranslationUITest {
-
-  /**
-   * The name of the test block.
-   */
-  protected $name;
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array(
-    'language',
-    'content_translation',
-    'block',
-    'field_ui',
-    'custom_block'
-  );
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block translation UI',
-      'description' => 'Tests the node translation UI.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Overrides \Drupal\simpletest\WebTestBase::setUp().
-   */
-  public function setUp() {
-    $this->entityTypeId = 'custom_block';
-    $this->bundle = 'basic';
-    $this->name = drupal_strtolower($this->randomName());
-    $this->testLanguageSelector = FALSE;
-    parent::setUp();
-  }
-
-  /**
-   * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getTranslatorPermission().
-   */
-  public function getTranslatorPermissions() {
-    return array_merge(parent::getTranslatorPermissions(), array(
-      'translate any entity',
-      'access administration pages',
-      'administer blocks',
-      'administer custom_block fields'
-    ));
-  }
-
-  /**
-   * Creates a custom block.
-   *
-   * @param string $title
-   *   (optional) Title of block. When no value is given uses a random name.
-   *   Defaults to FALSE.
-   * @param string $bundle
-   *   (optional) Bundle name. When no value is given, defaults to
-   *   $this->bundle. Defaults to FALSE.
-   *
-   * @return \Drupal\custom_block\Entity\CustomBlock
-   *   Created custom block.
-   */
-  protected function createCustomBlock($title = FALSE, $bundle = FALSE) {
-    $title = ($title ? : $this->randomName());
-    $bundle = ($bundle ? : $this->bundle);
-    $custom_block = entity_create('custom_block', array(
-      'info' => $title,
-      'type' => $bundle,
-      'langcode' => 'en'
-    ));
-    $custom_block->save();
-    return $custom_block;
-  }
-
-  /**
-   * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getNewEntityValues().
-   */
-  protected function getNewEntityValues($langcode) {
-    return array('info' => $this->name) + parent::getNewEntityValues($langcode);
-  }
-
-  /**
-   * Returns an edit array containing the values to be posted.
-   */
-  protected function getEditValues($values, $langcode, $new = FALSE) {
-    $edit = parent::getEditValues($values, $langcode, $new);
-    foreach ($edit as $property => $value) {
-      if ($property == 'info') {
-        $edit['info[0][value]'] = $value;
-        unset($edit[$property]);
-      }
-    }
-    return $edit;
-  }
-
-  /**
-   * Test that no metadata is stored for a disabled bundle.
-   */
-  public function testDisabledBundle() {
-    // Create a bundle that does not have translation enabled.
-    $disabled_bundle = $this->randomName();
-    $bundle = entity_create('custom_block_type', array(
-      'id' => $disabled_bundle,
-      'label' => $disabled_bundle,
-      'revision' => FALSE
-    ));
-    $bundle->save();
-
-    // Create a node for each bundle.
-    $enabled_custom_block = $this->createCustomBlock();
-    $disabled_custom_block = $this->createCustomBlock(FALSE, $bundle->id());
-
-    // Make sure that only a single row was inserted into the
-    // {content_translation} table.
-    $rows = db_query('SELECT * FROM {content_translation}')->fetchAll();
-    $this->assertEqual(1, count($rows));
-    $this->assertEqual($enabled_custom_block->id(), reset($rows)->entity_id);
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/CustomBlockTypeTest.php b/core/modules/block/custom_block/src/Tests/CustomBlockTypeTest.php
deleted file mode 100644
index c9c9187..0000000
--- a/core/modules/block/custom_block/src/Tests/CustomBlockTypeTest.php
+++ /dev/null
@@ -1,213 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\CustomBlockTypeTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-/**
- * Tests related to custom block types.
- */
-class CustomBlockTypeTest extends CustomBlockTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('field_ui');
-
-  /**
-   * Permissions to grant admin user.
-   *
-   * @var array
-   */
-  protected $permissions = array(
-    'administer blocks',
-    'administer custom_block fields'
-  );
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'CustomBlock types',
-      'description' => 'Ensures that custom block type functions work correctly.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Tests creating a block type programmatically and via a form.
-   */
-  public function testCustomBlockTypeCreation() {
-    // Create a block type programmaticaly.
-    $type = $this->createCustomBlockType('other');
-
-    $block_type = entity_load('custom_block_type', 'other');
-    $this->assertTrue($block_type, 'The new block type has been created.');
-
-    // Login a test user.
-    $this->drupalLogin($this->adminUser);
-
-    $this->drupalGet('block/add/' . $type->id());
-    $this->assertResponse(200, 'The new block type can be accessed at bloack/add.');
-
-    // Create a block type via the user interface.
-    $edit = array(
-      'id' => 'foo',
-      'label' => 'title for foo',
-    );
-    $this->drupalPostForm('admin/structure/block/custom-blocks/types/add', $edit, t('Save'));
-    $block_type = entity_load('custom_block_type', 'foo');
-    $this->assertTrue($block_type, 'The new block type has been created.');
-
-    // Check that the block type was created in site default language.
-    $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->id;
-    $this->assertEqual($block_type->langcode, $default_langcode);
-  }
-
-  /**
-   * Tests editing a block type using the UI.
-   */
-  public function testCustomBlockTypeEditing() {
-    $this->drupalLogin($this->adminUser);
-    // We need two block types to prevent /block/add redirecting.
-    $this->createCustomBlockType('other');
-
-    $field_definition = \Drupal::entityManager()->getFieldDefinitions('custom_block', 'other')['body'];
-    $this->assertEqual($field_definition->getLabel(), 'Body', 'Body field was found.');
-
-    // Verify that title and body fields are displayed.
-    $this->drupalGet('block/add/basic');
-    $this->assertRaw('Block description', 'Block info field was found.');
-    $this->assertRaw('Body', 'Body field was found.');
-
-    // Change the block type name.
-    $edit = array(
-      'label' => 'Bar',
-    );
-    $this->drupalPostForm('admin/structure/block/custom-blocks/manage/basic', $edit, t('Save'));
-
-    $this->drupalGet('block/add');
-    $this->assertRaw('Bar', 'New name was displayed.');
-    $this->clickLink('Bar');
-    $this->assertEqual(url('block/add/basic', array('absolute' => TRUE)), $this->getUrl(), 'Original machine name was used in URL.');
-
-    // Remove the body field.
-    $this->drupalPostForm('admin/structure/block/custom-blocks/manage/basic/fields/custom_block.basic.body/delete', array(), t('Delete'));
-    // Resave the settings for this type.
-    $this->drupalPostForm('admin/structure/block/custom-blocks/manage/basic', array(), t('Save'));
-    // Check that the body field doesn't exist.
-    $this->drupalGet('block/add/basic');
-    $this->assertNoRaw('Body', 'Body field was not found.');
-  }
-
-  /**
-   * Tests deleting a block type that still has content.
-   */
-  public function testCustomBlockTypeDeletion() {
-    // Create a block type programmatically.
-    $type = $this->createCustomBlockType('foo');
-
-    $this->drupalLogin($this->adminUser);
-
-    // Add a new block of this type.
-    $block = $this->createCustomBlock(FALSE, 'foo');
-    // Attempt to delete the block type, which should not be allowed.
-    $this->drupalGet('admin/structure/block/custom-blocks/manage/' . $type->id() . '/delete');
-    $this->assertRaw(
-      t('%label is used by 1 custom block on your site. You can not remove this block type until you have removed all of the %label blocks.', array('%label' => $type->label())),
-      'The block type will not be deleted until all blocks of that type are removed.'
-    );
-    $this->assertNoText(t('This action cannot be undone.'), 'The node type deletion confirmation form is not available.');
-
-    // Delete the block.
-    $block->delete();
-    // Attempt to delete the block type, which should now be allowed.
-    $this->drupalGet('admin/structure/block/custom-blocks/manage/' . $type->id() . '/delete');
-    $this->assertRaw(
-      t('Are you sure you want to delete %type?', array('%type' => $type->id())),
-      'The block type is available for deletion.'
-    );
-    $this->assertText(t('This action cannot be undone.'), 'The custom block type deletion confirmation form is available.');
-  }
-
-  /**
-   * Tests that redirects work as expected when multiple block types exist.
-   */
-  public function testsCustomBlockAddTypes() {
-    $this->drupalLogin($this->adminUser);
-    // Create two block types programmatically.
-    $type = $this->createCustomBlockType('foo');
-    $type = $this->createCustomBlockType('bar');
-
-    // Get the custom block storage.
-    $storage = $this->container
-      ->get('entity.manager')
-      ->getStorage('custom_block');
-
-    // Enable all themes.
-    theme_enable(array('bartik', 'seven'));
-    $themes = array('bartik', 'seven', 'stark');
-    $theme_settings = $this->container->get('config.factory')->get('system.theme');
-    foreach ($themes as $default_theme) {
-      // Change the default theme.
-      $theme_settings->set('default', $default_theme)->save();
-      \Drupal::service('router.builder')->rebuild();
-
-      // For each enabled theme, go to its block page and test the redirects.
-      $themes = array('bartik', 'stark', 'seven');
-      foreach ($themes as $theme) {
-        // Test that adding a block from the 'place blocks' form sends you to the
-        // block configure form.
-        $path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme";
-        $this->drupalGet($path);
-        $this->clickLink(t('Add custom block'));
-        // The seven theme has markup inside the link, we cannot use clickLink().
-        if ($default_theme == 'seven') {
-          $options = $theme != $default_theme ? array('query' => array('theme' => $theme)) : array();
-          $this->assertLinkByHref(url('block/add/foo', $options));
-          $this->drupalGet('block/add/foo', $options);
-        }
-        else {
-          $this->clickLink('foo');
-        }
-        // Create a new block.
-        $edit = array('info[0][value]' => $this->randomName(8));
-        $this->drupalPostForm(NULL, $edit, t('Save'));
-        $blocks = $storage->loadByProperties(array('info' => $edit['info[0][value]']));
-        if (!empty($blocks)) {
-          $block = reset($blocks);
-          $destination = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . $theme;
-          $this->assertUrl(url($destination, array('absolute' => TRUE)));
-          $this->drupalPostForm(NULL, array(), t('Save block'));
-          $this->assertUrl(url("admin/structure/block/list/$theme", array('absolute' => TRUE, 'query' => array('block-placement' => drupal_html_class($edit['info[0][value]'])))));
-        }
-        else {
-          $this->fail('Could not load created block.');
-        }
-      }
-    }
-
-    // Test that adding a block from the 'custom blocks list' doesn't send you
-    // to the block configure form.
-    $this->drupalGet('admin/structure/block/custom-blocks');
-    $this->clickLink(t('Add custom block'));
-    $this->clickLink('foo');
-    $edit = array('info[0][value]' => $this->randomName(8));
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $blocks = $storage->loadByProperties(array('info' => $edit['info[0][value]']));
-    if (!empty($blocks)) {
-      $destination = 'admin/structure/block/custom-blocks';
-      $this->assertUrl(url($destination, array('absolute' => TRUE)));
-    }
-    else {
-      $this->fail('Could not load created block.');
-    }
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Tests/PageEditTest.php b/core/modules/block/custom_block/src/Tests/PageEditTest.php
deleted file mode 100644
index 8062fb5..0000000
--- a/core/modules/block/custom_block/src/Tests/PageEditTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\PageEditTest.
- */
-
-namespace Drupal\custom_block\Tests;
-
-use Drupal\Core\Language\Language;
-
-/**
- * Tests the block edit functionality.
- */
-class PageEditTest extends CustomBlockTestBase {
-
-  /**
-   * Declares test information.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block edit',
-      'description' => 'Create a block and test block edit functionality.',
-      'group' => 'Custom Block',
-    );
-  }
-
-  /**
-   * Checks block edit functionality.
-   */
-  public function testPageEdit() {
-    $this->drupalLogin($this->adminUser);
-
-    $title_key = 'info[0][value]';
-    $body_key = 'body[0][value]';
-    // Create block to edit.
-    $edit = array();
-    $edit['info[0][value]'] = drupal_strtolower($this->randomName(8));
-    $edit[$body_key] = $this->randomName(16);
-    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
-
-    // Check that the block exists in the database.
-    $blocks = \Drupal::entityQuery('custom_block')->condition('info', $edit['info[0][value]'])->execute();
-    $block = entity_load('custom_block', reset($blocks));
-    $this->assertTrue($block, 'Custom block found in database.');
-
-    // Load the edit page.
-    $this->drupalGet('block/' . $block->id());
-    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
-    $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
-
-    // Edit the content of the block.
-    $edit = array();
-    $edit[$title_key] = $this->randomName(8);
-    $edit[$body_key] = $this->randomName(16);
-    // Stay on the current page, without reloading.
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-
-    // Edit the same block, creating a new revision.
-    $this->drupalGet("block/" . $block->id());
-    $edit = array();
-    $edit['info[0][value]'] = $this->randomName(8);
-    $edit[$body_key] = $this->randomName(16);
-    $edit['revision'] = TRUE;
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-
-    // Ensure that the block revision has been created.
-    $revised_block = entity_load('custom_block', $block->id(), TRUE);
-    $this->assertNotIdentical($block->getRevisionId(), $revised_block->getRevisionId(), 'A new revision has been created.');
-
-    // Test deleting the block.
-    $this->drupalGet("block/" . $revised_block->id());
-    $this->clickLink(t('Delete'));
-    $this->assertText(format_string('Are you sure you want to delete !label?', array('!label' => $revised_block->label())));
-  }
-
-}
diff --git a/core/modules/block/custom_block/templates/custom-block-add-list.html.twig b/core/modules/block/custom_block/templates/custom-block-add-list.html.twig
deleted file mode 100644
index d2af8e8..0000000
--- a/core/modules/block/custom_block/templates/custom-block-add-list.html.twig
+++ /dev/null
@@ -1,24 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation to present a list of custom block types.
- *
- * Available variables:
- * - types: A collection of all the available custom block types.
- *   Each block type contains the following:
- *   - link: A link to add a block of this type.
- *   - description: A description of this custom block type.
- *
- * @see template_preprocess_custom_block_add_list()
- *
- * @ingroup themeable
- */
-#}
-{% spaceless %}
-  <dl class="node-type-list">
-    {% for type in types %}
-      <dt>{{ type.link }}</dt>
-      <dd>{{ type.description }}</dd>
-    {% endfor %}
-  </dl>
-{% endspaceless %}
diff --git a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.info.yml b/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.info.yml
deleted file mode 100644
index 747efa4..0000000
--- a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.info.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: "Custom Block module tests"
-type: module
-description: "Support module for custom block related testing."
-package: Testing
-version: VERSION
-core: 8.x
diff --git a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.module b/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.module
deleted file mode 100644
index 65e9c56..0000000
--- a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.module
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * @file
- * A dummy module for testing custom block related hooks.
- *
- * This is a dummy module that implements custom block related hooks to test API
- * interaction with the custom_block module.
- */
-
-use Drupal\custom_block\Entity\CustomBlock;
-
-/**
- * Implements hook_custom_block_view().
- */
-function custom_block_test_custom_block_view(array &$build, CustomBlock $custom_block, $view_mode) {
-  // Add extra content.
-  $build['extra_content'] = array(
-    '#markup' => '<blink>Yowser</blink>',
-  );
-}
-
-/**
- * Implements hook_custom_block_presave().
- */
-function custom_block_test_custom_block_presave(CustomBlock $custom_block) {
-  if ($custom_block->label() == 'testing_custom_block_presave') {
-    $custom_block->setInfo($custom_block->label() .'_presave');
-  }
-  // Determine changes.
-  if (!empty($custom_block->original) && $custom_block->original->label() == 'test_changes') {
-    if ($custom_block->original->label() != $custom_block->label()) {
-      $custom_block->setInfo($custom_block->label() .'_presave');
-      // Drupal 1.0 release.
-      $custom_block->changed = 979534800;
-    }
-  }
-}
-
-/**
- * Implements hook_custom_block_update().
- */
-function custom_block_test_custom_block_update(CustomBlock $custom_block) {
-  // Determine changes on update.
-  if (!empty($custom_block->original) && $custom_block->original->label() == 'test_changes') {
-    if ($custom_block->original->label() != $custom_block->label()) {
-      $custom_block->setInfo($custom_block->label() .'_update');
-    }
-  }
-}
-
-/**
- * Implements hook_custom_block_insert().
- *
- * This tests saving a custom_block on custom_block insert.
- *
- * @see \Drupal\custom_block\Tests\CustomBlockSaveTest::testCustomBlockSaveOnInsert()
- */
-function custom_block_test_custom_block_insert(CustomBlock $custom_block) {
-  // Set the custom_block title to the custom_block ID and save.
-  if ($custom_block->label() == 'new') {
-    $custom_block->setInfo('CustomBlock ' . $custom_block->id());
-    $custom_block->save();
-  }
-  if ($custom_block->label() == 'fail_creation') {
-    throw new Exception('Test exception for rollback.');
-  }
-}
diff --git a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.routing.yml b/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.routing.yml
deleted file mode 100644
index a7ca068..0000000
--- a/core/modules/block/custom_block/tests/modules/custom_block_test/custom_block_test.routing.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-custom_block_test.custom_block_view:
-  path: '/custom-block/{custom_block}'
-  defaults:
-    _entity_view: 'custom_block'
-  requirements:
-    _entity_access: 'custom_block.view'
diff --git a/core/modules/block/custom_block/tests/src/Menu/CustomBlockLocalTasksTest.php b/core/modules/block/custom_block/tests/src/Menu/CustomBlockLocalTasksTest.php
deleted file mode 100644
index d04ad15..0000000
--- a/core/modules/block/custom_block/tests/src/Menu/CustomBlockLocalTasksTest.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\Tests\Menu\CustomBlockLocalTasksTest.
- */
-
-namespace Drupal\custom_block\Tests\Menu;
-
-use Drupal\Tests\Core\Menu\LocalTaskIntegrationTest;
-
-/**
- * Tests existence of custom_block local tasks.
- *
- * @group Drupal
- * @group Block
- */
-class CustomBlockLocalTasksTest extends LocalTaskIntegrationTest {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Custom Block local tasks test',
-      'description' => 'Test custom_block local tasks.',
-      'group' => 'Block',
-    );
-  }
-
-  public function setUp() {
-    $this->directoryList = array(
-      'block' => 'core/modules/block',
-      'custom_block' => 'core/modules/block/custom_block',
-    );
-    parent::setUp();
-  }
-
-  /**
-   * Checks custom_block listing local tasks.
-   *
-   * @dataProvider getCustomBlockListingRoutes
-   */
-  public function testCustomBlockListLocalTasks($route) {
-    //
-    $this->assertLocalTasks($route, array(
-      0 => array(
-        'block.admin_display',
-        'custom_block.list',
-      ),
-      1 => array(
-        'custom_block.list_sub',
-        'custom_block.type_list',
-      )
-    ));
-  }
-
-  /**
-   * Provides a list of routes to test.
-   */
-  public function getCustomBlockListingRoutes() {
-    return array(
-      array('custom_block.list', 'custom_block.type_list'),
-    );
-  }
-
-}
diff --git a/core/modules/block_content/block_content.contextual_links.yml b/core/modules/block_content/block_content.contextual_links.yml
new file mode 100644
index 0000000..4751e6c
--- /dev/null
+++ b/core/modules/block_content/block_content.contextual_links.yml
@@ -0,0 +1,10 @@
+block_content.block_edit:
+  title: 'Edit'
+  group: block_content
+  route_name: 'block_content.edit'
+
+block_content.block_delete:
+  title: 'Delete'
+  group: block_content
+  route_name: 'block_content.delete'
+  weight: 1
diff --git a/core/modules/block_content/block_content.info.yml b/core/modules/block_content/block_content.info.yml
new file mode 100644
index 0000000..e1ee81f
--- /dev/null
+++ b/core/modules/block_content/block_content.info.yml
@@ -0,0 +1,10 @@
+name: 'Block Content'
+type: module
+description: 'Allows the creation of custom blocks through the user interface.'
+package: Core
+version: VERSION
+core: 8.x
+dependencies:
+  - block
+  - text
+configure: block_content.list
diff --git a/core/modules/block_content/block_content.install b/core/modules/block_content/block_content.install
new file mode 100644
index 0000000..29ef0bb
--- /dev/null
+++ b/core/modules/block_content/block_content.install
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the custom block module.
+ */
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Implements hook_schema().
+ */
+function block_content_schema() {
+  $schema = array();
+  $schema['block_content'] = array(
+    'description' => 'Stores contents of custom-made blocks.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => "The block's {block_content}.id.",
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'info' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Block description.',
+      ),
+      // Defaults to NULL in order to avoid a brief period of potential
+      // deadlocks on the index.
+      'revision_id' => array(
+        'description' => 'The current {block_custom_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+      ),
+      'type' => array(
+        'description' => 'The type of this custom block.',
+        'type' => 'varchar',
+        'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the custom block was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this node.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'indexes' => array(
+      'block_custom_type' => array('type'),
+    ),
+    'unique keys' => array(
+      'revision_id' => array('revision_id'),
+      'uuid' => array('uuid'),
+      'info' => array('info'),
+    ),
+    'foreign keys' => array(
+      'block_content_revision' => array(
+        'table' => 'block_content_revision',
+        'columns' => array('revision_id' => 'revision_id'),
+      ),
+    ),
+  );
+
+  $schema['block_content_revision'] = array(
+    'description' => 'Stores contents of custom-made blocks.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => "The block's {block_content}.id.",
+      ),
+      // Defaults to NULL in order to avoid a brief period of potential
+      // deadlocks on the index.
+      'revision_id' => array(
+        'description' => 'The current version identifier.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'log' => array(
+        'description' => 'The log entry explaining the changes in this version.',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+      ),
+      'info' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Block description.',
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the version was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('revision_id'),
+  );
+  return $schema;
+}
diff --git a/core/modules/block_content/block_content.libraries.yml b/core/modules/block_content/block_content.libraries.yml
new file mode 100644
index 0000000..5a1b5b6
--- /dev/null
+++ b/core/modules/block_content/block_content.libraries.yml
@@ -0,0 +1,8 @@
+drupal.block_content:
+  version: VERSION
+  js:
+    js/block_content.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.form
diff --git a/core/modules/block_content/block_content.local_actions.yml b/core/modules/block_content/block_content.local_actions.yml
new file mode 100644
index 0000000..735e374
--- /dev/null
+++ b/core/modules/block_content/block_content.local_actions.yml
@@ -0,0 +1,14 @@
+block_content_type_add:
+  route_name: block_content.type_add
+  title: 'Add custom block type'
+  appears_on:
+    - block_content.type_list
+
+block_content_add_action:
+  route_name: block_content.add_page
+  title: 'Add custom block'
+  appears_on:
+    - block.admin_display
+    - block.admin_display_theme
+    - block_content.list
+  class: \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction
diff --git a/core/modules/block_content/block_content.local_tasks.yml b/core/modules/block_content/block_content.local_tasks.yml
new file mode 100644
index 0000000..0674c99
--- /dev/null
+++ b/core/modules/block_content/block_content.local_tasks.yml
@@ -0,0 +1,27 @@
+block_content.list:
+  title: 'Custom block library'
+  route_name: block_content.list
+  base_route: block.admin_display
+block_content.list_sub:
+  title: Blocks
+  route_name: block_content.list
+  parent_id: block_content.list
+block_content.type_list:
+  title: Types
+  route_name: block_content.type_list
+  parent_id: block_content.list
+
+block_content.edit:
+  title: Edit
+  route_name: block_content.edit
+  base_route: block_content.edit
+block_content.delete:
+  title: Delete
+  route_name: block_content.delete
+  base_route: block_content.edit
+
+# Default tab for custom block type editing.
+block_content.type_edit:
+  title: 'Edit'
+  route_name: block_content.type_edit
+  base_route: block_content.type_edit
diff --git a/core/modules/block_content/block_content.module b/core/modules/block_content/block_content.module
new file mode 100644
index 0000000..95086c9
--- /dev/null
+++ b/core/modules/block_content/block_content.module
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Allows the creation of custom blocks through the user interface.
+ */
+
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldInstanceConfig;
+
+/**
+ * Implements hook_help().
+ */
+function block_content_help($route_name, Request $request) {
+  switch ($route_name) {
+    case 'help.page.block_content':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The Custom Block module allows you to create blocks of content, which can be placed in regions throughout the website. Custom blocks can have fields; see the <a href="!field-help">Field module help</a> for more information. Once created, custom blocks can be placed like blocks provided by other modules; see the <a href="!blocks">Block module help page</a> for details. For more information, see <a href="!online-help">the online documentation for the Custom Block module</a>.', array('!block-content' => \Drupal::url('block_content.list'), '!field-help' => \Drupal::url('help.page', array('name' => 'field')), '!blocks' => \Drupal::url('help.page', array('name' => 'block')), '!online-help' => 'https://drupal.org/documentation/modules/block_content')) . '</p>';
+      $output .= '<h3>' . t('Uses') . '</h3>';
+      $output .= '<dl>';
+      $output .= '<dt>' . t('Creating and managing custom block types') . '</dt>';
+      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can create different custom block types, each with different fields and display settings, from the <a href="!types">Custom block types</a> page. The Custom block types page lists all of your created custom block types, and allows you to edit and manage them. For more information about managing fields and display settings, see the <a href="!field-ui">Field UI module help</a>.', array('!types' => \Drupal::url('block_content.type_list'), '!field-ui' => \Drupal::url('help.page', array('name' => 'field_ui')))) . '</dd>';
+      $output .= '<dt>' . t('Creating custom blocks') . '</dt>';
+      $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can <a href="!block-add">add custom blocks</a> of each of their defined custom block types. Created custom blocks are then listed on the <a href="!blocks">Blocks administration page</a>.', array('!blocks' => \Drupal::url('block.admin_display'), '!block-add' => \Drupal::url('block_content.add_page'))) . '</dd>';
+      $output .= '</dl>';
+      return $output;
+
+    case 'block_content.list':
+      $output = '<p>' . t('This page lists user-created blocks. These blocks are derived from block types. A block type can consist of different fields and display settings. From the block types tab you can manage these fields as well as create new block types.') . '</p>';
+      return $output;
+
+    case 'block_content.type_list':
+      $output = '<p>' . t('This page lists block types. A block type can consist of different fields and display settings. From here you can manage these fields as well as create new block types.') . '</p>';
+      return $output;
+
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function block_content_theme($existing, $type, $theme, $path) {
+  return array(
+    'block_content_add_list' => array(
+      'variables' => array('content' => NULL),
+      'file' => 'block_content.pages.inc',
+      'template' => 'block-content-add-list',
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_type_alter().
+ */
+function block_content_entity_type_alter(array &$entity_types) {
+  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
+  // Add a translation handler for fields if the language module is enabled.
+  if (\Drupal::moduleHandler()->moduleExists('language')) {
+    $translation = $entity_types['block_content']->get('translation');
+    $translation['block_content'] = TRUE;
+    $entity_types['block_content']->set('translation', $translation);
+  }
+}
+
+/**
+ * Adds the default body field to a custom block type.
+ *
+ * @param string $block_type_id
+ *   Id of the block type.
+ * @param string $label
+ *   (optional) The label for the body instance. Defaults to 'Body'
+ *
+ * @return array()
+ *   Body field instance.
+ */
+function block_content_add_body_field($block_type_id, $label = 'Body') {
+  // Add or remove the body field, as needed.
+  $field = FieldConfig::loadByName('block_content', 'body');
+  $instance = FieldInstanceConfig::loadByName('block_content', $block_type_id, 'body');
+  if (empty($field)) {
+    $field = entity_create('field_config', array(
+      'name' => 'body',
+      'entity_type' => 'block_content',
+      'type' => 'text_with_summary',
+    ));
+    $field->save();
+  }
+  if (empty($instance)) {
+    $instance = entity_create('field_instance_config', array(
+      'field_name' => 'body',
+      'entity_type' => 'block_content',
+      'bundle' => $block_type_id,
+      'label' => $label,
+      'settings' => array('display_summary' => FALSE),
+    ));
+    $instance->save();
+
+    // Assign widget settings for the 'default' form mode.
+    entity_get_form_display('block_content', $block_type_id, 'default')
+      ->setComponent('body', array(
+        'type' => 'text_textarea_with_summary',
+      ))
+      ->save();
+
+    // Assign display settings for 'default' view mode.
+    entity_get_display('block_content', $block_type_id, 'default')
+      ->setComponent('body', array(
+        'label' => 'hidden',
+        'type' => 'text_default',
+      ))
+      ->save();
+  }
+
+  return $instance;
+}
diff --git a/core/modules/block_content/block_content.pages.inc b/core/modules/block_content/block_content.pages.inc
new file mode 100644
index 0000000..d2dc0c4
--- /dev/null
+++ b/core/modules/block_content/block_content.pages.inc
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Provides page callbacks for custom blocks.
+ */
+
+use Drupal\Component\Utility\Xss;
+use Drupal\block_content\Entity\BlockContentType;
+use Drupal\block_content\Entity\BlockContent;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Prepares variables for a custom block type creation list templates.
+ *
+ * Default template: block-content-add-list.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - content: An array of block types.
+ *
+ * @see block_content_add_page()
+ */
+function template_preprocess_block_content_add_list(&$variables) {
+  $variables['types'] = array();
+  $query = \Drupal::request()->query->all();
+  foreach ($variables['content'] as $type) {
+    $variables['types'][$type->id()] = array(
+      'link' => \Drupal::l($type->label(), 'block_content.add_form', array('block_content_type' => $type->id()), array('query' => $query)),
+      'description' => Xss::filterAdmin($type->description),
+      'title' => $type->label(),
+      'localized_options' => array(
+        'query' => $query,
+      ),
+    );
+  }
+}
diff --git a/core/modules/block_content/block_content.routing.yml b/core/modules/block_content/block_content.routing.yml
new file mode 100644
index 0000000..b6a1aaf
--- /dev/null
+++ b/core/modules/block_content/block_content.routing.yml
@@ -0,0 +1,80 @@
+block_content.type_list:
+  path: '/admin/structure/block/block-content/types'
+  defaults:
+    _entity_list: 'block_content_type'
+    _title: 'Edit'
+  requirements:
+    _permission: 'administer blocks'
+
+block_content.add_page:
+  path: '/block/add'
+  defaults:
+    _content: '\Drupal\block_content\Controller\BlockContentController::add'
+    _title: 'Add custom block'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _permission: 'administer blocks'
+
+block_content.add_form:
+  path: '/block/add/{block_content_type}'
+  defaults:
+    _content: '\Drupal\block_content\Controller\BlockContentController::addForm'
+    _title_callback: 'Drupal\block_content\Controller\BlockContentController::getAddFormTitle'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _permission: 'administer blocks'
+
+block_content.type_delete:
+  path: '/admin/structure/block/block-content/manage/{block_content_type}/delete'
+  defaults:
+    _entity_form: 'block_content_type.delete'
+    _title: 'Delete'
+  requirements:
+    _entity_access: 'block_content_type.delete'
+  options:
+    _admin_route: TRUE
+
+block_content.edit:
+  path: '/block/{block_content}'
+  defaults:
+    _entity_form: 'block_content.edit'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _entity_access: 'block_content.update'
+
+block_content.delete:
+  path: '/block/{block_content}/delete'
+  defaults:
+    _entity_form: 'block_content.delete'
+    _title: 'Delete'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _entity_access: 'block_content.delete'
+
+block_content.type_add:
+  path: '/admin/structure/block/block-content/types/add'
+  defaults:
+    _entity_form: 'block_content_type.add'
+    _title: 'Add'
+  requirements:
+    _permission: 'administer blocks'
+
+block_content.type_edit:
+  path: '/admin/structure/block/block-content/manage/{block_content_type}'
+  defaults:
+    _entity_form: 'block_content_type.edit'
+    _title: 'Edit'
+  requirements:
+    _entity_access: 'block_content_type.update'
+
+block_content.list:
+  path: '/admin/structure/block/block-content'
+  defaults:
+    _title: 'Custom block library'
+    _entity_list: 'block_content'
+  requirements:
+    _permission: 'administer blocks'
diff --git a/core/modules/block_content/config/install/block_content.type.basic.yml b/core/modules/block_content/config/install/block_content.type.basic.yml
new file mode 100644
index 0000000..02982e4
--- /dev/null
+++ b/core/modules/block_content/config/install/block_content.type.basic.yml
@@ -0,0 +1,5 @@
+id: basic
+label: 'Basic block'
+revision: 0
+description: 'A basic block contains a title and a body.'
+langcode: en
diff --git a/core/modules/block_content/config/install/entity.view_mode.block_content.full.yml b/core/modules/block_content/config/install/entity.view_mode.block_content.full.yml
new file mode 100644
index 0000000..c558d54
--- /dev/null
+++ b/core/modules/block_content/config/install/entity.view_mode.block_content.full.yml
@@ -0,0 +1,8 @@
+id: block_content.full
+label: Full
+status: false
+cache: true
+targetEntityType: block_content
+dependencies:
+  module:
+    - block_content
diff --git a/core/modules/block_content/config/schema/block_content.schema.yml b/core/modules/block_content/config/schema/block_content.schema.yml
new file mode 100644
index 0000000..d87edfc
--- /dev/null
+++ b/core/modules/block_content/config/schema/block_content.schema.yml
@@ -0,0 +1,27 @@
+# Schema for the configuration files of the Custom Block module.
+
+block_content.type.*:
+  type: mapping
+  label: 'Custom block type settings'
+  mapping:
+    id:
+      type: string
+      label: 'Machine-readable name'
+    uuid:
+      type: string
+      label: 'UUID'
+    label:
+      type: label
+      label: 'Label'
+    revision:
+      type: integer
+      label: 'Create new revision'
+    description:
+      type: text
+      label: 'Description'
+    status:
+      type: boolean
+      label: 'Status'
+    langcode:
+      type: string
+      label: 'Default language'
diff --git a/core/modules/block_content/js/block_content.js b/core/modules/block_content/js/block_content.js
new file mode 100644
index 0000000..45e4583
--- /dev/null
+++ b/core/modules/block_content/js/block_content.js
@@ -0,0 +1,46 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the block_content module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  Drupal.behaviors.blockContentDetailsSummaries = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.block-content-form-revision-information').drupalSetSummary(function (context) {
+        var $context = $(context);
+        var revisionCheckbox = $context.find('.form-item-revision input');
+
+        // Return 'New revision' if the 'Create new revision' checkbox is checked,
+        // or if the checkbox doesn't exist, but the revision log does. For users
+        // without the "Administer content" permission the checkbox won't appear,
+        // but the revision log will if the content type is set to auto-revision.
+        if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $context.find('.form-item-log textarea').length)) {
+          return Drupal.t('New revision');
+        }
+
+        return Drupal.t('No revision');
+      });
+
+      $context.find('fieldset.block-content-translation-options').drupalSetSummary(function (context) {
+        var $context = $(context);
+        var translate;
+        var $checkbox = $context.find('.form-item-translation-translate input');
+
+        if ($checkbox.size()) {
+          translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
+        }
+        else {
+          $checkbox = $context.find('.form-item-translation-retranslate input');
+          translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
+        }
+
+        return translate;
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/modules/block_content/src/BlockContentAccessController.php b/core/modules/block_content/src/BlockContentAccessController.php
new file mode 100644
index 0000000..5709498
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentAccessController.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentAccessController.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityAccessController;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Defines the access controller for the custom block entity type.
+ */
+class BlockContentAccessController extends EntityAccessController {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+    if ($operation === 'view') {
+      return TRUE;
+    }
+    return parent::checkAccess($entity, $operation, $langcode, $account);
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
new file mode 100644
index 0000000..0b3c616
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -0,0 +1,254 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentForm.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for the custom block edit forms.
+ */
+class BlockContentForm extends ContentEntityForm {
+
+  /**
+   * The custom block storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $blockContentStorage;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * Constructs a BlockContentForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage
+   *   The custom block storage.
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   *   The language manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, EntityStorageInterface $block_content_storage, LanguageManager $language_manager) {
+    parent::__construct($entity_manager);
+    $this->blockContentStorage = $block_content_storage;
+    $this->languageManager = $language_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    $entity_manager = $container->get('entity.manager');
+    return new static(
+      $entity_manager,
+      $entity_manager->getStorage('block_content'),
+      $container->get('language_manager')
+    );
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityForm::prepareEntity().
+   *
+   * Prepares the custom block object.
+   *
+   * Fills in a few default values, and then invokes hook_block_content_prepare()
+   * on all modules.
+   */
+  protected function prepareEntity() {
+    $block = $this->entity;
+    // Set up default values, if required.
+    $block_type = entity_load('block_content_type', $block->bundle());
+    if (!$block->isNew()) {
+      $block->setRevisionLog(NULL);
+    }
+    // Always use the default revision setting.
+    $block->setNewRevision($block_type->revision);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, array &$form_state) {
+    $block = $this->entity;
+    $account = $this->currentUser();
+
+    if ($this->operation == 'edit') {
+      $form['#title'] = $this->t('Edit custom block %label', array('%label' => $block->label()));
+    }
+    // Override the default CSS class name, since the user-defined custom block
+    // type name in 'TYPE-block-form' potentially clashes with third-party class
+    // names.
+    $form['#attributes']['class'][0] = drupal_html_class('block-' . $block->bundle() . '-form');
+
+    if ($this->moduleHandler->moduleExists('language')) {
+      $language_configuration = language_get_default_configuration('block_content', $block->bundle());
+
+      // Set the correct default language.
+      if ($block->isNew()) {
+        $language_default = $this->languageManager->getCurrentLanguage($language_configuration['langcode']);
+        $block->langcode->value = $language_default->id;
+      }
+    }
+
+    $form['langcode'] = array(
+      '#title' => $this->t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $block->getUntranslated()->language()->id,
+      '#languages' => Language::STATE_ALL,
+      '#access' => isset($language_configuration['language_show']) && $language_configuration['language_show'],
+    );
+
+    $form['advanced'] = array(
+      '#type' => 'vertical_tabs',
+      '#weight' => 99,
+    );
+
+    // Add a log field if the "Create new revision" option is checked, or if the
+    // current user has the ability to check that option.
+    $form['revision_information'] = array(
+      '#type' => 'details',
+      '#title' => $this->t('Revision information'),
+      // Open by default when "Create new revision" is checked.
+      '#open' => $block->isNewRevision(),
+      '#group' => 'advanced',
+      '#attributes' => array(
+        'class' => array('block-content-form-revision-information'),
+      ),
+      '#attached' => array(
+        'library' => array('block_content/drupal.block_content'),
+      ),
+      '#weight' => 20,
+      '#access' => $block->isNewRevision() || $account->hasPermission('administer blocks'),
+    );
+
+    $form['revision_information']['revision'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Create new revision'),
+      '#default_value' => $block->isNewRevision(),
+      '#access' => $account->hasPermission('administer blocks'),
+    );
+
+    // Check the revision log checkbox when the log textarea is filled in.
+    // This must not happen if "Create new revision" is enabled by default,
+    // since the state would auto-disable the checkbox otherwise.
+    if (!$block->isNewRevision()) {
+      $form['revision_information']['revision']['#states'] = array(
+        'checked' => array(
+          'textarea[name="log"]' => array('empty' => FALSE),
+        ),
+      );
+    }
+
+    $form['revision_information']['log'] = array(
+      '#type' => 'textarea',
+      '#title' => $this->t('Revision log message'),
+      '#rows' => 4,
+      '#default_value' => $block->getRevisionLog(),
+      '#description' => $this->t('Briefly desribe the changes you have made.'),
+    );
+
+    return parent::form($form, $form_state, $block);
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityForm::submit().
+   *
+   * Updates the custom block object by processing the submitted values.
+   *
+   * This function can be called by a "Next" button of a wizard to update the
+   * form state's entity with the current step's values before proceeding to the
+   * next step.
+   */
+  public function submit(array $form, array &$form_state) {
+    // Build the block object from the submitted values.
+    $block = parent::submit($form, $form_state);
+
+    // Save as a new revision if requested to do so.
+    if (!empty($form_state['values']['revision'])) {
+      $block->setNewRevision();
+    }
+
+    return $block;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, array &$form_state) {
+    $block = $this->entity;
+    $insert = $block->isNew();
+    $block->save();
+    $watchdog_args = array('@type' => $block->bundle(), '%info' => $block->label());
+    $block_type = entity_load('block_content_type', $block->bundle());
+    $t_args = array('@type' => $block_type->label(), '%info' => $block->label());
+
+    if ($insert) {
+      watchdog('content', '@type: added %info.', $watchdog_args, WATCHDOG_NOTICE);
+      drupal_set_message($this->t('@type %info has been created.', $t_args));
+    }
+    else {
+      watchdog('content', '@type: updated %info.', $watchdog_args, WATCHDOG_NOTICE);
+      drupal_set_message($this->t('@type %info has been updated.', $t_args));
+    }
+
+    if ($block->id()) {
+      $form_state['values']['id'] = $block->id();
+      $form_state['id'] = $block->id();
+      if ($insert) {
+        if (!$theme = $block->getTheme()) {
+          $theme = $this->config('system.theme')->get('default');
+        }
+        $form_state['redirect_route'] = array(
+          'route_name' => 'block.admin_add',
+          'route_parameters' => array(
+            'plugin_id' => 'block_content:' . $block->uuid(),
+            'theme' => $theme,
+          ),
+        );
+      }
+      else {
+        $form_state['redirect_route']['route_name'] = 'block_content.list';
+      }
+    }
+    else {
+      // In the unlikely case something went wrong on save, the block will be
+      // rebuilt and block form redisplayed.
+      drupal_set_message($this->t('The block could not be saved.'), 'error');
+      $form_state['rebuild'] = TRUE;
+    }
+
+    // Clear the page and block caches.
+    Cache::invalidateTags(array('content' => TRUE));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    if ($this->entity->isNew()) {
+      $exists = $this->blockContentStorage->loadByProperties(array('info' => $form_state['values']['info']));
+      if (!empty($exists)) {
+        $this->setFormError('info', $form_state, $this->t('A block with description %name already exists.', array(
+          '%name' => $form_state['values']['info'][0]['value'],
+        )));
+      }
+    }
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentInterface.php b/core/modules/block_content/src/BlockContentInterface.php
new file mode 100644
index 0000000..29ef69f
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentInterface.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Entity\BlockContentInterface.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityChangedInterface;
+
+/**
+ * Provides an interface defining a custom block entity.
+ */
+interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface {
+
+  /**
+   * Returns the block revision log message.
+   *
+   * @return string
+   *   The revision log message.
+   */
+  public function getRevisionLog();
+
+  /**
+   * Sets the block description.
+   *
+   * @param string $info
+   *   The block description.
+   *
+   * @return \Drupal\block_content\BlockContentInterface
+   *   The class instance that this method is called on.
+   */
+  public function setInfo($info);
+
+  /**
+   * Sets the block revision log message.
+   *
+   * @param string $log
+   *   The revision log message.
+   *
+   * @return \Drupal\block_content\BlockContentInterface
+   *   The class instance that this method is called on.
+   */
+  public function setRevisionLog($log);
+
+  /**
+   * Sets the theme value.
+   *
+   * When creating a new custom block from the block library, the user is
+   * redirected to the configure form for that block in the given theme. The
+   * theme is stored against the block when the custom block add form is shown.
+   *
+   * @param string $theme
+   *   The theme name.
+   *
+   * @return \Drupal\block_content\BlockContentInterface
+   *   The class instance that this method is called on.
+   */
+  public function setTheme($theme);
+
+  /**
+   * Gets the theme value.
+   *
+   * When creating a new custom block from the block library, the user is
+   * redirected to the configure form for that block in the given theme. The
+   * theme is stored against the block when the custom block add form is shown.
+   *
+   * @return string
+   *   The theme name.
+   */
+  public function getTheme();
+
+  /**
+   * Gets the configured instances of this custom block.
+   *
+   * @return array
+   *   Array of Drupal\block\Core\Plugin\Entity\Block entities.
+   */
+  public function getInstances();
+
+}
diff --git a/core/modules/block_content/src/BlockContentListBuilder.php b/core/modules/block_content/src/BlockContentListBuilder.php
new file mode 100644
index 0000000..731f2da
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentListBuilder.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentListBuilder.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListBuilder;
+
+/**
+ * Defines a class to build a listing of custom block entities.
+ *
+ * @see \Drupal\block_content\Entity\BlockContent
+ */
+class BlockContentListBuilder extends EntityListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['label'] = t('Block description');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['label'] = $this->getLabel($entity);
+    return $row + parent::buildRow($entity);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+    if (isset($operations['edit'])) {
+      $operations['edit']['query']['destination'] = 'admin/structure/block/block-content';
+    }
+    return $operations;
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentTranslationHandler.php b/core/modules/block_content/src/BlockContentTranslationHandler.php
new file mode 100644
index 0000000..148e249
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentTranslationHandler.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentTranslationHandler.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\content_translation\ContentTranslationHandler;
+
+/**
+ * Defines the translation handler for custom blocks.
+ */
+class BlockContentTranslationHandler extends ContentTranslationHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function entityFormAlter(array &$form, array &$form_state, EntityInterface $entity) {
+    parent::entityFormAlter($form, $form_state, $entity);
+    // Move the translation fieldset to a vertical tab.
+    if (isset($form['translation'])) {
+      $form['translation'] += array(
+        '#group' => 'additional_settings',
+        '#weight' => 100,
+        '#attributes' => array(
+          'class' => array('block-content-translation-options'),
+        ),
+      );
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function entityFormTitle(EntityInterface $entity) {
+    $block_type = entity_load('block_content_type', $entity->bundle());
+    return t('<em>Edit @type</em> @title', array('@type' => $block_type->label(), '@title' => $entity->label()));
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentTypeForm.php b/core/modules/block_content/src/BlockContentTypeForm.php
new file mode 100644
index 0000000..e64bfa3
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentTypeForm.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentTypeForm.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Base form for category edit forms.
+ */
+class BlockContentTypeForm extends EntityForm {
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityForm::form().
+   */
+  public function form(array $form, array &$form_state) {
+    $form = parent::form($form, $form_state);
+
+    $block_type = $this->entity;
+
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $block_type->label(),
+      '#description' => t("Provide a label for this block type to help identify it in the administration pages."),
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $block_type->id(),
+      '#machine_name' => array(
+        'exists' => '\Drupal\block_content\Entity\BlockContentType::load',
+      ),
+      '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
+      '#disabled' => !$block_type->isNew(),
+    );
+
+    $form['description'] = array(
+      '#type' => 'textarea',
+      '#default_value' => $block_type->description,
+      '#description' => t('Enter a description for this block type.'),
+      '#title' => t('Description'),
+    );
+
+    $form['revision'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Create new revision'),
+      '#default_value' => $block_type->revision,
+      '#description' => t('Create a new revision by default for this block type.')
+    );
+
+    if ($this->moduleHandler->moduleExists('content_translation')) {
+      $form['language'] = array(
+        '#type' => 'details',
+        '#title' => t('Language settings'),
+        '#group' => 'additional_settings',
+      );
+
+      $language_configuration = language_get_default_configuration('block_content', $block_type->id());
+      $form['language']['language_configuration'] = array(
+        '#type' => 'language_configuration',
+        '#entity_information' => array(
+          'entity_type' => 'block_content',
+          'bundle' => $block_type->id(),
+        ),
+        '#default_value' => $language_configuration,
+      );
+
+      $form['#submit'][] = 'language_configuration_element_submit';
+    }
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityForm::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $block_type = $this->entity;
+    $status = $block_type->save();
+
+    $edit_link = \Drupal::linkGenerator()->generateFromUrl($this->t('Edit'), $this->entity->urlInfo());
+    if ($status == SAVED_UPDATED) {
+      drupal_set_message(t('Custom block type %label has been updated.', array('%label' => $block_type->label())));
+      watchdog('block_content', 'Custom block type %label has been updated.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, $edit_link);
+    }
+    else {
+      drupal_set_message(t('Custom block type %label has been added.', array('%label' => $block_type->label())));
+      watchdog('block_content', 'Custom block type %label has been added.', array('%label' => $block_type->label()), WATCHDOG_NOTICE, $edit_link);
+    }
+
+    $form_state['redirect_route']['route_name'] = 'block_content.type_list';
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentTypeInterface.php b/core/modules/block_content/src/BlockContentTypeInterface.php
new file mode 100644
index 0000000..cb21631
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentTypeInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Entity\BlockContentTypeInterface.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface defining a custom block type entity.
+ */
+interface BlockContentTypeInterface extends ConfigEntityInterface {
+
+}
diff --git a/core/modules/block_content/src/BlockContentTypeListBuilder.php b/core/modules/block_content/src/BlockContentTypeListBuilder.php
new file mode 100644
index 0000000..6cfda1d
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentTypeListBuilder.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentTypeListBuilder.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Defines a class to build a listing of custom block type entities.
+ *
+ * @see \Drupal\block_content\Entity\BlockContentType
+ */
+class BlockContentTypeListBuilder extends ConfigEntityListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+    // Place the edit operation after the operations added by field_ui.module
+    // which have the weights 15, 20, 25.
+    if (isset($operations['edit'])) {
+      $operations['edit']['weight'] = 30;
+    }
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['type'] = t('Block type');
+    $header['description'] = t('Description');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['type'] = \Drupal::linkGenerator()->generateFromUrl($entity->label(), $entity->urlInfo());
+    $row['description'] = Xss::filterAdmin($entity->description);
+    return $row + parent::buildRow($entity);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTitle() {
+    return $this->t('Custom block types');
+  }
+
+}
diff --git a/core/modules/block_content/src/BlockContentViewBuilder.php b/core/modules/block_content/src/BlockContentViewBuilder.php
new file mode 100644
index 0000000..0977e9f
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentViewBuilder.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\BlockContentViewBuilder.
+ */
+
+namespace Drupal\block_content;
+
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityViewBuilder;
+
+/**
+ * Render controller for custom blocks.
+ */
+class BlockContentViewBuilder extends EntityViewBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) {
+    parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
+    // Add contextual links for this custom block.
+    if (!$entity->isNew() && $view_mode == 'full') {
+      $build['#contextual_links']['block_content'] = array(
+        'route_parameters' => array('block_content' => $entity->id()),
+        'metadata' => array('changed' => $entity->getChangedTime()),
+      );
+    }
+  }
+
+}
diff --git a/core/modules/block_content/src/Controller/BlockContentController.php b/core/modules/block_content/src/Controller/BlockContentController.php
new file mode 100644
index 0000000..4716bba
--- /dev/null
+++ b/core/modules/block_content/src/Controller/BlockContentController.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Controller\BlockContentController
+ */
+
+namespace Drupal\block_content\Controller;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\block_content\BlockContentTypeInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+class BlockContentController extends ControllerBase {
+
+  /**
+   * The custom block storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $blockContentStorage;
+
+  /**
+   * The custom block type storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $blockContentTypeStorage;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    $entity_manager = $container->get('entity.manager');
+    return new static(
+      $entity_manager->getStorage('block_content'),
+      $entity_manager->getStorage('block_content_type')
+    );
+  }
+
+  /**
+   * Constructs a BlockContent object.
+   *
+   * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage
+   *   The custom block storage.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_type_storage
+   *   The custom block type storage.
+   */
+  public function __construct(EntityStorageInterface $block_content_storage, EntityStorageInterface $block_content_type_storage) {
+    $this->blockContentStorage = $block_content_storage;
+    $this->blockContentTypeStorage = $block_content_type_storage;
+  }
+
+  /**
+   * Displays add custom block links for available types.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object.
+   *
+   * @return array
+   *   A render array for a list of the custom block types that can be added or
+   *   if there is only one custom block type defined for the site, the function
+   *   returns the custom block add page for that custom block type.
+   */
+  public function add(Request $request) {
+    $types = $this->blockContentTypeStorage->loadMultiple();
+    if ($types && count($types) == 1) {
+      $type = reset($types);
+      return $this->addForm($type, $request);
+    }
+
+    return array('#theme' => 'block_content_add_list', '#content' => $types);
+  }
+
+  /**
+   * Presents the custom block creation form.
+   *
+   * @param \Drupal\block_content\BlockContentTypeInterface $block_content_type
+   *   The custom block type to add.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object.
+   *
+   * @return array
+   *   A form array as expected by drupal_render().
+   */
+  public function addForm(BlockContentTypeInterface $block_content_type, Request $request) {
+    $block = $this->blockContentStorage->create(array(
+      'type' => $block_content_type->id()
+    ));
+    if (($theme = $request->query->get('theme')) && in_array($theme, array_keys(list_themes()))) {
+      // We have navigated to this page from the block library and will keep track
+      // of the theme for redirecting the user to the configuration page for the
+      // newly created block in the given theme.
+      $block->setTheme($theme);
+    }
+    return $this->entityFormBuilder()->getForm($block);
+  }
+
+  /**
+   * Provides the page title for this controller.
+   *
+   * @param \Drupal\block_content\BlockContentTypeInterface $block_content_type
+   *   The custom block type being added.
+   *
+   * @return string
+   *   The page title.
+   */
+  public function getAddFormTitle(BlockContentTypeInterface $block_content_type) {
+    return $this->t('Add %type custom block', array('%type' => $block_content_type->label()));
+  }
+
+}
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
new file mode 100644
index 0000000..d85baef
--- /dev/null
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -0,0 +1,232 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Entity\BlockContent.
+ */
+
+namespace Drupal\block_content\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\FieldDefinition;
+use Drupal\block_content\BlockContentInterface;
+
+/**
+ * Defines the custom block entity class.
+ *
+ * @ContentEntityType(
+ *   id = "block_content",
+ *   label = @Translation("Custom Block"),
+ *   bundle_label = @Translation("Custom Block type"),
+ *   controllers = {
+ *     "access" = "Drupal\block_content\BlockContentAccessController",
+ *     "list_builder" = "Drupal\block_content\BlockContentListBuilder",
+ *     "view_builder" = "Drupal\block_content\BlockContentViewBuilder",
+ *     "form" = {
+ *       "add" = "Drupal\block_content\BlockContentForm",
+ *       "edit" = "Drupal\block_content\BlockContentForm",
+ *       "delete" = "Drupal\block_content\Form\BlockContentDeleteForm",
+ *       "default" = "Drupal\block_content\BlockContentForm"
+ *     },
+ *     "translation" = "Drupal\block_content\BlockContentTranslationHandler"
+ *   },
+ *   admin_permission = "administer blocks",
+ *   base_table = "block_content",
+ *   revision_table = "block_content_revision",
+ *   links = {
+ *     "canonical" = "block_content.edit",
+ *     "delete-form" = "block_content.delete",
+ *     "edit-form" = "block_content.edit",
+ *     "admin-form" = "block_content.type_edit"
+ *   },
+ *   fieldable = TRUE,
+ *   translatable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "revision" = "revision_id",
+ *     "bundle" = "type",
+ *     "label" = "info",
+ *     "uuid" = "uuid"
+ *   },
+ *   bundle_entity_type = "block_content_type"
+ * )
+ */
+class BlockContent extends ContentEntityBase implements BlockContentInterface {
+
+  /**
+   * The theme the block is being created in.
+   *
+   * When creating a new custom block from the block library, the user is
+   * redirected to the configure form for that block in the given theme. The
+   * theme is stored against the block when the custom block add form is shown.
+   *
+   * @var string
+   */
+  protected $theme;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createDuplicate() {
+    $duplicate = parent::createDuplicate();
+    $duplicate->revision_id->value = NULL;
+    $duplicate->id->value = NULL;
+    return $duplicate;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setTheme($theme) {
+    $this->theme = $theme;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTheme() {
+    return $this->theme;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
+    parent::postSave($storage, $update);
+
+    // Invalidate the block cache to update custom block-based derivatives.
+    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInstances() {
+    return entity_load_multiple_by_properties('block', array('plugin' => 'block_content:' . $this->uuid()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
+    parent::preSaveRevision($storage, $record);
+
+    if ($this->isNewRevision()) {
+      // When inserting either a new custom block or a new block_content
+      // revision, $entity->log must be set because {block_custom_revision}.log
+      // is a text column and therefore cannot have a default value. However,
+      // it might not be set at this point (for example, if the user submitting
+      // the form does not have permission to create revisions), so we ensure
+      // that it is at least an empty string in that case.
+      // @todo: Make the {block_custom_revision}.log column nullable so that we
+      // can remove this check.
+      if (!isset($record->log)) {
+        $record->log = '';
+      }
+    }
+    elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
+      // If we are updating an existing block_content without adding a new
+      // revision and the user did not supply a log, keep the existing one.
+      $record->log = $this->original->getRevisionLog();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete() {
+    foreach ($this->getInstances() as $instance) {
+      $instance->delete();
+    }
+    parent::delete();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields['id'] = FieldDefinition::create('integer')
+      ->setLabel(t('Custom block ID'))
+      ->setDescription(t('The custom block ID.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+
+    $fields['uuid'] = FieldDefinition::create('uuid')
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The custom block UUID.'))
+      ->setReadOnly(TRUE);
+
+    $fields['revision_id'] = FieldDefinition::create('integer')
+      ->setLabel(t('Revision ID'))
+      ->setDescription(t('The revision ID.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+
+    $fields['langcode'] = FieldDefinition::create('language')
+      ->setLabel(t('Language code'))
+      ->setDescription(t('The custom block language code.'));
+
+    $fields['info'] = FieldDefinition::create('string')
+      ->setLabel(t('Block description'))
+      ->setDescription(t('A brief description of your block.'))
+      ->setRevisionable(TRUE)
+      ->setRequired(TRUE)
+      ->setDisplayOptions('form', array(
+        'type' => 'string',
+        'weight' => -5,
+      ))
+      ->setDisplayConfigurable('form', TRUE);
+
+    $fields['type'] = FieldDefinition::create('entity_reference')
+      ->setLabel(t('Block type'))
+      ->setDescription(t('The block type.'))
+      ->setSetting('target_type', 'block_content_type')
+      ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
+
+    $fields['log'] = FieldDefinition::create('string')
+      ->setLabel(t('Revision log message'))
+      ->setDescription(t('The revision log message.'))
+      ->setRevisionable(TRUE);
+
+    $fields['changed'] = FieldDefinition::create('changed')
+      ->setLabel(t('Changed'))
+      ->setDescription(t('The time that the custom block was last edited.'))
+      ->setRevisionable(TRUE);
+
+    return $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getChangedTime() {
+    return $this->get('changed')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionLog() {
+    return $this->get('log')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setInfo($info) {
+    $this->set('info', $info);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRevisionLog($log) {
+    $this->set('log', $log);
+    return $this;
+  }
+
+}
diff --git a/core/modules/block_content/src/Entity/BlockContentType.php b/core/modules/block_content/src/Entity/BlockContentType.php
new file mode 100644
index 0000000..17a358e
--- /dev/null
+++ b/core/modules/block_content/src/Entity/BlockContentType.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Entity\BlockContentType.
+ */
+
+namespace Drupal\block_content\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\block_content\BlockContentTypeInterface;
+
+/**
+ * Defines the custom block type entity.
+ *
+ * @ConfigEntityType(
+ *   id = "block_content_type",
+ *   label = @Translation("Custom block type"),
+ *   controllers = {
+ *     "form" = {
+ *       "default" = "Drupal\block_content\BlockContentTypeForm",
+ *       "add" = "Drupal\block_content\BlockContentTypeForm",
+ *       "edit" = "Drupal\block_content\BlockContentTypeForm",
+ *       "delete" = "Drupal\block_content\Form\BlockContentTypeDeleteForm"
+ *     },
+ *     "list_builder" = "Drupal\block_content\BlockContentTypeListBuilder"
+ *   },
+ *   admin_permission = "administer blocks",
+ *   config_prefix = "type",
+ *   bundle_of = "block_content",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label"
+ *   },
+ *   links = {
+ *     "delete-form" = "block_content.type_delete",
+ *     "edit-form" = "block_content.type_edit"
+ *   }
+ * )
+ */
+class BlockContentType extends ConfigEntityBundleBase implements BlockContentTypeInterface {
+
+  /**
+   * The custom block type ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The custom block type label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The default revision setting for custom blocks of this type.
+   *
+   * @var bool
+   */
+  public $revision;
+
+  /**
+   * The description of the block type.
+   *
+   * @var string
+   */
+  public $description;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
+    parent::postSave($storage, $update);
+
+    if (!$update && !$this->isSyncing()) {
+      entity_invoke_bundle_hook('create', 'block_content', $this->id());
+      if (!$this->isSyncing()) {
+        block_content_add_body_field($this->id);
+      }
+    }
+    elseif ($this->getOriginalId() != $this->id) {
+      entity_invoke_bundle_hook('rename', 'block_content', $this->getOriginalId(), $this->id);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function postDelete(EntityStorageInterface $storage, array $entities) {
+    parent::postDelete($storage, $entities);
+
+    foreach ($entities as $entity) {
+      entity_invoke_bundle_hook('delete', 'block_content', $entity->id());
+    }
+  }
+
+}
diff --git a/core/modules/block_content/src/Form/BlockContentDeleteForm.php b/core/modules/block_content/src/Form/BlockContentDeleteForm.php
new file mode 100644
index 0000000..35c0386
--- /dev/null
+++ b/core/modules/block_content/src/Form/BlockContentDeleteForm.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Form\BlockContentDeleteForm.
+ */
+
+namespace Drupal\block_content\Form;
+
+use Drupal\Core\Entity\ContentEntityConfirmFormBase;
+use Drupal\Core\Url;
+
+/**
+ * Provides a confirmation form for deleting a custom block entity.
+ */
+class BlockContentDeleteForm extends ContentEntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelRoute() {
+    return new Url('block.admin_display');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $instances = $this->entity->getInstances();
+
+    $form['message'] = array(
+      '#markup' => format_plural(count($instances), 'This will also remove 1 placed block instance.', 'This will also remove @count placed block instances.'),
+      '#access' => !empty($instances),
+    );
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submit(array $form, array &$form_state) {
+    $this->entity->delete();
+    drupal_set_message($this->t('Custom block %label has been deleted.', array('%label' => $this->entity->label())));
+    watchdog('block_content', 'Custom block %label has been deleted.', array('%label' => $this->entity->label()), WATCHDOG_NOTICE);
+    $form_state['redirect_route'] = new Url('block_content.list');
+  }
+
+}
diff --git a/core/modules/block_content/src/Form/BlockContentTypeDeleteForm.php b/core/modules/block_content/src/Form/BlockContentTypeDeleteForm.php
new file mode 100644
index 0000000..7e196ee
--- /dev/null
+++ b/core/modules/block_content/src/Form/BlockContentTypeDeleteForm.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Form\BlockContentTypeDeleteForm.
+ */
+
+namespace Drupal\block_content\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a confirmation form for deleting a custom block type entity.
+ */
+class BlockContentTypeDeleteForm extends EntityConfirmFormBase {
+
+  /**
+   * The query factory to create entity queries.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  public $queryFactory;
+
+  /**
+   * Constructs a query factory object.
+   *
+   * @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
+   *   The entity query object.
+   */
+  public function __construct(QueryFactory $query_factory) {
+    $this->queryFactory = $query_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.query')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete %label?', array('%label' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelRoute() {
+    return new Url('block_content.type_list');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $blocks = $this->queryFactory->get('block_content')->condition('type', $this->entity->id())->execute();
+    if (!empty($blocks)) {
+      $caption = '<p>' . format_plural(count($blocks), '%label is used by 1 custom block on your site. You can not remove this block type until you have removed all of the %label blocks.', '%label is used by @count custom blocks on your site. You may not remove %label until you have removed all of the %label custom blocks.', array('%label' => $this->entity->label())) . '</p>';
+      $form['description'] = array('#markup' => $caption);
+      return $form;
+    }
+    else {
+      return parent::buildForm($form, $form_state);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submit(array $form, array &$form_state) {
+    $this->entity->delete();
+    drupal_set_message(t('Custom block type %label has been deleted.', array('%label' => $this->entity->label())));
+    watchdog('block_content', 'Custom block type %label has been deleted.', array('%label' => $this->entity->label()), WATCHDOG_NOTICE);
+    $form_state['redirect_route'] = $this->getCancelRoute();
+  }
+
+}
diff --git a/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
new file mode 100644
index 0000000..9f3d92d
--- /dev/null
+++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Plugin\Block\BlockContentBlock.
+ */
+
+namespace Drupal\block_content\Plugin\Block;
+
+use Drupal\block\BlockBase;
+use Drupal\block\BlockManagerInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines a generic custom block type.
+ *
+ * @Block(
+ *  id = "block_content",
+ *  admin_label = @Translation("Custom block"),
+ *  category = @Translation("Custom"),
+ *  derivative = "Drupal\block_content\Plugin\Derivative\BlockContent"
+ * )
+ */
+class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The Plugin Block Manager.
+   *
+   * @var \Drupal\block\BlockManagerInterface.
+   */
+  protected $blockManager;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The Module Handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface.
+   */
+  protected $moduleHandler;
+
+  /**
+   * The Drupal account to use for checking for access to block.
+   *
+   * @var \Drupal\Core\Session\AccountInterface.
+   */
+  protected $account;
+
+  /**
+   * Constructs a new BlockContentBlock.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\block\BlockManagerInterface
+   *   The Plugin Block Manager.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface
+   *   The Module Handler.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account for which view access should be checked.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockManagerInterface $block_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $account) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->blockManager = $block_manager;
+    $this->entityManager = $entity_manager;
+    $this->moduleHandler = $module_handler;
+    $this->account = $account;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('plugin.manager.block'),
+      $container->get('entity.manager'),
+      $container->get('module_handler'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array(
+      'status' => TRUE,
+      'info' => '',
+      'view_mode' => 'full',
+      // Modify the default max age for custom block blocks: modifications made
+      // to them will automatically invalidate corresponding cache tags, thus
+      // allowing us to cache custom block blocks forever.
+      'cache' => array(
+        'max_age' => \Drupal\Core\Cache\Cache::PERMANENT,
+      ),
+    );
+  }
+
+  /**
+   * Overrides \Drupal\block\BlockBase::blockForm().
+   *
+   * Adds body and description fields to the block configuration form.
+   */
+  public function blockForm($form, &$form_state) {
+    $form['block_content']['view_mode'] = array(
+      '#type' => 'select',
+      '#options' => $this->entityManager->getViewModeOptions('block_content'),
+      '#title' => t('View mode'),
+      '#description' => t('Output the block in this view mode.'),
+      '#default_value' => $this->configuration['view_mode']
+    );
+    $form['title']['#description'] = t('The title of the block as shown to the user.');
+    return $form;
+  }
+
+  /**
+   * Overrides \Drupal\block\BlockBase::blockSubmit().
+   */
+  public function blockSubmit($form, &$form_state) {
+    // Invalidate the block cache to update custom block-based derivatives.
+    if ($this->moduleHandler->moduleExists('block')) {
+      $this->configuration['view_mode'] = $form_state['values']['block_content']['view_mode'];
+      $this->blockManager->clearCachedDefinitions();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $uuid = $this->getDerivativeId();
+    if ($block = entity_load_by_uuid('block_content', $uuid)) {
+      return entity_view($block, $this->configuration['view_mode']);
+    }
+    else {
+      return array(
+        '#markup' => t('Block with uuid %uuid does not exist. <a href="!url">Add custom block</a>.', array(
+          '%uuid' => $uuid,
+          '!url' => url('block/add')
+        )),
+        '#access' => $this->account->hasPermission('administer blocks')
+      );
+    }
+  }
+}
diff --git a/core/modules/block_content/src/Plugin/Derivative/BlockContent.php b/core/modules/block_content/src/Plugin/Derivative/BlockContent.php
new file mode 100644
index 0000000..a7c1784
--- /dev/null
+++ b/core/modules/block_content/src/Plugin/Derivative/BlockContent.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Plugin\Derivative\BlockContent.
+ */
+
+namespace Drupal\block_content\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DerivativeBase;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
+
+/**
+ * Retrieves block plugin definitions for all custom blocks.
+ */
+class BlockContent extends DerivativeBase {
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $block_contents = entity_load_multiple('block_content');
+    foreach ($block_contents as $block_content) {
+      $this->derivatives[$block_content->uuid()] = $base_plugin_definition;
+      $this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label();
+    }
+    return parent::getDerivativeDefinitions($base_plugin_definition);
+  }
+}
diff --git a/core/modules/block_content/src/Plugin/Menu/LocalAction/BlockContentAddLocalAction.php b/core/modules/block_content/src/Plugin/Menu/LocalAction/BlockContentAddLocalAction.php
new file mode 100644
index 0000000..e22425e
--- /dev/null
+++ b/core/modules/block_content/src/Plugin/Menu/LocalAction/BlockContentAddLocalAction.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction.
+ */
+
+namespace Drupal\block_content\Plugin\Menu\LocalAction;
+
+use Drupal\Core\Menu\LocalActionDefault;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Modifies the 'Add custom block' local action.
+ */
+class BlockContentAddLocalAction extends LocalActionDefault {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOptions(Request $request) {
+    $options = parent::getOptions($request);
+    // If the route specifies a theme, append it to the query string.
+    if ($request->attributes->has('theme')) {
+      $options['query']['theme'] = $request->attributes->get('theme');
+    }
+    // Adds a destination on custom block listing.
+    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block_content.list') {
+      $options['query']['destination'] = 'admin/structure/block/block-content';
+    }
+    // Adds a destination on custom block listing.
+    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block_content.list') {
+      $options['query']['destination'] = 'admin/structure/block/block-content';
+    }
+    return $options;
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
new file mode 100644
index 0000000..52c3286
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentCacheTagsTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
+
+/**
+ * Tests the Custom Block entity's cache tags.
+ */
+class BlockContentCacheTagsTest extends EntityCacheTagsTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('block_content');
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return parent::generateStandardizedInfo('Custom Block', 'Custom Block');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function createEntity() {
+    // Create a "Llama" custom block.
+    $block_content = entity_create('block_content', array(
+      'info' => 'Llama',
+      'type' => 'basic',
+      'body' => array(
+        'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
+        'format' => 'plain_text',
+      ),
+    ));
+    $block_content->save();
+
+    return $block_content;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Each comment must have a comment body, which always has a text format.
+   */
+  protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
+    return array('filter_format:plain_text');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentCreationTest.php b/core/modules/block_content/src/Tests/BlockContentCreationTest.php
new file mode 100644
index 0000000..dd98507
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentCreationTest.php
@@ -0,0 +1,222 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentCreationTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\Core\Database\Database;
+use Drupal\block_content\Entity\BlockContent;
+
+/**
+ * Tests creating and saving a block.
+ */
+class BlockContentCreationTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * Enable dummy module that implements hook_block_insert() for exceptions.
+   *
+   * @var array
+   */
+  public static $modules = array('block_content_test', 'dblog');
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block creation',
+      'description' => 'Create a block and test saving it.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Creates a "Basic page" block and verifies its consistency in the database.
+   */
+  public function testBlockContentCreation() {
+    // Add a new view mode and verify if it is selected as expected.
+    $this->drupalLogin($this->drupalCreateUser(array('administer display modes')));
+    $this->drupalGet('admin/structure/display-modes/view/add/block_content');
+    $edit = array(
+      'id' => 'test_view_mode',
+      'label' => 'Test View Mode',
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertRaw(t('Saved the %label view mode.', array('%label' => $edit['label'])));
+
+    $this->drupalLogin($this->adminUser);
+
+    // Create a block.
+    $edit = array();
+    $edit['info[0][value]'] = 'Test Block';
+    $edit['body[0][value]'] = $this->randomName(16);
+    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
+
+    // Check that the Basic block has been created.
+    $this->assertRaw(format_string('!block %name has been created.', array(
+      '!block' => 'Basic block',
+      '%name' => $edit['info[0][value]']
+    )), 'Basic block created.');
+
+    // Change the view mode.
+    $view_mode['settings[block_content][view_mode]'] = 'test_view_mode';
+    $this->drupalPostForm(NULL, $view_mode, t('Save block'));
+
+    // Go to the configure page and verify that the new view mode is correct.
+    $this->drupalGet('admin/structure/block/manage/testblock');
+    $this->assertFieldByXPath('//select[@name="settings[block_content][view_mode]"]/option[@selected="selected"]/@value', 'test_view_mode', 'View mode changed to Test View Mode');
+
+    // Test the available view mode options.
+    $this->assertOption('edit-settings-block-content-view-mode', 'default', 'The default view mode is available.');
+
+    // Check that the block exists in the database.
+    $blocks = entity_load_multiple_by_properties('block_content', array('info' => $edit['info[0][value]']));
+    $block = reset($blocks);
+    $this->assertTrue($block, 'Custom Block found in database.');
+
+    // Check that attempting to create another block with the same value for
+    // 'info' returns an error.
+    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
+
+    // Check that the Basic block has been created.
+    $this->assertRaw(format_string('A block with description %name already exists.', array(
+      '%name' => $edit['info[0][value]']
+    )));
+    $this->assertResponse(200);
+  }
+
+  /**
+   * Create a default custom block.
+   *
+   * Creates a custom block from defaults and ensures that the 'basic block'
+   * type is being used.
+   */
+  public function testDefaultBlockContentCreation() {
+    $edit = array();
+    $edit['info[0][value]'] = $this->randomName(8);
+    $edit['body[0][value]'] = $this->randomName(16);
+    // Don't pass the custom block type in the url so the default is forced.
+    $this->drupalPostForm('block/add', $edit, t('Save'));
+
+    // Check that the block has been created and that it is a basic block.
+    $this->assertRaw(format_string('!block %name has been created.', array(
+      '!block' => 'Basic block',
+      '%name' => $edit['info[0][value]'],
+    )), 'Basic block created.');
+
+    // Check that the block exists in the database.
+    $blocks = entity_load_multiple_by_properties('block_content', array('info' => $edit['info[0][value]']));
+    $block = reset($blocks);
+    $this->assertTrue($block, 'Default Custom Block found in database.');
+  }
+
+  /**
+   * Verifies that a transaction rolls back the failed creation.
+   */
+  public function testFailedBlockCreation() {
+    // Create a block.
+    try {
+      $this->createBlockContent('fail_creation');
+      $this->fail('Expected exception has not been thrown.');
+    }
+    catch (\Exception $e) {
+      $this->pass('Expected exception has been thrown.');
+    }
+
+    if (Database::getConnection()->supportsTransactions()) {
+      // Check that the block does not exist in the database.
+      $id = db_select('block_content', 'b')
+        ->fields('b', array('id'))
+        ->condition('info', 'fail_creation')
+        ->execute()
+        ->fetchField();
+      $this->assertFalse($id, 'Transactions supported, and block not found in database.');
+    }
+    else {
+      // Check that the block exists in the database.
+      $id = db_select('block_content', 'b')
+        ->fields('b', array('id'))
+        ->condition('info', 'fail_creation')
+        ->execute()
+        ->fetchField();
+      $this->assertTrue($id, 'Transactions not supported, and block found in database.');
+
+      // Check that the failed rollback was logged.
+      $records = db_query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll();
+      $this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.');
+    }
+  }
+
+  /**
+   * Test deleting a block.
+   */
+  public function testBlockDelete() {
+    // Create a block.
+    $edit = array();
+    $edit['info[0][value]'] = $this->randomName(8);
+    $body = $this->randomName(16);
+    $edit['body[0][value]'] = $body;
+    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
+
+    // Place the block.
+    $instance = array(
+      'id' => drupal_strtolower($edit['info[0][value]']),
+      'settings[label]' => $edit['info[0][value]'],
+      'region' => 'sidebar_first',
+    );
+    $block = entity_load('block_content', 1);
+    $url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
+    $this->drupalPostForm($url, $instance, t('Save block'));
+
+    $block = BlockContent::load(1);
+
+    // Test getInstances method.
+    $this->assertEqual(1, count($block->getInstances()));
+
+    // Navigate to home page.
+    $this->drupalGet('');
+    $this->assertText($body);
+
+    // Delete the block.
+    $this->drupalGet('block/1/delete');
+    $this->assertText(format_plural(1, 'This will also remove 1 placed block instance.', 'This will also remove @count placed block instance.'));
+
+    $this->drupalPostForm(NULL, array(), 'Delete');
+    $this->assertRaw(t('Custom block %name has been deleted.', array('%name' => $edit['info[0][value]'])));
+
+    // Create another block and force the plugin cache to flush.
+    $edit2 = array();
+    $edit2['info[0][value]'] = $this->randomName(8);
+    $body2 = $this->randomName(16);
+    $edit2['body[0][value]'] = $body2;
+    $this->drupalPostForm('block/add/basic', $edit2, t('Save'));
+
+    $this->assertNoRaw('Error message');
+
+    // Create another block with no instances, and test we don't get a
+    // confirmation message about deleting instances.
+    $edit3 = array();
+    $edit3['info[0][value]'] = $this->randomName(8);
+    $body = $this->randomName(16);
+    $edit3['body[0][value]'] = $body;
+    $this->drupalPostForm('block/add/basic', $edit3, t('Save'));
+
+    // Show the delete confirm form.
+    $this->drupalGet('block/3/delete');
+    $this->assertNoText('This will also remove');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentFieldTest.php b/core/modules/block_content/src/Tests/BlockContentFieldTest.php
new file mode 100644
index 0000000..f32a1ea
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentFieldTest.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentFieldTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+/**
+ * Tests the block edit functionality.
+ *
+ * @todo Consider removing this test when https://drupal.org/node/1822000 is
+ * fixed.
+ */
+class BlockContentFieldTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'block_content', 'link');
+
+  /**
+   * The created field.
+   *
+   * @var \Drupal\field\Entity\FieldConfig
+   */
+  protected $field;
+
+  /**
+   * The created instance.
+   *
+   * @var \Drupal\field\Entity\FieldInstanceConfig
+   */
+  protected $instance;
+
+  /**
+   * The block type.
+   *
+   * @var \Drupal\block_content\Entity\BlockContentType
+   */
+  protected $blockType;
+
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block field test',
+      'description' => 'Test block fieldability.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Checks block edit functionality.
+   */
+  public function testBlockFields() {
+    $this->drupalLogin($this->adminUser);
+
+    $this->blockType = $this->createBlockContentType('link');
+
+    // Create a field with settings to validate.
+    $this->field = entity_create('field_config', array(
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'block_content',
+      'type' => 'link',
+      'cardinality' => 2,
+    ));
+    $this->field->save();
+    $this->instance = entity_create('field_instance_config', array(
+      'field_name' => $this->field->getName(),
+      'entity_type' => 'block_content',
+      'bundle' => 'link',
+      'settings' => array(
+        'title' => DRUPAL_OPTIONAL,
+      ),
+    ));
+    $this->instance->save();
+    entity_get_form_display('block_content', 'link', 'default')
+      ->setComponent($this->field->getName(), array(
+        'type' => 'link_default',
+      ))
+      ->save();
+    entity_get_display('block_content', 'link', 'default')
+      ->setComponent($this->field->getName(), array(
+        'type' => 'link',
+        'label' => 'hidden',
+      ))
+      ->save();
+
+    // Create a block.
+    $this->drupalGet('block/add/link');
+    $edit = array(
+      'info[0][value]' => $this->randomName(8),
+      $this->field->getName() . '[0][url]' => 'http://example.com',
+      $this->field->getName() . '[0][title]' => 'Example.com'
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $block = entity_load('block_content', 1);
+    $url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
+    // Place the block.
+    $instance = array(
+      'id' => drupal_strtolower($edit['info[0][value]']),
+      'settings[label]' => $edit['info[0][value]'],
+      'region' => 'sidebar_first',
+    );
+    $this->drupalPostForm($url, $instance, t('Save block'));
+    // Navigate to home page.
+    $this->drupalGet('<front>');
+    $this->assertLinkByHref('http://example.com');
+    $this->assertText('Example.com');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentListTest.php b/core/modules/block_content/src/Tests/BlockContentListTest.php
new file mode 100644
index 0000000..048fdfb
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentListTest.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentListTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests the listing of custom blocks.
+ *
+ * @see \Drupal\block\BlockContentListBuilder
+ */
+class BlockContentListTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'block_content', 'config_translation');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block listing',
+      'description' => 'Tests the listing of custom blocks.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Tests the custom block listing page.
+   */
+  public function testListing() {
+    $this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'translate configuration')));
+    $this->drupalGet('admin/structure/block/block-content');
+
+    // Test for the page title.
+    $this->assertTitle(t('Custom block library') . ' | Drupal');
+
+    // Test for the table.
+    $element = $this->xpath('//div[@class="l-content"]//table');
+    $this->assertTrue($element, 'Configuration entity list table found.');
+
+    // Test the table header.
+    $elements = $this->xpath('//div[@class="l-content"]//table/thead/tr/th');
+    $this->assertEqual(count($elements), 2, 'Correct number of table header cells found.');
+
+    // Test the contents of each th cell.
+    $expected_items = array(t('Block description'), t('Operations'));
+    foreach ($elements as $key => $element) {
+      $this->assertIdentical((string) $element[0], $expected_items[$key]);
+    }
+
+    $label = 'Antelope';
+    $new_label = 'Albatross';
+    // Add a new entity using the operations link.
+    $link_text = t('Add custom block');
+    $this->assertLink($link_text);
+    $this->clickLink($link_text);
+    $this->assertResponse(200);
+    $edit = array();
+    $edit['info[0][value]'] = $label;
+    $edit['body[0][value]'] = $this->randomName(16);
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    // Confirm that once the user returns to the listing, the text of the label
+    // (versus elsewhere on the page).
+    $this->assertFieldByXpath('//td', $label, 'Label found for added block.');
+
+    // Check the number of table row cells.
+    $elements = $this->xpath('//div[@class="l-content"]//table/tbody/tr[@class="odd"]/td');
+    $this->assertEqual(count($elements), 2, 'Correct number of table row cells found.');
+    // Check the contents of each row cell. The first cell contains the label,
+    // the second contains the machine name, and the third contains the
+    // operations list.
+    $this->assertIdentical((string) $elements[0], $label);
+
+    // Edit the entity using the operations link.
+    $blocks = $this->container
+      ->get('entity.manager')
+      ->getStorage('block_content')
+      ->loadByProperties(array('info' => $label));
+    $block = reset($blocks);
+    if (!empty($block)) {
+      $this->assertLinkByHref('block/' . $block->id());
+      $this->clickLink(t('Edit'));
+      $this->assertResponse(200);
+      $this->assertTitle(strip_tags(t('Edit custom block %label', array('%label' => $label)) . ' | Drupal'));
+      $edit = array('info[0][value]' => $new_label);
+      $this->drupalPostForm(NULL, $edit, t('Save'));
+    }
+    else {
+      $this->fail('Did not find Albatross block in the database.');
+    }
+
+    // Confirm that once the user returns to the listing, the text of the label
+    // (versus elsewhere on the page).
+    $this->assertFieldByXpath('//td', $new_label, 'Label found for updated custom block.');
+
+    // Delete the added entity using the operations link.
+    $this->assertLinkByHref('block/' . $block->id() . '/delete');
+    $delete_text = t('Delete');
+    $this->clickLink($delete_text);
+    $this->assertResponse(200);
+    $this->assertTitle(strip_tags(t('Are you sure you want to delete %label?', array('%label' => $new_label)) . ' | Drupal'));
+    $this->drupalPostForm(NULL, array(), $delete_text);
+
+    // Verify that the text of the label and machine name does not appear in
+    // the list (though it may appear elsewhere on the page).
+    $this->assertNoFieldByXpath('//td', $new_label, 'No label found for deleted custom block.');
+
+    // Confirm that the empty text is displayed.
+    $this->assertText(t('There is no Custom Block yet.'));
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentPageViewTest.php b/core/modules/block_content/src/Tests/BlockContentPageViewTest.php
new file mode 100644
index 0000000..b8edb11
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentPageViewTest.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentPageViewTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+/**
+ * Tests the block edit functionality.
+ */
+class BlockContentPageViewTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'block_content', 'block_content_test');
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block page view',
+      'description' => 'Create a block and test block access by attempting to view the block.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Checks block edit functionality.
+   */
+  public function testPageEdit() {
+    $this->drupalLogin($this->adminUser);
+    $block = $this->createBlockContent();
+
+    // Attempt to view the block.
+    $this->drupalGet('block-content/' . $block->id());
+
+    // Assert response was '200' and not '403 Access denied'.
+    $this->assertResponse('200', 'User was able the view the block');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentRevisionsTest.php b/core/modules/block_content/src/Tests/BlockContentRevisionsTest.php
new file mode 100644
index 0000000..ca05a73
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentRevisionsTest.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentRevisionsTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+/**
+ * Tests the block revision functionality.
+ */
+class BlockContentRevisionsTest extends BlockContentTestBase {
+
+  /**
+   * Stores blocks created during the test.
+   * @var array
+   */
+  protected $blocks;
+
+  /**
+   * Stores log messages used during the test.
+   * @var array
+   */
+  protected $logs;
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block revisions',
+      'description' => 'Create a block with revisions.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create initial block.
+    $block = $this->createBlockContent('initial');
+
+    $blocks = array();
+    $logs = array();
+
+    // Get original block.
+    $blocks[] = $block->getRevisionId();
+    $logs[] = '';
+
+    // Create three revisions.
+    $revision_count = 3;
+    for ($i = 0; $i < $revision_count; $i++) {
+      $block->setNewRevision(TRUE);
+      $block->setRevisionLog($this->randomName(32));
+      $logs[] = $block->getRevisionLog();
+      $block->save();
+      $blocks[] = $block->getRevisionId();
+    }
+
+    $this->blocks = $blocks;
+    $this->logs = $logs;
+  }
+
+  /**
+   * Checks block revision related operations.
+   */
+  public function testRevisions() {
+    $blocks = $this->blocks;
+    $logs = $this->logs;
+
+    foreach ($blocks as $delta => $revision_id) {
+      // Confirm the correct revision text appears.
+      $loaded = entity_revision_load('block_content', $revision_id);
+      // Verify log is the same.
+      $this->assertEqual($loaded->getRevisionLog(), $logs[$delta], format_string('Correct log message found for revision !revision', array(
+        '!revision' => $loaded->getRevisionId(),
+      )));
+    }
+
+    // Confirm that this is the default revision.
+    $this->assertTrue($loaded->isDefaultRevision(), 'Third block revision is the default one.');
+
+    // Make a new revision and set it to not be default.
+    // This will create a new revision that is not "front facing".
+    // Save this as a non-default revision.
+    $loaded->setNewRevision();
+    $loaded->isDefaultRevision(FALSE);
+    $loaded->body = $this->randomName(8);
+    $loaded->save();
+
+    $this->drupalGet('block/' . $loaded->id());
+    $this->assertNoText($loaded->body->value, 'Revision body text is not present on default version of block.');
+
+    // Verify that the non-default revision id is greater than the default
+    // revision id.
+    $default_revision = entity_load('block_content', $loaded->id());
+    $this->assertTrue($loaded->getRevisionId() > $default_revision->getRevisionId(), 'Revision id is greater than default revision id.');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentSaveTest.php b/core/modules/block_content/src/Tests/BlockContentSaveTest.php
new file mode 100644
index 0000000..03d0857
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentSaveTest.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentSaveTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\block_content\Entity\BlockContent;
+
+/**
+ * Tests block save related functionality.
+ */
+class BlockContentSaveTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block_content_test');
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block save',
+      'description' => 'Test $block_content->save() for saving content.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Checks whether custom block IDs are saved properly during an import.
+   */
+  public function testImport() {
+    // Custom block ID must be a number that is not in the database.
+    $max_id = db_query('SELECT MAX(id) FROM {block_content}')->fetchField();
+    $test_id = $max_id + mt_rand(1000, 1000000);
+    $info = $this->randomName(8);
+    $block_array = array(
+      'info' => $info,
+      'body' => array('value' => $this->randomName(32)),
+      'type' => 'basic',
+      'id' => $test_id
+    );
+    $block = entity_create('block_content', $block_array);
+    $block->enforceIsNew(TRUE);
+    $block->save();
+
+    // Verify that block_submit did not wipe the provided id.
+    $this->assertEqual($block->id(), $test_id, 'Block imported using provide id');
+
+    // Test the import saved.
+    $block_by_id = BlockContent::load($test_id);
+    $this->assertTrue($block_by_id, 'Custom block load by block ID.');
+    $this->assertIdentical($block_by_id->body->value, $block_array['body']['value']);
+  }
+
+  /**
+   * Tests determing changes in hook_block_presave().
+   *
+   * Verifies the static block load cache is cleared upon save.
+   */
+  public function testDeterminingChanges() {
+    // Initial creation.
+    $block = $this->createBlockContent('test_changes');
+    $this->assertEqual($block->getChangedTime(), REQUEST_TIME, 'Creating a block sets default "changed" timestamp.');
+
+    // Update the block without applying changes.
+    $block->save();
+    $this->assertEqual($block->label(), 'test_changes', 'No changes have been determined.');
+
+    // Apply changes.
+    $block->setInfo('updated');
+    $block->save();
+
+    // The hook implementations block_content_test_block_content_presave() and
+    // block_content_test_block_content_update() determine changes and change the
+    // title as well as programatically set the 'changed' timestamp.
+    $this->assertEqual($block->label(), 'updated_presave_update', 'Changes have been determined.');
+    $this->assertEqual($block->getChangedTime(), 979534800, 'Saving a custom block uses "changed" timestamp set in presave hook.');
+
+    // Test the static block load cache to be cleared.
+    $block = BlockContent::load($block->id());
+    $this->assertEqual($block->label(), 'updated_presave', 'Static cache has been cleared.');
+  }
+
+  /**
+   * Tests saving a block on block insert.
+   *
+   * This test ensures that a block has been fully saved when
+   * hook_block_content_insert() is invoked, so that the block can be saved again
+   * in a hook implementation without errors.
+   *
+   * @see block_test_block_insert()
+   */
+  public function testBlockContentSaveOnInsert() {
+    // block_content_test_block_content_insert() triggers a save on insert if the
+    // title equals 'new'.
+    $block = $this->createBlockContent('new');
+    $this->assertEqual($block->label(), 'BlockContent ' . $block->id(), 'Custom block saved on block insert.');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentTestBase.php b/core/modules/block_content/src/Tests/BlockContentTestBase.php
new file mode 100644
index 0000000..4f77de4
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentTestBase.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentTestBase.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Sets up page and article content types.
+ */
+abstract class BlockContentTestBase extends WebTestBase {
+
+  /**
+   * Profile to use.
+   */
+  protected $profile = 'testing';
+
+  /**
+   * Admin user
+   *
+   * @var object
+   */
+  protected $adminUser;
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = array(
+    'administer blocks'
+  );
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'block_content');
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+  }
+
+  /**
+   * Creates a custom block.
+   *
+   * @param string $title
+   *   (optional) Title of block. When no value is given uses a random name.
+   *   Defaults to FALSE.
+   * @param string $bundle
+   *   (optional) Bundle name. Defaults to 'basic'.
+   *
+   * @return \Drupal\block_content\Entity\BlockContent
+   *   Created custom block.
+   */
+  protected function createBlockContent($title = FALSE, $bundle = 'basic') {
+    $title = ($title ? : $this->randomName());
+    if ($block_content = entity_create('block_content', array(
+      'info' => $title,
+      'type' => $bundle,
+      'langcode' => 'en'
+    ))) {
+      $block_content->save();
+    }
+    return $block_content;
+  }
+
+  /**
+   * Creates a custom block type (bundle).
+   *
+   * @param string $label
+   *   The block type label.
+   *
+   * @return \Drupal\block_content\Entity\BlockContentType
+   *   Created custom block type.
+   */
+  protected function createBlockContentType($label) {
+    $bundle = entity_create('block_content_type', array(
+      'id' => $label,
+      'label' => $label,
+      'revision' => FALSE
+    ));
+    $bundle->save();
+    return $bundle;
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
new file mode 100644
index 0000000..35cf5ec
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentTranslationUITest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\content_translation\Tests\ContentTranslationUITest;
+use Drupal\block_content\Entity\BlockContent;
+
+/**
+ * Tests the Custom Block Translation UI.
+ */
+class BlockContentTranslationUITest extends ContentTranslationUITest {
+
+  /**
+   * The name of the test block.
+   */
+  protected $name;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array(
+    'language',
+    'content_translation',
+    'block',
+    'field_ui',
+    'block_content'
+  );
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block translation UI',
+      'description' => 'Tests the node translation UI.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Overrides \Drupal\simpletest\WebTestBase::setUp().
+   */
+  public function setUp() {
+    $this->entityTypeId = 'block_content';
+    $this->bundle = 'basic';
+    $this->name = drupal_strtolower($this->randomName());
+    $this->testLanguageSelector = FALSE;
+    parent::setUp();
+  }
+
+  /**
+   * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getTranslatorPermission().
+   */
+  public function getTranslatorPermissions() {
+    return array_merge(parent::getTranslatorPermissions(), array(
+      'translate any entity',
+      'access administration pages',
+      'administer blocks',
+      'administer block_content fields'
+    ));
+  }
+
+  /**
+   * Creates a custom block.
+   *
+   * @param string $title
+   *   (optional) Title of block. When no value is given uses a random name.
+   *   Defaults to FALSE.
+   * @param string $bundle
+   *   (optional) Bundle name. When no value is given, defaults to
+   *   $this->bundle. Defaults to FALSE.
+   *
+   * @return \Drupal\block_content\Entity\BlockContent
+   *   Created custom block.
+   */
+  protected function createBlockContent($title = FALSE, $bundle = FALSE) {
+    $title = ($title ? : $this->randomName());
+    $bundle = ($bundle ? : $this->bundle);
+    $block_content = entity_create('block_content', array(
+      'info' => $title,
+      'type' => $bundle,
+      'langcode' => 'en'
+    ));
+    $block_content->save();
+    return $block_content;
+  }
+
+  /**
+   * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getNewEntityValues().
+   */
+  protected function getNewEntityValues($langcode) {
+    return array('info' => $this->name) + parent::getNewEntityValues($langcode);
+  }
+
+  /**
+   * Returns an edit array containing the values to be posted.
+   */
+  protected function getEditValues($values, $langcode, $new = FALSE) {
+    $edit = parent::getEditValues($values, $langcode, $new);
+    foreach ($edit as $property => $value) {
+      if ($property == 'info') {
+        $edit['info[0][value]'] = $value;
+        unset($edit[$property]);
+      }
+    }
+    return $edit;
+  }
+
+  /**
+   * Test that no metadata is stored for a disabled bundle.
+   */
+  public function testDisabledBundle() {
+    // Create a bundle that does not have translation enabled.
+    $disabled_bundle = $this->randomName();
+    $bundle = entity_create('block_content_type', array(
+      'id' => $disabled_bundle,
+      'label' => $disabled_bundle,
+      'revision' => FALSE
+    ));
+    $bundle->save();
+
+    // Create a node for each bundle.
+    $enabled_block_content = $this->createBlockContent();
+    $disabled_block_content = $this->createBlockContent(FALSE, $bundle->id());
+
+    // Make sure that only a single row was inserted into the
+    // {content_translation} table.
+    $rows = db_query('SELECT * FROM {content_translation}')->fetchAll();
+    $this->assertEqual(1, count($rows));
+    $this->assertEqual($enabled_block_content->id(), reset($rows)->entity_id);
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentTypeTest.php b/core/modules/block_content/src/Tests/BlockContentTypeTest.php
new file mode 100644
index 0000000..fc20f0c
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentTypeTest.php
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentTypeTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+/**
+ * Tests related to custom block types.
+ */
+class BlockContentTypeTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('field_ui');
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = array(
+    'administer blocks',
+    'administer block_content fields'
+  );
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block types',
+      'description' => 'Ensures that custom block type functions work correctly.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Tests creating a block type programmatically and via a form.
+   */
+  public function testBlockContentTypeCreation() {
+    // Create a block type programmaticaly.
+    $type = $this->createBlockContentType('other');
+
+    $block_type = entity_load('block_content_type', 'other');
+    $this->assertTrue($block_type, 'The new block type has been created.');
+
+    // Login a test user.
+    $this->drupalLogin($this->adminUser);
+
+    $this->drupalGet('block/add/' . $type->id());
+    $this->assertResponse(200, 'The new block type can be accessed at bloack/add.');
+
+    // Create a block type via the user interface.
+    $edit = array(
+      'id' => 'foo',
+      'label' => 'title for foo',
+    );
+    $this->drupalPostForm('admin/structure/block/block-content/types/add', $edit, t('Save'));
+    $block_type = entity_load('block_content_type', 'foo');
+    $this->assertTrue($block_type, 'The new block type has been created.');
+
+    // Check that the block type was created in site default language.
+    $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->id;
+    $this->assertEqual($block_type->langcode, $default_langcode);
+  }
+
+  /**
+   * Tests editing a block type using the UI.
+   */
+  public function testBlockContentTypeEditing() {
+    $this->drupalLogin($this->adminUser);
+    // We need two block types to prevent /block/add redirecting.
+    $this->createBlockContentType('other');
+
+    $field_definition = \Drupal::entityManager()->getFieldDefinitions('block_content', 'other')['body'];
+    $this->assertEqual($field_definition->getLabel(), 'Body', 'Body field was found.');
+
+    // Verify that title and body fields are displayed.
+    $this->drupalGet('block/add/basic');
+    $this->assertRaw('Block description', 'Block info field was found.');
+    $this->assertRaw('Body', 'Body field was found.');
+
+    // Change the block type name.
+    $edit = array(
+      'label' => 'Bar',
+    );
+    $this->drupalPostForm('admin/structure/block/block-content/manage/basic', $edit, t('Save'));
+    \Drupal::entityManager()->clearCachedFieldDefinitions();
+
+    $this->drupalGet('block/add');
+    $this->assertRaw('Bar', 'New name was displayed.');
+    $this->clickLink('Bar');
+    $this->assertEqual(url('block/add/basic', array('absolute' => TRUE)), $this->getUrl(), 'Original machine name was used in URL.');
+
+    // Remove the body field.
+    $this->drupalPostForm('admin/structure/block/block-content/manage/basic/fields/block_content.basic.body/delete', array(), t('Delete'));
+    // Resave the settings for this type.
+    $this->drupalPostForm('admin/structure/block/block-content/manage/basic', array(), t('Save'));
+    // Check that the body field doesn't exist.
+    $this->drupalGet('block/add/basic');
+    $this->assertNoRaw('Body', 'Body field was not found.');
+  }
+
+  /**
+   * Tests deleting a block type that still has content.
+   */
+  public function testBlockContentTypeDeletion() {
+    // Create a block type programmatically.
+    $type = $this->createBlockContentType('foo');
+
+    $this->drupalLogin($this->adminUser);
+
+    // Add a new block of this type.
+    $block = $this->createBlockContent(FALSE, 'foo');
+    // Attempt to delete the block type, which should not be allowed.
+    $this->drupalGet('admin/structure/block/block-content/manage/' . $type->id() . '/delete');
+    $this->assertRaw(
+      t('%label is used by 1 custom block on your site. You can not remove this block type until you have removed all of the %label blocks.', array('%label' => $type->label())),
+      'The block type will not be deleted until all blocks of that type are removed.'
+    );
+    $this->assertNoText(t('This action cannot be undone.'), 'The node type deletion confirmation form is not available.');
+
+    // Delete the block.
+    $block->delete();
+    // Attempt to delete the block type, which should now be allowed.
+    $this->drupalGet('admin/structure/block/block-content/manage/' . $type->id() . '/delete');
+    $this->assertRaw(
+      t('Are you sure you want to delete %type?', array('%type' => $type->id())),
+      'The block type is available for deletion.'
+    );
+    $this->assertText(t('This action cannot be undone.'), 'The custom block type deletion confirmation form is available.');
+  }
+
+  /**
+   * Tests that redirects work as expected when multiple block types exist.
+   */
+  public function testsBlockContentAddTypes() {
+    $this->drupalLogin($this->adminUser);
+    // Create two block types programmatically.
+    $type = $this->createBlockContentType('foo');
+    $type = $this->createBlockContentType('bar');
+
+    // Get the custom block storage.
+    $storage = $this->container
+      ->get('entity.manager')
+      ->getStorage('block_content');
+
+    // Enable all themes.
+    theme_enable(array('bartik', 'seven'));
+    $themes = array('bartik', 'seven', 'stark');
+    $theme_settings = $this->container->get('config.factory')->get('system.theme');
+    foreach ($themes as $default_theme) {
+      // Change the default theme.
+      $theme_settings->set('default', $default_theme)->save();
+      \Drupal::service('router.builder')->rebuild();
+
+      // For each enabled theme, go to its block page and test the redirects.
+      $themes = array('bartik', 'stark', 'seven');
+      foreach ($themes as $theme) {
+        // Test that adding a block from the 'place blocks' form sends you to the
+        // block configure form.
+        $path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme";
+        $this->drupalGet($path);
+        $this->clickLink(t('Add custom block'));
+        // The seven theme has markup inside the link, we cannot use clickLink().
+        if ($default_theme == 'seven') {
+          $options = $theme != $default_theme ? array('query' => array('theme' => $theme)) : array();
+          $this->assertLinkByHref(url('block/add/foo', $options));
+          $this->drupalGet('block/add/foo', $options);
+        }
+        else {
+          $this->clickLink('foo');
+        }
+        // Create a new block.
+        $edit = array('info[0][value]' => $this->randomName(8));
+        $this->drupalPostForm(NULL, $edit, t('Save'));
+        $blocks = $storage->loadByProperties(array('info' => $edit['info[0][value]']));
+        if (!empty($blocks)) {
+          $block = reset($blocks);
+          $destination = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . $theme;
+          $this->assertUrl(url($destination, array('absolute' => TRUE)));
+          $this->drupalPostForm(NULL, array(), t('Save block'));
+          $this->assertUrl(url("admin/structure/block/list/$theme", array('absolute' => TRUE, 'query' => array('block-placement' => drupal_html_class($edit['info[0][value]'])))));
+        }
+        else {
+          $this->fail('Could not load created block.');
+        }
+      }
+    }
+
+    // Test that adding a block from the 'custom blocks list' doesn't send you
+    // to the block configure form.
+    $this->drupalGet('admin/structure/block/block-content');
+    $this->clickLink(t('Add custom block'));
+    $this->clickLink('foo');
+    $edit = array('info[0][value]' => $this->randomName(8));
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $blocks = $storage->loadByProperties(array('info' => $edit['info[0][value]']));
+    if (!empty($blocks)) {
+      $destination = 'admin/structure/block/block-content';
+      $this->assertUrl(url($destination, array('absolute' => TRUE)));
+    }
+    else {
+      $this->fail('Could not load created block.');
+    }
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/PageEditTest.php b/core/modules/block_content/src/Tests/PageEditTest.php
new file mode 100644
index 0000000..3c35125
--- /dev/null
+++ b/core/modules/block_content/src/Tests/PageEditTest.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\PageEditTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+use Drupal\Core\Language\Language;
+
+/**
+ * Tests the block edit functionality.
+ */
+class PageEditTest extends BlockContentTestBase {
+
+  /**
+   * Declares test information.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block edit',
+      'description' => 'Create a block and test block edit functionality.',
+      'group' => 'Custom Block',
+    );
+  }
+
+  /**
+   * Checks block edit functionality.
+   */
+  public function testPageEdit() {
+    $this->drupalLogin($this->adminUser);
+
+    $title_key = 'info[0][value]';
+    $body_key = 'body[0][value]';
+    // Create block to edit.
+    $edit = array();
+    $edit['info[0][value]'] = drupal_strtolower($this->randomName(8));
+    $edit[$body_key] = $this->randomName(16);
+    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
+
+    // Check that the block exists in the database.
+    $blocks = \Drupal::entityQuery('block_content')->condition('info', $edit['info[0][value]'])->execute();
+    $block = entity_load('block_content', reset($blocks));
+    $this->assertTrue($block, 'Custom block found in database.');
+
+    // Load the edit page.
+    $this->drupalGet('block/' . $block->id());
+    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+    $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
+
+    // Edit the content of the block.
+    $edit = array();
+    $edit[$title_key] = $this->randomName(8);
+    $edit[$body_key] = $this->randomName(16);
+    // Stay on the current page, without reloading.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    // Edit the same block, creating a new revision.
+    $this->drupalGet("block/" . $block->id());
+    $edit = array();
+    $edit['info[0][value]'] = $this->randomName(8);
+    $edit[$body_key] = $this->randomName(16);
+    $edit['revision'] = TRUE;
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    // Ensure that the block revision has been created.
+    $revised_block = entity_load('block_content', $block->id(), TRUE);
+    $this->assertNotIdentical($block->getRevisionId(), $revised_block->getRevisionId(), 'A new revision has been created.');
+
+    // Test deleting the block.
+    $this->drupalGet("block/" . $revised_block->id());
+    $this->clickLink(t('Delete'));
+    $this->assertText(format_string('Are you sure you want to delete !label?', array('!label' => $revised_block->label())));
+  }
+
+}
diff --git a/core/modules/block_content/templates/block-content-add-list.html.twig b/core/modules/block_content/templates/block-content-add-list.html.twig
new file mode 100644
index 0000000..e5a5d97
--- /dev/null
+++ b/core/modules/block_content/templates/block-content-add-list.html.twig
@@ -0,0 +1,24 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a list of custom block types.
+ *
+ * Available variables:
+ * - types: A collection of all the available custom block types.
+ *   Each block type contains the following:
+ *   - link: A link to add a block of this type.
+ *   - description: A description of this custom block type.
+ *
+ * @see template_preprocess_block_content_add_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% spaceless %}
+  <dl class="node-type-list">
+    {% for type in types %}
+      <dt>{{ type.link }}</dt>
+      <dd>{{ type.description }}</dd>
+    {% endfor %}
+  </dl>
+{% endspaceless %}
diff --git a/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml b/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml
new file mode 100644
index 0000000..747efa4
--- /dev/null
+++ b/core/modules/block_content/tests/modules/block_content_test/block_content_test.info.yml
@@ -0,0 +1,6 @@
+name: "Custom Block module tests"
+type: module
+description: "Support module for custom block related testing."
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/block_content/tests/modules/block_content_test/block_content_test.module b/core/modules/block_content/tests/modules/block_content_test/block_content_test.module
new file mode 100644
index 0000000..d714cb2
--- /dev/null
+++ b/core/modules/block_content/tests/modules/block_content_test/block_content_test.module
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * A dummy module for testing custom block related hooks.
+ *
+ * This is a dummy module that implements custom block related hooks to test API
+ * interaction with the block_content module.
+ */
+
+use Drupal\block_content\Entity\BlockContent;
+
+/**
+ * Implements hook_block_content_view().
+ */
+function block_content_test_block_content_view(array &$build, BlockContent $block_content, $view_mode) {
+  // Add extra content.
+  $build['extra_content'] = array(
+    '#markup' => '<blink>Yowser</blink>',
+  );
+}
+
+/**
+ * Implements hook_block_content_presave().
+ */
+function block_content_test_block_content_presave(BlockContent $block_content) {
+  if ($block_content->label() == 'testing_block_content_presave') {
+    $block_content->setInfo($block_content->label() .'_presave');
+  }
+  // Determine changes.
+  if (!empty($block_content->original) && $block_content->original->label() == 'test_changes') {
+    if ($block_content->original->label() != $block_content->label()) {
+      $block_content->setInfo($block_content->label() .'_presave');
+      // Drupal 1.0 release.
+      $block_content->changed = 979534800;
+    }
+  }
+}
+
+/**
+ * Implements hook_block_content_update().
+ */
+function block_content_test_block_content_update(BlockContent $block_content) {
+  // Determine changes on update.
+  if (!empty($block_content->original) && $block_content->original->label() == 'test_changes') {
+    if ($block_content->original->label() != $block_content->label()) {
+      $block_content->setInfo($block_content->label() .'_update');
+    }
+  }
+}
+
+/**
+ * Implements hook_block_content_insert().
+ *
+ * This tests saving a block_content on block_content insert.
+ *
+ * @see \Drupal\block_content\Tests\BlockContentSaveTest::testBlockContentSaveOnInsert()
+ */
+function block_content_test_block_content_insert(BlockContent $block_content) {
+  // Set the block_content title to the block_content ID and save.
+  if ($block_content->label() == 'new') {
+    $block_content->setInfo('BlockContent ' . $block_content->id());
+    $block_content->save();
+  }
+  if ($block_content->label() == 'fail_creation') {
+    throw new Exception('Test exception for rollback.');
+  }
+}
diff --git a/core/modules/block_content/tests/modules/block_content_test/block_content_test.routing.yml b/core/modules/block_content/tests/modules/block_content_test/block_content_test.routing.yml
new file mode 100644
index 0000000..c25abf9
--- /dev/null
+++ b/core/modules/block_content/tests/modules/block_content_test/block_content_test.routing.yml
@@ -0,0 +1,6 @@
+block_content_test.block_content_view:
+  path: '/block-content/{block_content}'
+  defaults:
+    _entity_view: 'block_content'
+  requirements:
+    _entity_access: 'block_content.view'
diff --git a/core/modules/block_content/tests/src/Menu/BlockContentLocalTasksTest.php b/core/modules/block_content/tests/src/Menu/BlockContentLocalTasksTest.php
new file mode 100644
index 0000000..c989278
--- /dev/null
+++ b/core/modules/block_content/tests/src/Menu/BlockContentLocalTasksTest.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\Menu\BlockContentLocalTasksTest.
+ */
+
+namespace Drupal\block_content\Tests\Menu;
+
+use Drupal\Tests\Core\Menu\LocalTaskIntegrationTest;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * Tests existence of block_content local tasks.
+ *
+ * @group Drupal
+ * @group Block
+ */
+class BlockContentLocalTasksTest extends LocalTaskIntegrationTest {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Custom Block local tasks test',
+      'description' => 'Test block_content local tasks.',
+      'group' => 'Block',
+    );
+  }
+
+  public function setUp() {
+    $this->directoryList = array(
+      'block' => 'core/modules/block',
+      'block_content' => 'core/modules/block_content',
+    );
+    parent::setUp();
+
+    $config_factory = $this->getConfigFactoryStub(array('system.theme' => array(
+      'default' => 'test_c',
+    )));
+
+    $themes = array();
+    $themes['test_a'] = (object) array(
+      'status' => 0,
+    );
+    $themes['test_b'] = (object) array(
+      'status' => 1,
+      'info' => array(
+        'name' => 'test_b',
+      ),
+    );
+    $themes['test_c'] = (object) array(
+      'status' => 1,
+      'info' => array(
+        'name' => 'test_c',
+      ),
+    );
+    $theme_handler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
+    $theme_handler->expects($this->any())
+      ->method('listInfo')
+      ->will($this->returnValue($themes));
+
+    $container = new ContainerBuilder();
+    $container->set('config.factory', $config_factory);
+    $container->set('theme_handler', $theme_handler);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Checks block_content listing local tasks.
+   *
+   * @dataProvider getBlockContentListingRoutes
+   */
+  public function testBlockContentListLocalTasks($route) {
+    $this->assertLocalTasks($route, array(
+      0 => array(
+        'block.admin_display',
+        'block_content.list',
+      ),
+      1 => array(
+        'block_content.list_sub',
+        'block_content.type_list',
+      ),
+    ));
+  }
+
+  /**
+   * Provides a list of routes to test.
+   */
+  public function getBlockContentListingRoutes() {
+    return array(
+      array('block_content.list', 'block_content.type_list'),
+    );
+  }
+
+}
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
index acb5646..116275b 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationListUiTest.php
@@ -27,7 +27,7 @@ class ConfigTranslationListUiTest extends WebTestBase {
     'block',
     'config_translation',
     'contact',
-    'custom_block',
+    'block_content',
     'field',
     'field_ui',
     'menu_ui',
@@ -62,7 +62,7 @@ public function setUp() {
       'administer blocks',
       'administer contact forms',
       'administer content types',
-      'administer custom_block fields',
+      'administer block_content fields',
       'administer filters',
       'administer menu',
       'administer node fields',
@@ -187,20 +187,20 @@ protected function doVocabularyListTest() {
   /**
    * Tests the custom block listing for the translate operation.
    */
-  public function doCustomBlockTypeListTest() {
+  public function doCustomContentTypeListTest() {
     // Create a test custom block type to decouple looking for translate
     // operations link so this does not test more than necessary.
-    $custom_block_type = entity_create('custom_block_type', array(
+    $block_content_type = entity_create('block_content_type', array(
       'id' => Unicode::strtolower($this->randomName(16)),
       'label' => $this->randomName(),
       'revision' => FALSE
     ));
-    $custom_block_type->save();
+    $block_content_type->save();
 
     // Get the custom block type listing.
-    $this->drupalGet('admin/structure/block/custom-blocks/types');
+    $this->drupalGet('admin/structure/block/block-content/types');
 
-    $translate_link = 'admin/structure/block/custom-blocks/manage/' . $custom_block_type->id() . '/translate';
+    $translate_link = 'admin/structure/block/block-content/manage/' . $block_content_type->id() . '/translate';
     // Test if the link to translate the custom block type is on the page.
     $this->assertLinkByHref($translate_link);
 
@@ -404,8 +404,8 @@ public function doFieldListTest() {
         'field' => 'node.' . $content_type->id() . '.body',
       ),
       array(
-        'list' => 'admin/structure/block/custom-blocks/manage/basic/fields',
-        'field' => 'custom_block.basic.body',
+        'list' => 'admin/structure/block/block-content/manage/basic/fields',
+        'field' => 'block_content.basic.body',
       ),
     );
 
@@ -466,7 +466,7 @@ public function testTranslateOperationInListUi() {
     $this->doBlockListTest();
     $this->doMenuListTest();
     $this->doVocabularyListTest();
-    $this->doCustomBlockTypeListTest();
+    $this->doCustomContentTypeListTest();
     $this->doContactFormsListTest();
     $this->doContentTypeListTest();
     $this->doFormatsListTest();
diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_custom_block.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_custom_block.yml
index 709d1ea..c7dd4ab 100644
--- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_custom_block.yml
+++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_custom_block.yml
@@ -14,7 +14,7 @@ process:
     source: format
   'body.value': body
 destination:
-  plugin: entity:custom_block
+  plugin: entity:block_content
 migration_dependencies:
   required:
     - d6_filter_format
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php b/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php
index 3f205b2..663b9e6 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php
@@ -32,14 +32,14 @@ class BlockPluginId extends ProcessPluginBase implements ContainerFactoryPluginI
   /**
    * @var \Drupal\Core\Entity\EntityStorageInterface
    */
-  protected $customBlockStorage;
+  protected $blockContentStorage;
 
   /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, MigratePluginManager $process_plugin_manager) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->customBlockStorage = $storage;
+    $this->blockContentStorage = $storage;
     $this->migration = $migration;
     $this->processPluginManager = $process_plugin_manager;
   }
@@ -54,7 +54,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_id,
       $plugin_definition,
       $migration,
-      $entity_manager->getDefinition('custom_block') ? $entity_manager->getStorage('custom_block') : NULL,
+      $entity_manager->getDefinition('block_content') ? $entity_manager->getStorage('block_content') : NULL,
       $container->get('plugin.manager.migrate.process')
     );
   }
@@ -80,11 +80,11 @@ public function transform($value, MigrateExecutable $migrate_executable, Row $ro
           $value = "system_menu_block:$delta";
           break;
         case 'block':
-          if ($this->customBlockStorage) {
+          if ($this->blockContentStorage) {
             $block_ids = $this->processPluginManager
               ->createInstance('migration', array('migration' => 'd6_custom_block'), $this->migration)
               ->transform($delta, $migrate_executable, $row, $destination_property);
-            $value = 'custom_block:' . $this->customBlockStorage->load($block_ids[0])->uuid();
+            $value = 'block_content:' . $this->blockContentStorage->load($block_ids[0])->uuid();
           }
           else {
             throw new MigrateSkipRowException();
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php
new file mode 100644
index 0000000..4c78605
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate_drupal\Tests\d6\MigrateBlockContentTest.
+ */
+
+namespace Drupal\migrate_drupal\Tests\d6;
+
+use Drupal\Core\Language\Language;
+use Drupal\block_content\Entity\BlockContent;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+
+/**
+ * Tests the Drupal 6 custom block to Drupal 8 migration.
+ */
+class MigrateBlockContentTest extends MigrateDrupalTestBase {
+
+  static $modules = array('block', 'block_content');
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name'  => 'Migrate custom blocks.',
+      'description'  => 'Upgrade custom blocks.',
+      'group' => 'Migrate Drupal',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->prepareIdMappings(array(
+      'd6_filter_format' => array(
+        array(array(2), array('full_html'))
+      )
+    ));
+    /** @var \Drupal\migrate\entity\Migration $migration */
+    $migration = entity_load('migration', 'd6_custom_block');
+    $dumps = array(
+      $this->getDumpDirectory() . '/Drupal6Box.php',
+    );
+    $this->prepare($migration, $dumps);
+    $executable = new MigrateExecutable($migration, $this);
+    $executable->import();
+  }
+
+  /**
+   * Tests the Drupal 6 custom block to Drupal 8 migration.
+   */
+  public function testBlockMigration() {
+    /** @var BlockContent $block */
+    $block = entity_load('block_content', 1);
+    $this->assertEqual('My block 1', $block->label());
+    $this->assertEqual(1, $block->getRevisionId());
+    $this->assertTrue(REQUEST_TIME <= $block->getChangedTime() && $block->getChangedTime() <= time());
+    $this->assertEqual(Language::LANGCODE_NOT_SPECIFIED, $block->language()->id);
+    $this->assertEqual('<h3>My first custom block body</h3>', $block->body->value);
+    $this->assertEqual('full_html', $block->body->format);
+
+    $block = entity_load('block_content', 2);
+    $this->assertEqual('My block 2', $block->label());
+    $this->assertEqual(2, $block->getRevisionId());
+    $this->assertTrue(REQUEST_TIME <= $block->getChangedTime() && $block->getChangedTime() <= time());
+    $this->assertEqual(Language::LANGCODE_NOT_SPECIFIED, $block->language()->id);
+    $this->assertEqual('<h3>My second custom block body</h3>', $block->body->value);
+    $this->assertEqual('full_html', $block->body->format);
+  }
+
+}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
index bb79dc6..800e784 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
@@ -25,7 +25,7 @@ class MigrateBlockTest extends MigrateDrupalTestBase {
     'views',
     'comment',
     'menu_ui',
-    'custom_block',
+    'block_content',
     'node',
   );
 
@@ -48,8 +48,8 @@ public function setUp() {
     $entities = array(
       entity_create('menu', array('id' => 'primary-links')),
       entity_create('menu', array('id' => 'secondary-links')),
-      entity_create('custom_block', array('id' => 1, 'type' => 'basic', 'info' => $this->randomName(8))),
-      entity_create('custom_block', array('id' => 2, 'type' => 'basic', 'info' => $this->randomName(8))),
+      entity_create('block_content', array('id' => 1, 'type' => 'basic', 'info' => $this->randomName(8))),
+      entity_create('block_content', array('id' => 2, 'type' => 'basic', 'info' => $this->randomName(8))),
     );
     foreach ($entities as $entity) {
       $entity->enforceIsNew(TRUE);
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateCustomBlockTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateCustomBlockTest.php
deleted file mode 100644
index d48acaf..0000000
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateCustomBlockTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateCustomBlockTest.
- */
-
-namespace Drupal\migrate_drupal\Tests\d6;
-
-use Drupal\Core\Language\Language;
-use Drupal\custom_block\Entity\CustomBlock;
-use Drupal\migrate\MigrateExecutable;
-use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
-
-/**
- * Tests the Drupal 6 custom block to Drupal 8 migration.
- */
-class MigrateCustomBlockTest extends MigrateDrupalTestBase {
-
-  static $modules = array('block', 'custom_block');
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getInfo() {
-    return array(
-      'name'  => 'Migrate custom blocks.',
-      'description'  => 'Upgrade custom blocks.',
-      'group' => 'Migrate Drupal',
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setUp() {
-    parent::setUp();
-    $this->prepareIdMappings(array(
-      'd6_filter_format' => array(
-        array(array(2), array('full_html'))
-      )
-    ));
-    /** @var \Drupal\migrate\entity\Migration $migration */
-    $migration = entity_load('migration', 'd6_custom_block');
-    $dumps = array(
-      $this->getDumpDirectory() . '/Drupal6Box.php',
-    );
-    $this->prepare($migration, $dumps);
-    $executable = new MigrateExecutable($migration, $this);
-    $executable->import();
-  }
-
-  /**
-   * Tests the Drupal 6 custom block to Drupal 8 migration.
-   */
-  public function testBlockMigration() {
-    /** @var CustomBlock $block */
-    $block = entity_load('custom_block', 1);
-    $this->assertEqual('My block 1', $block->label());
-    $this->assertEqual(1, $block->getRevisionId());
-    $this->assertTrue(REQUEST_TIME <= $block->getChangedTime() && $block->getChangedTime() <= time());
-    $this->assertEqual(Language::LANGCODE_NOT_SPECIFIED, $block->language()->id);
-    $this->assertEqual('<h3>My first custom block body</h3>', $block->body->value);
-    $this->assertEqual('full_html', $block->body->format);
-
-    $block = entity_load('custom_block', 2);
-    $this->assertEqual('My block 2', $block->label());
-    $this->assertEqual(2, $block->getRevisionId());
-    $this->assertTrue(REQUEST_TIME <= $block->getChangedTime() && $block->getChangedTime() <= time());
-    $this->assertEqual(Language::LANGCODE_NOT_SPECIFIED, $block->language()->id);
-    $this->assertEqual('<h3>My second custom block body</h3>', $block->body->value);
-    $this->assertEqual('full_html', $block->body->format);
-  }
-
-}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6Test.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6Test.php
index adcf35b..79f11da 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6Test.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6Test.php
@@ -26,7 +26,7 @@ class MigrateDrupal6Test extends MigrateFullDrupalTestBase {
     'book',
     'comment',
     'contact',
-    'custom_block',
+    'block_content',
     'datetime',
     'dblog',
     'file',
@@ -235,7 +235,7 @@ protected function getTestClassesList() {
       __NAMESPACE__ . '\MigrateCommentVariableInstance',
       __NAMESPACE__ . '\MigrateContactCategoryTest',
       __NAMESPACE__ . '\MigrateContactConfigsTest',
-      __NAMESPACE__ . '\MigrateCustomBlockTest',
+      __NAMESPACE__ . '\MigrateBlockContentTest',
       __NAMESPACE__ . '\MigrateDateFormatTest',
       __NAMESPACE__ . '\MigrateDblogConfigsTest',
       __NAMESPACE__ . '\MigrateFieldConfigsTest',
diff --git a/core/modules/quickedit/js/quickedit.js b/core/modules/quickedit/js/quickedit.js
index 9244694..98a7e27 100644
--- a/core/modules/quickedit/js/quickedit.js
+++ b/core/modules/quickedit/js/quickedit.js
@@ -434,7 +434,7 @@
    * @param Object contextualLink
    *   An object with the following properties:
    *     - String entityID: a Quick Edit entity identifier, e.g. "node/1" or
-   *       "custom_block/5".
+   *       "block_content/5".
    *     - String entityInstanceID: a Quick Edit entity instance identifier,
    *       e.g. 0, 1 or n (depending on whether it's the first, second, or n+1st
    *       instance of this entity).
diff --git a/core/profiles/standard/standard.info.yml b/core/profiles/standard/standard.info.yml
index 3f8116b..7029739 100644
--- a/core/profiles/standard/standard.info.yml
+++ b/core/profiles/standard/standard.info.yml
@@ -15,7 +15,7 @@ dependencies:
   - contextual
   - contact
   - datetime
-  - custom_block
+  - block_content
   - quickedit
   - editor
   - entity_reference
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index c3e7a32..34c165d 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -136,11 +136,11 @@ function seven_node_add_list($variables) {
 }
 
 /**
- * Overrides theme_custom_block_add_list().
+ * Overrides theme_block_content_add_list().
  *
  * Displays the list of available custom block types for creation.
  */
-function seven_custom_block_add_list($variables) {
+function seven_block_content_add_list($variables) {
   $output = '';
   if (!empty($variables['types'])) {
     $output = '<ul class="admin-list">';
@@ -150,7 +150,7 @@ function seven_custom_block_add_list($variables) {
       $content .= '<div class="description">' . Xss::filterAdmin($type['description']) . '</div>';
       $options = $type['localized_options'];
       $options['html'] = TRUE;
-      $output .= \Drupal::l($content, 'custom_block.add_form', array('custom_block_type' => $id), $options);
+      $output .= \Drupal::l($content, 'block_content.add_form', array('block_content_type' => $id), $options);
       $output .= '</li>';
     }
     $output .= '</ul>';
