diff --git a/2831274_61.patch b/2831274_61.patch
new file mode 100644
index 0000000000000000000000000000000000000000..16ffcbdb8268d337a88db656ac7367457b6ff1ad
GIT binary patch
literal 173557
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

literal 0
HcmV?d00001

diff --git a/core/composer.json b/core/composer.json
index 99b5cd016d045664da2acb0d5f568bd78ef42ed3..e11676697cb5e7abb7b4597bec4939ff5e451aaf 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -109,6 +109,7 @@
         "drupal/link": "self.version",
         "drupal/locale": "self.version",
         "drupal/minimal": "self.version",
+        "drupal/media_entity": "self.version",
         "drupal/menu_link_content": "self.version",
         "drupal/menu_ui": "self.version",
         "drupal/migrate": "self.version",
diff --git a/core/composer.json b/core/composer.json.orig
similarity index 100%
copy from core/composer.json
copy to core/composer.json.orig
diff --git a/core/modules/media_entity/config/install/media_entity.settings.yml b/core/modules/media_entity/config/install/media_entity.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..85f1c811d37abb3e309d9bb1dafc68bfe81a3d9b
--- /dev/null
+++ b/core/modules/media_entity/config/install/media_entity.settings.yml
@@ -0,0 +1 @@
+icon_base: 'public://media-icons/generic'
diff --git a/core/modules/media_entity/config/install/system.action.media_delete_action.yml b/core/modules/media_entity/config/install/system.action.media_delete_action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c68b0a59f27692ba197820105dc7917b860674e1
--- /dev/null
+++ b/core/modules/media_entity/config/install/system.action.media_delete_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+id: media_delete_action
+label: 'Delete media'
+type: media
+plugin: media_delete_action
+configuration: {  }
diff --git a/core/modules/media_entity/config/install/system.action.media_publish_action.yml b/core/modules/media_entity/config/install/system.action.media_publish_action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a5b4959bb095a8f56b74a3e8205bd744d02a8e73
--- /dev/null
+++ b/core/modules/media_entity/config/install/system.action.media_publish_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+id: media_publish_action
+label: 'Publish media'
+type: media
+plugin: media_publish_action
+configuration: {  }
diff --git a/core/modules/media_entity/config/install/system.action.media_save_action.yml b/core/modules/media_entity/config/install/system.action.media_save_action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fecb8a27d869f88481f48218f70452ccf0680f09
--- /dev/null
+++ b/core/modules/media_entity/config/install/system.action.media_save_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+id: media_save_action
+label: 'Save media'
+type: media
+plugin: media_save_action
+configuration: {  }
diff --git a/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml b/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1e87b113c9d190448da0635d95f377acd27ec9ec
--- /dev/null
+++ b/core/modules/media_entity/config/install/system.action.media_unpublish_action.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+id: media_unpublish_action
+label: 'Unpublish media'
+type: media
+plugin: media_unpublish_action
+configuration: {  }
diff --git a/core/modules/media_entity/config/schema/media_entity.schema.yml b/core/modules/media_entity/config/schema/media_entity.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3322fe3daef96efda1edfe930715e7fe787aec7f
--- /dev/null
+++ b/core/modules/media_entity/config/schema/media_entity.schema.yml
@@ -0,0 +1,72 @@
+media_entity.settings:
+  type: config_object
+  label: 'Media entity settings'
+  mapping:
+    icon_base:
+      type: string
+      label: 'URI where media type icons will be installed'
+
+media_entity.bundle.*:
+  type: config_entity
+  label: 'Media bundle'
+  mapping:
+    id:
+      type: string
+      label: 'Machine name'
+    label:
+      type: label
+      label: 'Label'
+    description:
+      type: text
+      label: 'Description'
+    type:
+      type: string
+      label: 'Type plugin ID'
+    type_configuration:
+      type: media_entity.bundle.type.[%parent.type]
+    queue_thumbnail_downloads:
+      type: boolean
+      label: 'Queue thumbnail downloads'
+    new_revision:
+      type: boolean
+      label: 'Whether a new revision should be created by default'
+    field_map:
+      type: sequence
+      label: 'Field map'
+      sequence:
+        type: string
+
+media_entity.bundle.type.document:
+  type: mapping
+  label: 'Document type configuration'
+  mapping:
+    source_field:
+      type: string
+      label: 'Field with source information'
+
+action.configuration.media_delete_action:
+  type: action_configuration_default
+  label: 'Delete media configuration'
+
+action.configuration.media_save_action:
+  type: action_configuration_default
+  label: 'Save media configuration'
+
+action.configuration.media_publish_action:
+  type: action_configuration_default
+  label: 'Publish media configuration'
+
+action.configuration.media_unpublish_action:
+  type: action_configuration_default
+  label: 'Unpublish media configuration'
+
+field.formatter.settings.media_thumbnail:
+  type: mapping
+  label: 'Media thumbnail field display format settings'
+  mapping:
+    image_link:
+      type: string
+      label: 'Link image to'
+    image_style:
+      type: string
+      label: 'Image style'
diff --git a/core/modules/media_entity/config/schema/media_entity.views.schema.yml b/core/modules/media_entity/config/schema/media_entity.views.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..61062490c81b35c0de3bd399f0fc5df9bd961ac7
--- /dev/null
+++ b/core/modules/media_entity/config/schema/media_entity.views.schema.yml
@@ -0,0 +1,5 @@
+# Schema for the views plugins of the Media Entity module.
+
+views.field.media_bulk_form:
+  type: views_field_bulk_form
+  label: 'Media Entity bulk form'
diff --git a/core/modules/media_entity/images/icons/generic.png b/core/modules/media_entity/images/icons/generic.png
new file mode 100644
index 0000000000000000000000000000000000000000..46125e7f6212958869bb83934c28d871929277bd
--- /dev/null
+++ b/core/modules/media_entity/images/icons/generic.png
@@ -0,0 +1,8 @@
+PNG
+
+   IHDR         e   tEXtSoftware Adobe ImageReadyqe<  IDATxK*[Q#ʄ2(8T(.*?>񦏋"$>H)iY(DxvGg-"d7gt_ a%           @@@@@@@@@@           @@@@@@@@@@@    3Ѥ;%,rJrS7==-ubB!LܔEAZ???i;gqJ%Bv}nn΂f|dttTlQp:V5Mz-ԗrIqǣB)ioou#!̂f1111<<ޕxE9ONN~uuu"XX]qᇆǍBL(d2L+uc%	qŸe}[[5wds)bwoYww(^v;r4e?nP0%S3sM*GP<55ՅMMMKKK9ԏ
+pp*ahDĴbfqݲ
+VѺB
+{	9dy׸׮ԽiTI4 G!<qoo"s
+?A2ǗsF*jyzucc#T3M9q𸼼M2D?*oD A@@@@@@@P;JԗC4Hnrsl,{~~fo59$C\TD+ʩ<\__777qf>/uGrXKX*J^C:QX*XHqn(et	rR9jntUΕRF"Uk"reJm---Rş8yiHˋQS+~xd!n,lpe3   H뉨#l6i&5%CJ<y?>>nnn!kr4$HDѮ.395H6tH)~E9jMR5吣H>r r r r rșO@9Ltu8^X7rK\A#%L+_V	j|ǳ9~&NgYHR\NC9ޞDoiJ&ڒ+5쫫tl|!lu=a$Y>xwvv<|M0>H       @@@@@@@@@@           @@@@@@@@@@@        5O 
+a?    IENDB`
\ No newline at end of file
diff --git a/core/modules/media_entity/js/media_bundle_form.js b/core/modules/media_entity/js/media_bundle_form.js
new file mode 100644
index 0000000000000000000000000000000000000000..7db10c55344183b262a23fc67a540c7add16a948
--- /dev/null
+++ b/core/modules/media_entity/js/media_bundle_form.js
@@ -0,0 +1,45 @@
+/**
+ * @file
+ * Javascript for the media bundle form.
+ */
+
+(function ($, Drupal) {
+  'use strict';
+
+  /**
+   * Behaviors for setting summaries on media bundle form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behaviors on media bundle edit forms.
+   */
+  Drupal.behaviors.mediaBundles = {
+    attach: function (context) {
+      var $context = $(context);
+      // Provide the vertical tab summaries.
+      $context.find('#edit-workflow').drupalSetSummary(function (context) {
+        var vals = [];
+        $(context).find('input[name^="options"]:checked').parent().each(function () {
+          vals.push(Drupal.checkPlain($(this).find('label').text()));
+        });
+        if (!$(context).find('#edit-options-status').is(':checked')) {
+          vals.unshift(Drupal.t('Not published'));
+        }
+        return vals.join(', ');
+      });
+      $(context).find('#edit-language').drupalSetSummary(function (context) {
+        var vals = [];
+
+        vals.push($(context).find('.js-form-item-language-configuration-langcode select option:selected').text());
+
+        $(context).find('input:checked').next('label').each(function () {
+          vals.push(Drupal.checkPlain($(this).text()));
+        });
+
+        return vals.join(', ');
+      });
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/modules/media_entity/js/media_form.js b/core/modules/media_entity/js/media_form.js
new file mode 100644
index 0000000000000000000000000000000000000000..e8c95476b71d95136d6b9642b3878ec5b3743f80
--- /dev/null
+++ b/core/modules/media_entity/js/media_form.js
@@ -0,0 +1,55 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the media entity form.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  'use strict';
+
+  /**
+   * Behaviors for tabs in the media edit form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behavior for tabs in the media edit form.
+   */
+  Drupal.behaviors.mediaDetailsSummaries = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.media-form-revision-information').drupalSetSummary(function (context) {
+        var $revisionContext = $(context);
+        var revisionCheckbox = $revisionContext.find('.js-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 && $revisionContext.find('.js-form-item-revision-log textarea').length)) {
+          return Drupal.t('New revision');
+        }
+
+        return Drupal.t('No revision');
+      });
+
+      $context.find('.media-form-author').drupalSetSummary(function (context) {
+        var $authorContext = $(context);
+        var name = $authorContext.find('.field--name-uid input').val();
+        var date = $authorContext.find('.field--name-created input').val();
+
+        if (name && date) {
+          return Drupal.t('By @name on @date', {'@name': name, '@date': date});
+        }
+        else if (name) {
+          return Drupal.t('By @name', {'@name': name});
+        }
+        else if (date) {
+          return Drupal.t('Authored on @date', {'@date': date});
+        }
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/media_entity/media_entity.api.php b/core/modules/media_entity/media_entity.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..040d47cef294b85829494376845a865957db3440
--- /dev/null
+++ b/core/modules/media_entity/media_entity.api.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Hooks related to media entity and it's plugins.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter the information provided in \Drupal\media_entity\Annotation\MediaType.
+ *
+ * @param array $types
+ *   The array of type plugins, keyed on the machine-readable name.
+ */
+function hook_media_entity_type_info_alter(array &$types) {
+  $types['youtube']['label'] = t('Youtube rocks!');
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/core/modules/media_entity/media_entity.info.yml b/core/modules/media_entity/media_entity.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fb21c0094f61d7d565866e1db1b3d87959a783a4
--- /dev/null
+++ b/core/modules/media_entity/media_entity.info.yml
@@ -0,0 +1,10 @@
+name: Media entity
+description: 'Media entity API.'
+type: module
+package: Core
+version: VERSION
+core: 8.x
+dependencies:
+  - drupal:image
+  - drupal:user
+  - drupal:system
diff --git a/core/modules/media_entity/media_entity.install b/core/modules/media_entity/media_entity.install
new file mode 100644
index 0000000000000000000000000000000000000000..63422ed4e100c652e19f14403a2e2f6fe0832676
--- /dev/null
+++ b/core/modules/media_entity/media_entity.install
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Install, uninstall and update hooks for Media entity module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function media_entity_install() {
+  $source = drupal_get_path('module', 'media_entity') . '/images/icons';
+  $destination = \Drupal::config('media_entity.settings')->get('icon_base');
+  media_entity_copy_icons($source, $destination);
+}
+
+/**
+ * Implements hook_update_last_removed().
+ *
+ * Media entity module lived in contrib before being adopted by core. 8003 was
+ * last schema version before that happened and this should make sure existing
+ * sites update to last contrib before using core version of the module.
+ */
+function media_entity_update_last_removed() {
+  return 8003;
+}
diff --git a/core/modules/media_entity/media_entity.libraries.yml b/core/modules/media_entity/media_entity.libraries.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cd280bc8d55fb08316492fd727881f49a23f88a8
--- /dev/null
+++ b/core/modules/media_entity/media_entity.libraries.yml
@@ -0,0 +1,13 @@
+media_form:
+  version: VERSION
+  js:
+    'js/media_form.js': {}
+  dependencies:
+    - core/drupal.form
+
+media_bundle_form:
+  version: VERSION
+  js:
+    'js/media_bundle_form.js': {}
+  dependencies:
+    - core/drupal.form
diff --git a/core/modules/media_entity/media_entity.links.action.yml b/core/modules/media_entity/media_entity.links.action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cb5492ca960ce9b3a6daa49ad7d5721865d8a2ac
--- /dev/null
+++ b/core/modules/media_entity/media_entity.links.action.yml
@@ -0,0 +1,11 @@
+media.bundle_add:
+  route_name: entity.media_bundle.add_form
+  title: 'Add media bundle'
+  appears_on:
+    - entity.media_bundle.collection
+
+media.add:
+  route_name: entity.media.add_page
+  title: 'Add media'
+  weight: 10
+
diff --git a/core/modules/media_entity/media_entity.links.contextual.yml b/core/modules/media_entity/media_entity.links.contextual.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cce5f83d5f91ed1d14642cea01d9bfb1ef0a0680
--- /dev/null
+++ b/core/modules/media_entity/media_entity.links.contextual.yml
@@ -0,0 +1,9 @@
+entity.media.edit_form:
+  route_name: entity.media.edit_form
+  group: media
+  title: Edit
+entity.media.delete_form:
+  route_name: entity.media.delete_form
+  group: media
+  title: Delete
+  weight: 10
diff --git a/core/modules/media_entity/media_entity.links.menu.yml b/core/modules/media_entity/media_entity.links.menu.yml
new file mode 100644
index 0000000000000000000000000000000000000000..713c668b908a3f71f62f75d9e5bedd230ae11796
--- /dev/null
+++ b/core/modules/media_entity/media_entity.links.menu.yml
@@ -0,0 +1,11 @@
+entity.media_bundle.collection:
+  title: 'Media bundles'
+  parent: system.admin_structure
+  description: 'Manage media bundles.'
+  route_name: entity.media_bundle.collection
+
+entity.media.add:
+  title: 'Add a new media'
+  parent: entity.media.collection
+  description: 'Add a new media entity.'
+  route_name: entity.media.add_page
diff --git a/core/modules/media_entity/media_entity.links.task.yml b/core/modules/media_entity/media_entity.links.task.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c04484799390a0faea667d14ff2b844bc3f6fee2
--- /dev/null
+++ b/core/modules/media_entity/media_entity.links.task.yml
@@ -0,0 +1,21 @@
+entity.media.canonical:
+  route_name: entity.media.canonical
+  base_route: entity.media.canonical
+  title: 'View'
+entity.media.edit_form:
+  route_name: entity.media.edit_form
+  base_route: entity.media.canonical
+  title: Edit
+entity.media.delete_form:
+  route_name: entity.media.delete_form
+  base_route: entity.media.canonical
+  title: Delete
+  weight: 10
+entity.media_bundle.edit_form:
+  title: 'Edit'
+  route_name: entity.media_bundle.edit_form
+  base_route: entity.media_bundle.edit_form
+entity.media_bundle.collection:
+  title: List
+  route_name: entity.media_bundle.collection
+  base_route: entity.media_bundle.collection
diff --git a/core/modules/media_entity/media_entity.module b/core/modules/media_entity/media_entity.module
new file mode 100644
index 0000000000000000000000000000000000000000..56c649333e1375248e32a745ee8e22c95ca0de78
--- /dev/null
+++ b/core/modules/media_entity/media_entity.module
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Provides media entities.
+ */
+
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Implements hook_help().
+ */
+function media_entity_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    case 'help.page.media_entity':
+      $output = '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The <a href=":media_entity_url">Media entity</a> module provides a "base" entity for media. This is a very basic entity which can reference to all kinds of media-objects (local files, YouTube videos, Tweets, Instagram photos, ...). Media entity provides a relation between your website and the media resource. You can reference to/use this entity within any other entity on your site. For more information, see the <a href=":media_entity_handbook">online documentation for the Media entity module</a>.',
+          [
+            ':media_entity_url' => 'https://www.drupal.org/project/media_entity',
+            ':media_entity_handbook' => 'https://drupal-media.gitbooks.io/drupal8-guide/content/modules/media_entity/intro.html',
+          ]) . '</p>';
+      $output .= '<h3>' . t('Uses') . '</h3>';
+      $output .= '<p>' . t('For detailed information about the usage of this module please refer to <a href=":media_entity_handbook">the official documentation</a>.',
+          [
+            ':media_entity_handbook' => 'https://drupal-media.gitbooks.io/drupal8-guide/content/modules/media_entity/intro.html',
+          ]) . '</p>';
+
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function media_entity_theme() {
+  return [
+    'media' => [
+      'render element' => 'elements',
+      'file' => 'media_entity.theme.inc',
+      'template' => 'media',
+    ],
+  ];
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function media_entity_theme_suggestions_media(array $variables) {
+  $suggestions = [];
+  $media = $variables['elements']['#media'];
+  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
+
+  $suggestions[] = 'media__' . $sanitized_view_mode;
+  $suggestions[] = 'media__' . $media->bundle();
+  $suggestions[] = 'media__' . $media->bundle() . '__' . $sanitized_view_mode;
+
+  return $suggestions;
+}
+
+/**
+ * Copy the media file icons to files directory for use with image styles.
+ *
+ * @param string $source
+ *   Source folder.
+ * @param string $destination
+ *   Destination folder.
+ *
+ * @throws Exception
+ *   Thrown when media icons can't be copied to their destination.
+ */
+function media_entity_copy_icons($source, $destination) {
+  if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
+    throw new Exception("Unable to create directory $destination.");
+  }
+
+  $files = file_scan_directory($source, '/.*\.(png|jpg)$/');
+  foreach ($files as $file) {
+    $result = file_unmanaged_copy($file->uri, $destination, FILE_EXISTS_REPLACE);
+    if (!$result) {
+      throw new Exception("Unable to copy {$file->uri} to $destination.");
+    }
+  }
+}
diff --git a/core/modules/media_entity/media_entity.permissions.yml b/core/modules/media_entity/media_entity.permissions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..072259e1d4e22611d0cc8155d5f01fe2b119062d
--- /dev/null
+++ b/core/modules/media_entity/media_entity.permissions.yml
@@ -0,0 +1,18 @@
+administer media:
+  title: 'Administer media'
+  restrict access: TRUE
+administer media bundles:
+  title: 'Administer media bundles'
+  restrict access: TRUE
+view media:
+  title: 'View media'
+update media:
+  title: 'Update own media'
+update any media:
+  title: 'Update any media'
+delete media:
+  title:  'Delete own media'
+delete any media:
+  title:  'Delete any media'
+create media:
+  title: 'Create media'
diff --git a/core/modules/media_entity/media_entity.routing.yml b/core/modules/media_entity/media_entity.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..47771860cea57626da4117ace5eb6cceb0db40f8
--- /dev/null
+++ b/core/modules/media_entity/media_entity.routing.yml
@@ -0,0 +1,6 @@
+entity.media.multiple_delete_confirm:
+  path: '/admin/content/media/delete'
+  defaults:
+    _form: '\Drupal\media_entity\Form\DeleteMultiple'
+  requirements:
+    _permission: 'delete any media'
diff --git a/core/modules/media_entity/media_entity.services.yml b/core/modules/media_entity/media_entity.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a227b7378ccbe38ca7f3d4fe432cb1a300985862
--- /dev/null
+++ b/core/modules/media_entity/media_entity.services.yml
@@ -0,0 +1,4 @@
+services:
+  plugin.manager.media_entity.type:
+    class: Drupal\media_entity\MediaTypeManager
+    parent: default_plugin_manager
diff --git a/core/modules/media_entity/media_entity.theme.inc b/core/modules/media_entity/media_entity.theme.inc
new file mode 100644
index 0000000000000000000000000000000000000000..1efeee6aaccf57154ecf3b3d2ead8fb5b7b8476a
--- /dev/null
+++ b/core/modules/media_entity/media_entity.theme.inc
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Theme functions for the media_entity module.
+ */
+
+use Drupal\Core\Render\Element;
+use Drupal\Core\Link;
+use Drupal\Component\Utility\Html;
+
+/**
+ * Prepares variables for media templates.
+ *
+ * Default template: media.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - media: An individual media for display.
+ */
+function template_preprocess_media(array &$variables) {
+  $variables['media'] = $variables['elements']['#media'];
+
+  $variables['view_mode'] = $variables['elements']['#view_mode'];
+  $variables['name'] = $variables['media']->label();
+
+  // Helpful $content variable for templates.
+  foreach (Element::children($variables['elements']) as $key) {
+    $variables['content'][$key] = $variables['elements'][$key];
+  }
+
+  $variables['attributes']['class'][] = 'media';
+  $variables['attributes']['class'][] = Html::getClass('media-' . $variables['media']->bundle());
+  $variables['attributes']['class'][] = Html::getClass('view-mode-' . $variables['elements']['#view_mode']);
+  if (!$variables['media']->isPublished()) {
+    $variables['attributes']['class'][] = 'unpublished';
+  }
+}
diff --git a/core/modules/media_entity/media_entity.tokens.inc b/core/modules/media_entity/media_entity.tokens.inc
new file mode 100644
index 0000000000000000000000000000000000000000..00559254ac7a408c41f88b942e81fe212bb29581
--- /dev/null
+++ b/core/modules/media_entity/media_entity.tokens.inc
@@ -0,0 +1,180 @@
+<?php
+
+/**
+ * @file
+ * Builds placeholder replacement tokens for media_entity-related data.
+ */
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Datetime\Entity\DateFormat;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\media_entity\Entity\MediaBundle;
+
+/**
+ * Implements hook_token_info().
+ */
+function media_entity_token_info() {
+  $type = [
+    'name' => t('Media'),
+    'description' => t('Tokens related to individual media items.'),
+    'needs-data' => 'media',
+  ];
+
+  // Core tokens for media.
+  $media['mid'] = [
+    'name' => t('Media ID'),
+    'description' => t('The unique ID of the media item.'),
+  ];
+  $media['uuid'] = [
+    'name' => t('Media UUID'),
+    'description' => t('The unique UUID of the media item.'),
+  ];
+  $media['vid'] = [
+    'name' => t('Revision ID'),
+    'description' => t("'The unique ID of the media's latest revision."),
+  ];
+  $media['bundle'] = [
+    'name' => t('Media bundle'),
+  ];
+  $media['bundle-name'] = [
+    'name' => t('Media bundle name'),
+    'description' => t('The human-readable name of the media bundle.'),
+  ];
+  $media['langcode'] = [
+    'name' => t('Language code'),
+    'description' => t('The language code of the language the media is written in.'),
+  ];
+  $media['name'] = [
+    'name' => t('Name'),
+    'description' => t('The name of this media.'),
+  ];
+  $node['author'] = [
+    'name' => t('Author'),
+    'type' => 'user',
+  ];
+  $media['url'] = [
+    'name' => t('URL'),
+    'description' => t('The URL of the media.'),
+  ];
+  $media['edit-url'] = [
+    'name' => t('Edit URL'),
+    'description' => t("The URL of the media's edit page."),
+  ];
+
+  // Chained tokens for media.
+  $media['created'] = [
+    'name' => t('Date created'),
+    'type' => 'date',
+  ];
+  $media['changed'] = [
+    'name' => t('Date changed'),
+    'description' => t('The date the media was most recently updated.'),
+    'type' => 'date',
+  ];
+
+  return [
+    'types' => ['media' => $type],
+    'tokens' => ['media' => $media],
+  ];
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function media_entity_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+  $token_service = \Drupal::token();
+
+  $url_options = ['absolute' => TRUE];
+  if (isset($options['langcode'])) {
+    $url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']);
+    $langcode = $options['langcode'];
+  }
+  else {
+    $langcode = LanguageInterface::LANGCODE_DEFAULT;
+  }
+
+  $replacements = [];
+  if ($type == 'media' && !empty($data['media'])) {
+    /** @var \Drupal\media_entity\MediaInterface $media */
+    $media = \Drupal::service('entity.repository')->getTranslationFromContext($data['media'], $langcode, ['operation' => 'media_entity_tokens']);
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        // Simple key values on the media_entity.
+        case 'mid':
+          $replacements[$original] = $media->id();
+          break;
+
+        case 'uuid':
+          $replacements[$original] = $media->uuid();
+          break;
+
+        case 'vid':
+          $replacements[$original] = $media->getRevisionId();
+          break;
+
+        case 'bundle':
+          $replacements[$original] = $media->bundle();
+          break;
+
+        case 'bundle-name':
+          $replacements[$original] = $media->bundle->entity->label();
+          break;
+
+        case 'langcode':
+          $replacements[$original] = $media->language()->getId();
+          break;
+
+        case 'name':
+          $replacements[$original] = $media->name->value;
+          break;
+
+        case 'url':
+          $replacements[$original] = $media->toUrl('canonical', $url_options);
+          break;
+
+        case 'edit-url':
+          $replacements[$original] = $media->toUrl('edit-form', $url_options);
+          break;
+
+        // Default values for the chained tokens handled below.
+        case 'author':
+          /** @var \Drupal\user\UserInterface $account */
+          $account = $media->getOwner();
+          $bubbleable_metadata->addCacheableDependency($account);
+          $replacements[$original] = $account->label();
+          break;
+
+        case 'created':
+          $date_format = DateFormat::load('medium');
+          $bubbleable_metadata->addCacheableDependency($date_format);
+          $replacements[$original] = \Drupal::service('date.formatter')
+            ->format($media->getCreatedTime(), 'medium', '', NULL, $langcode);
+          break;
+
+        case 'changed':
+          $date_format = DateFormat::load('medium');
+          $bubbleable_metadata->addCacheableDependency($date_format);
+          $replacements[$original] = \Drupal::service('date.formatter')
+            ->format($media->getChangedTime(), 'medium', '', NULL, $langcode);
+          break;
+      }
+    }
+
+    if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
+      $account = $media->get('uid')->entity;
+      $replacements += $token_service->generate('user', $author_tokens, ['user' => $account], $options, $bubbleable_metadata);
+    }
+
+    if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
+      $replacements += $token_service->generate('date', $created_tokens, ['date' => $media->getCreatedTime()], $options, $bubbleable_metadata);
+    }
+
+    if ($changed_tokens = $token_service->findWithPrefix($tokens, 'changed')) {
+      $replacements += $token_service->generate('date', $changed_tokens, ['date' => $media->getChangedTime()], $options, $bubbleable_metadata);
+    }
+  }
+
+  return $replacements;
+}
diff --git a/core/modules/media_entity/src/Annotation/MediaType.php b/core/modules/media_entity/src/Annotation/MediaType.php
new file mode 100644
index 0000000000000000000000000000000000000000..3566c1f7aa9c674534445e31f94f0bb4073379ca
--- /dev/null
+++ b/core/modules/media_entity/src/Annotation/MediaType.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\media_entity\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines an media entity type plugin annotation object.
+ *
+ * @see hook_media_entity_type_info_alter()
+ *
+ * @Annotation
+ */
+class MediaType extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * A brief description of the plugin.
+   *
+   * This will be shown when adding or configuring this display.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description = '';
+
+}
diff --git a/core/modules/media_entity/src/Entity/Media.php b/core/modules/media_entity/src/Entity/Media.php
new file mode 100644
index 0000000000000000000000000000000000000000..42d93c0d77001ed1ac7a6befe848e7858155df30
--- /dev/null
+++ b/core/modules/media_entity/src/Entity/Media.php
@@ -0,0 +1,438 @@
+<?php
+
+namespace Drupal\media_entity\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityPublishedTrait;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\media_entity\MediaInterface;
+use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\user\UserInterface;
+
+/**
+ * Defines the media entity class.
+ *
+ * @ContentEntityType(
+ *   id = "media",
+ *   label = @Translation("Media"),
+ *   label_singular = @Translation("media item"),
+ *   label_plural = @Translation("media items"),
+ *   label_count = @PluralTranslation(
+ *     singular = "@count media item",
+ *     plural = "@count media items"
+ *   ),
+ *   bundle_label = @Translation("Media bundle"),
+ *   handlers = {
+ *     "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
+ *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
+ *     "list_builder" = "Drupal\Core\Entity\EntityListBuilder",
+ *     "access" = "Drupal\media_entity\MediaAccessController",
+ *     "form" = {
+ *       "default" = "Drupal\media_entity\MediaForm",
+ *       "delete" = "Drupal\media_entity\Form\MediaDeleteForm",
+ *       "edit" = "Drupal\media_entity\MediaForm"
+ *     },
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ *     "views_data" = "Drupal\media_entity\MediaViewsData",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
+ *     }
+ *   },
+ *   base_table = "media",
+ *   data_table = "media_field_data",
+ *   revision_table = "media_revision",
+ *   revision_data_table = "media_field_revision",
+ *   translatable = TRUE,
+ *   render_cache = TRUE,
+ *   entity_keys = {
+ *     "id" = "mid",
+ *     "revision" = "vid",
+ *     "bundle" = "bundle",
+ *     "label" = "name",
+ *     "langcode" = "langcode",
+ *     "uuid" = "uuid",
+ *     "published" = "status",
+ *   },
+ *   bundle_entity_type = "media_bundle",
+ *   permission_granularity = "entity_type",
+ *   admin_permission = "administer media",
+ *   field_ui_base_route = "entity.media_bundle.edit_form",
+ *   links = {
+ *     "add-page" = "/media/add",
+ *     "add-form" = "/media/add/{media_bundle}",
+ *     "canonical" = "/media/{media}",
+ *     "delete-form" = "/media/{media}/delete",
+ *     "edit-form" = "/media/{media}/edit",
+ *     "admin-form" = "/admin/structure/media/manage/{media_bundle}"
+ *   }
+ * )
+ */
+class Media extends ContentEntityBase implements MediaInterface {
+
+  use EntityChangedTrait;
+  use EntityPublishedTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCreatedTime() {
+    return $this->get('created')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCreatedTime($timestamp) {
+    $this->set('created', $timestamp);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwner() {
+    return $this->get('uid')->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwner(UserInterface $account) {
+    $this->set('uid', $account->id());
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwnerId() {
+    return $this->get('uid')->target_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwnerId($uid) {
+    $this->set('uid', $uid);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPublisher() {
+    return $this->getOwner();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPublisherId() {
+    return $this->getOwnerId();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setPublisherId($uid) {
+    return $this->setOwnerId($uid);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getType() {
+    return $this->bundle->entity->getType();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSave(EntityStorageInterface $storage) {
+    parent::preSave($storage);
+
+    // Try to set fields provided by type plugin and mapped in bundle
+    // configuration.
+    foreach ($this->bundle->entity->getFieldMap() as $source_field => $destination_field) {
+      // Only save value in entity field if empty. Do not overwrite existing
+      // data.
+      if ($this->hasField($destination_field) && $this->{$destination_field}->isEmpty() && ($value = $this->getType()->getField($this, $source_field))) {
+        $this->set($destination_field, $value);
+      }
+    }
+
+    // Try to set a default name for this media if no label is provided.
+    if (!$this->label()) {
+      $this->set('name', $this->getType()->getDefaultName($this));
+    }
+
+    // Set thumbnail.
+    if (!$this->get('thumbnail')->entity) {
+      $this->automaticallySetThumbnail();
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
+    parent::postSave($storage, $update);
+    if (!$update && $this->bundle->entity->getQueueThumbnailDownloads()) {
+      $queue = \Drupal::queue('media_entity_thumbnail');
+      $queue->createItem(['id' => $this->id()]);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function automaticallySetThumbnail() {
+    /** @var \Drupal\Core\Entity\EntityStorageInterface $file_storage */
+    $file_storage = $this->entityTypeManager()->getStorage('file');
+
+    // If thumbnail fetching should be queued then temporary use default
+    // thumbnail or fetch it immediately otherwise.
+    if ($this->bundle->entity->getQueueThumbnailDownloads() && $this->isNew()) {
+      $thumbnail_uri = $this->getType()->getDefaultThumbnail();
+    }
+    else {
+      $thumbnail_uri = $this->getType()->thumbnail($this);
+    }
+    $existing = $file_storage->getQuery()
+      ->condition('uri', $thumbnail_uri)
+      ->execute();
+
+    if ($existing) {
+      $this->thumbnail->target_id = reset($existing);
+    }
+    else {
+      /** @var \Drupal\file\FileInterface $file */
+      $file = $file_storage->create(['uri' => $thumbnail_uri]);
+      if ($owner = $this->getOwner()) {
+        $file->setOwner($owner);
+      }
+      $file->setPermanent();
+      $file->save();
+      $this->thumbnail->target_id = $file->id();
+    }
+
+    // TODO - We should probably use something smarter (tokens, ...).
+    $this->thumbnail->alt = t('Thumbnail');
+    $this->thumbnail->title = $this->label();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
+    parent::preSaveRevision($storage, $record);
+
+    if (!$this->isNewRevision() && isset($this->original) && (!isset($record->revision_log) || $record->revision_log === '')) {
+      // If we are updating an existing node without adding a new revision, we
+      // need to make sure $entity->revision_log is reset whenever it is empty.
+      // Therefore, this code allows us to avoid clobbering an existing log
+      // entry with an empty one.
+      $record->revision_log = $this->original->revision_log->value;
+    }
+
+    if ($this->isNewRevision()) {
+      $record->revision_timestamp = \Drupal::time()->getRequestTime();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate() {
+    $this->getType()->attachConstraints($this);
+    return parent::validate();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::publishedBaseFieldDefinitions($entity_type);
+
+    $fields['name'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Media name'))
+      ->setDescription(t('The name of this media.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
+      ->setDefaultValue('')
+      ->setSetting('max_length', 255)
+      ->setDisplayOptions('form', [
+        'type' => 'string_textfield',
+        'weight' => -5,
+      ])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayOptions('view', [
+        'label' => 'hidden',
+        'type' => 'string',
+        'weight' => -5,
+      ]);
+
+    $fields['thumbnail'] = BaseFieldDefinition::create('image')
+      ->setLabel(t('Thumbnail'))
+      ->setDescription(t('The thumbnail of the media.'))
+      ->setRevisionable(TRUE)
+      ->setDisplayOptions('view', [
+        'type' => 'image',
+        'weight' => 5,
+        'label' => 'hidden',
+        'settings' => [
+          'image_style' => 'thumbnail',
+        ],
+      ])
+      ->setDisplayConfigurable('view', TRUE)
+      ->setReadOnly(TRUE);
+
+    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
+      ->setLabel(t('Publisher ID'))
+      ->setDescription(t('The user ID of the media publisher.'))
+      ->setRevisionable(TRUE)
+      ->setDefaultValueCallback('Drupal\media_entity\Entity\Media::getCurrentUserId')
+      ->setSetting('target_type', 'user')
+      ->setTranslatable(TRUE)
+      ->setDisplayOptions('form', [
+        'type' => 'entity_reference_autocomplete',
+        'weight' => 5,
+        'settings' => [
+          'match_operator' => 'CONTAINS',
+          'size' => '60',
+          'autocomplete_type' => 'tags',
+          'placeholder' => '',
+        ],
+      ])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayOptions('view', [
+        'label' => 'hidden',
+        'type' => 'author',
+        'weight' => 0,
+      ])
+      ->setDisplayConfigurable('view', TRUE);
+
+    $fields['created'] = BaseFieldDefinition::create('created')
+      ->setLabel(t('Created'))
+      ->setDescription(t('The time that the media was created.'))
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
+      ->setDisplayOptions('form', [
+        'type' => 'datetime_timestamp',
+        'weight' => 10,
+      ])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayOptions('view', [
+        'label' => 'hidden',
+        'type' => 'timestamp',
+        'weight' => 0,
+      ])
+      ->setDisplayConfigurable('view', TRUE);
+
+    $fields['changed'] = BaseFieldDefinition::create('changed')
+      ->setLabel(t('Changed'))
+      ->setDescription(t('The time that the media was last edited.'))
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE);
+
+    $fields['revision_timestamp'] = BaseFieldDefinition::create('created')
+      ->setLabel(t('Revision timestamp'))
+      ->setDescription(t('The time that the current revision was created.'))
+      ->setQueryable(FALSE)
+      ->setRevisionable(TRUE);
+
+    $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
+      ->setLabel(t('Revision publisher ID'))
+      ->setDescription(t('The user ID of the publisher of the current revision.'))
+      ->setDefaultValueCallback('Drupal\media_entity\Entity\Media::getCurrentUserId')
+      ->setSetting('target_type', 'user')
+      ->setQueryable(FALSE)
+      ->setRevisionable(TRUE);
+
+    $fields['revision_log'] = BaseFieldDefinition::create('string_long')
+      ->setLabel(t('Revision Log'))
+      ->setDescription(t('The log entry explaining the changes in this revision.'))
+      ->setRevisionable(TRUE)
+      ->setTranslatable(TRUE);
+
+    return $fields;
+  }
+
+  /**
+   * Default value callback for 'uid' base field definition.
+   *
+   * @see ::baseFieldDefinitions()
+   *
+   * @return array
+   *   An array of default values.
+   */
+  public static function getCurrentUserId() {
+    return [\Drupal::currentUser()->id()];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionCreationTime() {
+    return $this->revision_timestamp->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRevisionCreationTime($timestamp) {
+    $this->revision_timestamp->value = $timestamp;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionUser() {
+    return $this->revision_uid->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRevisionUser(UserInterface $account) {
+    $this->revision_uid->entity = $account;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionUserId() {
+    return $this->revision_uid->target_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRevisionUserId($user_id) {
+    $this->revision_uid->target_id = $user_id;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionLogMessage() {
+    return $this->revision_log->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRevisionLogMessage($revision_log_message) {
+    $this->revision_log->value = $revision_log_message;
+    return $this;
+  }
+
+}
diff --git a/core/modules/media_entity/src/Entity/MediaBundle.php b/core/modules/media_entity/src/Entity/MediaBundle.php
new file mode 100644
index 0000000000000000000000000000000000000000..683ab84f521017e13ab4d3ab245c27a2ebb6b4ac
--- /dev/null
+++ b/core/modules/media_entity/src/Entity/MediaBundle.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace Drupal\media_entity\Entity;
+
+use Drupal\Core\Entity\EntityDescriptionInterface;
+use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
+use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\media_entity\MediaBundleInterface;
+use Drupal\media_entity\MediaInterface;
+
+/**
+ * Defines the Media bundle configuration entity.
+ *
+ * @ConfigEntityType(
+ *   id = "media_bundle",
+ *   label = @Translation("Media bundle"),
+ *   label_singular = @Translation("media bundle item"),
+ *   label_plural = @Translation("media bundle items"),
+ *   label_count = @PluralTranslation(
+ *     singular = "@count media bundle item",
+ *     plural = "@count media bundle items"
+ *   ),
+ *   handlers = {
+ *     "form" = {
+ *       "add" = "Drupal\media_entity\MediaBundleForm",
+ *       "edit" = "Drupal\media_entity\MediaBundleForm",
+ *       "delete" = "Drupal\media_entity\Form\MediaBundleDeleteConfirm"
+ *     },
+ *     "list_builder" = "Drupal\media_entity\MediaBundleListBuilder",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     }
+ *   },
+ *   admin_permission = "administer media bundles",
+ *   config_prefix = "bundle",
+ *   bundle_of = "media",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label"
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "label",
+ *     "description",
+ *     "type",
+ *     "queue_thumbnail_downloads",
+ *     "new_revision",
+ *     "third_party_settings",
+ *     "type_configuration",
+ *     "field_map",
+ *     "status",
+ *   },
+ *   links = {
+ *     "add-form" = "/admin/structure/media/add",
+ *     "edit-form" = "/admin/structure/media/manage/{media_bundle}",
+ *     "delete-form" = "/admin/structure/media/manage/{media_bundle}/delete",
+ *     "collection" = "/admin/structure/media",
+ *   }
+ * )
+ */
+class MediaBundle extends ConfigEntityBundleBase implements MediaBundleInterface, EntityWithPluginCollectionInterface, EntityDescriptionInterface {
+
+  /**
+   * The machine name of this media bundle.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The human-readable name of the media bundle.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * A brief description of this media bundle.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
+   * The type plugin id.
+   *
+   * @var string
+   */
+  protected $type = 'generic';
+
+  /**
+   * Are thumbnail downloads queued.
+   *
+   * @var bool
+   */
+  protected $queue_thumbnail_downloads = FALSE;
+
+  /**
+   * Default value of the 'Create new revision' checkbox of this media bundle.
+   *
+   * @var bool
+   */
+  protected $new_revision = FALSE;
+
+  /**
+   * The type plugin configuration.
+   *
+   * @var array
+   */
+  protected $type_configuration = [];
+
+  /**
+   * Type lazy plugin collection.
+   *
+   * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
+   */
+  protected $typePluginCollection;
+
+  /**
+   * Field map. Fields provided by type plugin to be stored as entity fields.
+   *
+   * @var array
+   */
+  public $field_map = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginCollections() {
+    return [
+      'type_configuration' => $this->typePluginCollection(),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getLabel(MediaInterface $media) {
+    $bundle = static::load($media->bundle());
+    return $bundle ? $bundle->label() : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function exists($id) {
+    return (bool) static::load($id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->description;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDescription($description) {
+    $this->description = $description;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTypeConfiguration() {
+    return $this->type_configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setTypeConfiguration($configuration) {
+    $this->type_configuration = $configuration;
+    $this->typePluginCollection = NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQueueThumbnailDownloads() {
+    return $this->queue_thumbnail_downloads;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setQueueThumbnailDownloads($queue_thumbnail_downloads) {
+    $this->queue_thumbnail_downloads = $queue_thumbnail_downloads;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getType() {
+    return $this->typePluginCollection()->get($this->type);
+  }
+
+  /**
+   * Returns type lazy plugin collection.
+   *
+   * @return \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
+   *   The tag plugin collection.
+   */
+  protected function typePluginCollection() {
+    if (!$this->typePluginCollection) {
+      $this->typePluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.media_entity.type'), $this->type, $this->type_configuration);
+    }
+    return $this->typePluginCollection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStatus() {
+    return $this->status;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function shouldCreateNewRevision() {
+    return $this->new_revision;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setNewRevision($new_revision) {
+    $this->new_revision = $new_revision;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldMap() {
+    return $this->field_map;
+  }
+
+}
diff --git a/core/modules/media_entity/src/Form/DeleteMultiple.php b/core/modules/media_entity/src/Form/DeleteMultiple.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a9cc40ae82e8b4be3b9ad5c005fc18d7cc0efb8
--- /dev/null
+++ b/core/modules/media_entity/src/Form/DeleteMultiple.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace Drupal\media_entity\Form;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Provides a media deletion confirmation form.
+ */
+class DeleteMultiple extends ConfirmFormBase {
+
+  /**
+   * The array of media entities to delete.
+   *
+   * @var string[][]
+   */
+  protected $entityInfo = [];
+
+  /**
+   * The tempstore factory.
+   *
+   * @var \Drupal\user\PrivateTempStoreFactory
+   */
+  protected $tempStoreFactory;
+
+  /**
+   * The entity storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * Constructs a DeleteMultiple form object.
+   *
+   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
+   *   The tempstore factory.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $manager
+   *   The entity manager.
+   */
+  public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $manager) {
+    $this->tempStoreFactory = $temp_store_factory;
+    $this->storage = $manager->getStorage('media');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.private_tempstore'),
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'media_multiple_delete_confirm';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->formatPlural(count($this->entityInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('system.admin_content');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $this->entityInfo = $this->tempStoreFactory->get('media_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
+    if (empty($this->entityInfo)) {
+      return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
+    }
+    /** @var \Drupal\media_entity\MediaInterface[] $entities */
+    $entities = $this->storage->loadMultiple(array_keys($this->entityInfo));
+
+    $items = [];
+    foreach ($this->entityInfo as $id => $langcodes) {
+      foreach ($langcodes as $langcode) {
+        $entity = $entities[$id]->getTranslation($langcode);
+        $key = $id . ':' . $langcode;
+        $default_key = $id . ':' . $entity->getUntranslated()->language()->getId();
+
+        // If we have a translated entity we build a nested list of translations
+        // that will be deleted.
+        $languages = $entity->getTranslationLanguages();
+        if (count($languages) > 1 && $entity->isDefaultTranslation()) {
+          $names = [];
+          foreach ($languages as $translation_langcode => $language) {
+            $names[] = $language->getName();
+            unset($items[$id . ':' . $translation_langcode]);
+          }
+          $items[$default_key] = [
+            'label' => [
+              '#markup' => $this->t('@label (Original translation) - <em>The following media translations will be deleted:</em>', ['@label' => $entity->label()]),
+            ],
+            'deleted_translations' => [
+              '#theme' => 'item_list',
+              '#items' => $names,
+            ],
+          ];
+        }
+        elseif (!isset($items[$default_key])) {
+          $items[$key] = $entity->label();
+        }
+      }
+    }
+
+    $form['entities'] = [
+      '#theme' => 'item_list',
+      '#items' => $items,
+    ];
+    $form = parent::buildForm($form, $form_state);
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->getValue('confirm') && !empty($this->entityInfo)) {
+      $total_count = 0;
+      $delete_entities = [];
+      /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
+      $delete_translations = [];
+      /** @var \Drupal\media_entity\MediaInterface[] $entities */
+      $entities = $this->storage->loadMultiple(array_keys($this->entityInfo));
+
+      foreach ($this->entityInfo as $id => $langcodes) {
+        foreach ($langcodes as $langcode) {
+          $entity = $entities[$id]->getTranslation($langcode);
+          if ($entity->isDefaultTranslation()) {
+            $delete_entities[$id] = $entity;
+            unset($delete_translations[$id]);
+            $total_count += count($entity->getTranslationLanguages());
+          }
+          elseif (!isset($delete_entities[$id])) {
+            $delete_translations[$id][] = $entity;
+          }
+        }
+      }
+
+      if ($delete_entities) {
+        $this->storage->delete($delete_entities);
+        $this->logger('media_entity')->notice('Deleted @count media entities.', ['@count' => count($delete_entities)]);
+      }
+
+      if ($delete_translations) {
+        $count = 0;
+        foreach ($delete_translations as $id => $translations) {
+          $entity = $entities[$id]->getUntranslated();
+          foreach ($translations as $translation) {
+            $entity->removeTranslation($translation->language()->getId());
+          }
+          $entity->save();
+          $count += count($translations);
+        }
+        if ($count) {
+          $total_count += $count;
+          $this->logger('media_entity')->notice('Deleted @count media translations.', ['@count' => $count]);
+        }
+      }
+
+      if ($total_count) {
+        drupal_set_message($this->formatPlural($total_count, 'Deleted 1 media entity.', 'Deleted @count media entities.'));
+      }
+
+      $this->tempStoreFactory->get('media_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
+    }
+
+    $form_state->setRedirect('system.admin_content');
+  }
+
+}
diff --git a/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php b/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php
new file mode 100644
index 0000000000000000000000000000000000000000..c56318f1ea88e1611aec6a1f22a8c5561cd1f7c0
--- /dev/null
+++ b/core/modules/media_entity/src/Form/MediaBundleDeleteConfirm.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\media_entity\Form;
+
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Entity\EntityDeleteForm;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for media bundle deletion.
+ */
+class MediaBundleDeleteConfirm extends EntityDeleteForm {
+
+  /**
+   * The query factory to create entity queries.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $queryFactory;
+
+  /**
+   * Constructs a new MediaBundleDeleteConfirm 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 buildForm(array $form, FormStateInterface $form_state) {
+    $num_entities = $this->queryFactory->get('media')
+      ->condition('bundle', $this->entity->id())
+      ->count()
+      ->execute();
+    if ($num_entities) {
+      $caption = '<p>' . $this->formatPlural($num_entities,
+          '%type is used by @count piece of content on your site. You can not remove this content type until you have removed all of the %type content.',
+          '%type is used by @count pieces of content on your site. You may not remove %type until you have removed all of the %type content.',
+          ['%type' => $this->entity->label()]) . '</p>';
+      $form['#title'] = $this->getQuestion();
+      $form['description'] = ['#markup' => $caption];
+      return $form;
+    }
+
+    return parent::buildForm($form, $form_state);
+  }
+
+}
diff --git a/core/modules/media_entity/src/Form/MediaDeleteForm.php b/core/modules/media_entity/src/Form/MediaDeleteForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..d84e34a17f9fc187ccf89c0a52eb3f4719180564
--- /dev/null
+++ b/core/modules/media_entity/src/Form/MediaDeleteForm.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\media_entity\Form;
+
+use Drupal\Core\Entity\ContentEntityDeleteForm;
+use Drupal\Core\Url;
+
+/**
+ * Provides a form for deleting a media.
+ */
+class MediaDeleteForm extends ContentEntityDeleteForm {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return Url::fromUri('internal:/');
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaAccessController.php b/core/modules/media_entity/src/MediaAccessController.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a0e2148be391a9b58bb89fd8ccf6f1ff4c6fd86
--- /dev/null
+++ b/core/modules/media_entity/src/MediaAccessController.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Defines an access controller for the media entity.
+ */
+class MediaAccessController extends EntityAccessControlHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    if ($account->hasPermission('administer media')) {
+      return AccessResult::allowed()->cachePerPermissions();
+    }
+
+    $is_owner = ($account->id() && $account->id() == $entity->getPublisherId()) ? TRUE : FALSE;
+    switch ($operation) {
+      case 'view':
+        return AccessResult::allowedIf($account->hasPermission('view media') && $entity->status->value)->cachePerPermissions()->addCacheableDependency($entity);
+
+      case 'update':
+        return AccessResult::allowedIf(($account->hasPermission('update media') && $is_owner) || $account->hasPermission('update any media'))->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
+
+      case 'delete':
+        return AccessResult::allowedIf(($account->hasPermission('delete media') && $is_owner) ||  $account->hasPermission('delete any media'))->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
+    }
+
+    // No opinion.
+    return AccessResult::neutral()->cachePerPermissions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    return AccessResult::allowedIfHasPermission($account, 'create media');
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaBundleForm.php b/core/modules/media_entity/src/MediaBundleForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..6ecad543c5145e6248d14533c4545fb0cd516338
--- /dev/null
+++ b/core/modules/media_entity/src/MediaBundleForm.php
@@ -0,0 +1,385 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\language\Entity\ContentLanguageSettings;
+use Drupal\media_entity\Entity\MediaBundle;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for media bundle forms.
+ */
+class MediaBundleForm extends EntityForm {
+
+  /**
+   * The instantiated plugin instances that have configuration forms.
+   *
+   * @var \Drupal\Core\Plugin\PluginFormInterface[]
+   */
+  protected $configurableInstances = [];
+
+  /**
+   * Manager for media entity type plugins.
+   *
+   * @var \Drupal\media_entity\MediaTypeManager
+   */
+  protected $mediaTypeManager;
+
+  /**
+   * Entity field manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * Constructs a new class instance.
+   *
+   * @param \Drupal\media_entity\MediaTypeManager $media_type_manager
+   *   Media type manager.
+   */
+  public function __construct(MediaTypeManager $media_type_manager, EntityFieldManagerInterface $entity_field_manager) {
+    $this->mediaTypeManager = $media_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.media_entity.type'),
+      $container->get('entity_field.manager')
+    );
+  }
+
+  /**
+   * Ajax callback triggered by the type provider select element.
+   *
+   * @param array $form
+   *   The form array.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   Current form state.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   The ajax response.
+   */
+  public function ajaxTypeProviderData(array $form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    $plugin = $this->entity->getType()->getPluginId();
+
+    $response->addCommand(new ReplaceCommand('#edit-type-configuration-plugin-wrapper', $form['type_configuration'][$plugin]));
+    $response->addCommand(new ReplaceCommand('#field-mapping-wrapper', $form['field_mapping']));
+
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
+    $form['#entity'] = $bundle = $this->getEntity();
+    $form_state->set('bundle', $bundle->id());
+
+    if ($this->operation == 'add') {
+      $form['#title'] = $this->t('Add media bundle');
+    }
+    elseif ($this->operation == 'edit') {
+      $form['#title'] = $this->t('Edit %label media bundle', ['%label' => $bundle->label()]);
+    }
+
+    $form['label'] = [
+      '#title' => $this->t('Label'),
+      '#type' => 'textfield',
+      '#default_value' => $bundle->label(),
+      '#description' => $this->t('The human-readable name of this media bundle.'),
+      '#required' => TRUE,
+      '#size' => 30,
+      '#weight' => -100,
+    ];
+
+    // @todo: '#disabled' not always FALSE.
+    $form['id'] = [
+      '#type' => 'machine_name',
+      '#default_value' => $bundle->id(),
+      '#maxlength' => 32,
+      '#disabled' => !$bundle->isNew(),
+      '#machine_name' => [
+        'exists' => [MediaBundle::class, 'exists'],
+        'source' => ['label'],
+      ],
+      '#description' => $this->t('A unique machine-readable name for this media bundle.'),
+      '#weight' => -90,
+    ];
+
+    $form['description'] = [
+      '#title' => $this->t('Description'),
+      '#type' => 'textarea',
+      '#default_value' => $bundle->getDescription(),
+      '#description' => $this->t('Describe this media bundle. The text will be displayed on the <em>Add new media</em> page.'),
+      '#weight' => -80,
+    ];
+
+    $plugins = $this->mediaTypeManager->getDefinitions();
+    $options = [];
+    foreach ($plugins as $plugin => $definition) {
+      $options[$plugin] = $definition['label'];
+    }
+
+    $form['type'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Type provider'),
+      '#default_value' => $bundle->getType()->getPluginId(),
+      '#options' => $options,
+      '#description' => $this->t('Media type provider plugin that is responsible for additional logic related to this media.'),
+      '#weight' => -70,
+      '#ajax' => [
+        'callback' => '::ajaxTypeProviderData',
+        'progress' => [
+          'type' => 'throbber',
+          'message' => $this->t('Updating type provider configuration form.'),
+        ],
+      ],
+    ];
+
+    // Media type plugin configuration.
+    $form['type_configuration'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Type provider configuration'),
+      '#tree' => TRUE,
+      '#weight' => -60,
+    ];
+
+    /** @var \Drupal\media_entity\MediaTypeInterface $plugin */
+    if ($plugin = $bundle->getType()) {
+      $plugin_configuration = (empty($this->configurableInstances[$plugin->getPluginId()]['plugin_config'])) ? $bundle->getTypeConfiguration() : $this->configurableInstances[$plugin->getPluginId()]['plugin_config'];
+      /** @var \Drupal\media_entity\MediaTypeBase $instance */
+      $instance = $this->mediaTypeManager->createInstance($plugin->getPluginId(), $plugin_configuration);
+      // Store the configuration for validate and submit handlers.
+      $this->configurableInstances[$plugin->getPluginId()]['plugin_config'] = $plugin_configuration;
+
+      $form['type_configuration'][$plugin->getPluginId()] = [
+        '#type' => 'container',
+        '#attributes' => [
+          'id' => 'edit-type-configuration-plugin-wrapper',
+        ],
+      ];
+      $form['type_configuration'][$plugin->getPluginId()] += $instance->buildConfigurationForm([], $form_state);
+    }
+
+    // Field mapping configuration.
+    $form['field_mapping'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Field mapping'),
+      '#tree' => TRUE,
+      '#attributes' => ['id' => 'field-mapping-wrapper'],
+      'description' => [
+        '#type' => 'html_tag',
+        '#tag' => 'p',
+        '#value' => $this->t('Media type plugins can provide metadata fields such as title, caption, size information, credits, ... Media entity can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.'),
+      ],
+      '#weight' => -50,
+    ];
+
+    if (empty($plugin) || empty($plugin->providedFields())) {
+      $form['field_mapping']['empty_message'] = [
+        '#prefix' => '<em>',
+        '#suffix' => '</em>',
+        '#markup' => $this->t('No metadata fields available.'),
+      ];
+    }
+    else {
+      $skipped_fields = [
+        'mid',
+        'uuid',
+        'vid',
+        'bundle',
+        'langcode',
+        'default_langcode',
+        'uid',
+        'revision_timestamp',
+        'revision_log',
+        'revision_uid',
+      ];
+      $options = ['_none' => $this->t('- Skip field -')];
+      foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
+        if (!in_array($field_name, $skipped_fields)) {
+          $options[$field_name] = $field->getLabel();
+        }
+      }
+
+      $field_map = $bundle->getFieldMap();
+      foreach ($plugin->providedFields() as $field_name => $field_label) {
+        $form['field_mapping'][$field_name] = [
+          '#type' => 'select',
+          '#title' => $field_label,
+          '#options' => $options,
+          '#default_value' => isset($field_map[$field_name]) ? $field_map[$field_name] : '_none',
+        ];
+      }
+    }
+
+    $form['additional_settings'] = [
+      '#type' => 'vertical_tabs',
+      '#attached' => [
+        'library' => ['media_entity/media_bundle_form'],
+      ],
+      '#weight' => 100,
+    ];
+
+    $form['workflow'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Publishing options'),
+      '#group' => 'additional_settings',
+    ];
+
+    $workflow_options = [
+      'status' => $bundle->getStatus(),
+      'new_revision' => $bundle->shouldCreateNewRevision(),
+      'queue_thumbnail_downloads' => $bundle->getQueueThumbnailDownloads(),
+    ];
+    // Prepare workflow options to be used for 'checkboxes' form element.
+    $keys = array_keys(array_filter($workflow_options));
+    $workflow_options = array_combine($keys, $keys);
+    $form['workflow']['options'] = [
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Default options'),
+      '#default_value' => $workflow_options,
+      '#options' => [
+        'status' => $this->t('Published'),
+        'new_revision' => $this->t('Create new revision'),
+        'queue_thumbnail_downloads' => $this->t('Queue thumbnail downloads'),
+      ],
+    ];
+
+    $form['workflow']['options']['status']['#description'] = $this->t('Entities will be automatically published when they are created.');
+    $form['workflow']['options']['new_revision']['#description'] = $this->t('Automatically create a new revision of media entities. Users with the Administer media permission will be able to override this option.');
+    $form['workflow']['options']['queue_thumbnail_downloads']['#description'] = $this->t('Download thumbnails via a queue.');
+
+    if ($this->moduleHandler->moduleExists('language')) {
+      $form['language'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Language settings'),
+        '#group' => 'additional_settings',
+      ];
+
+      $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('media', $bundle->id());
+
+      $form['language']['language_configuration'] = [
+        '#type' => 'language_configuration',
+        '#entity_information' => [
+          'entity_type' => 'media',
+          'bundle' => $bundle->id(),
+        ],
+        '#default_value' => $language_configuration,
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
+
+    // Let the selected plugin validate its settings.
+    $plugin = $this->entity->getType()->getPluginId();
+    $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : [];
+    $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration);
+    $instance->validateConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm($form, $form_state);
+
+    $this->entity->setQueueThumbnailDownloads((bool) $form_state->getValue(['options', 'queue_thumbnail_downloads']));
+    $this->entity->setStatus((bool) $form_state->getValue(['options', 'status']));
+
+    $this->entity->setNewRevision((bool) $form_state->getValue(['options', 'new_revision']));
+
+    // Let the selected plugin save its settings.
+    $plugin = $this->entity->getType()->getPluginId();
+    $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : [];
+    $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration);
+    $instance->submitConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(array $form, FormStateInterface $form_state) {
+    $actions = parent::actions($form, $form_state);
+    $actions['submit']['#value'] = $this->t('Save media bundle');
+    $actions['delete']['#value'] = $this->t('Delete media bundle');
+    $actions['delete']['#access'] = $this->entity->access('delete');
+    return $actions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
+    $configuration = $form_state->getValue('type_configuration');
+
+    // Store previous plugin config.
+    $plugin = $entity->getType()->getPluginId();
+    $this->configurableInstances[$plugin]['plugin_config'] = empty($configuration[$plugin]) ? [] : $configuration[$plugin];
+
+    /** @var \Drupal\media_entity\MediaBundleInterface $entity */
+    parent::copyFormValuesToEntity($entity, $form, $form_state);
+
+    // Use type configuration for the plugin that was chosen.
+    $plugin = $entity->getType()->getPluginId();
+    $plugin_configuration = empty($configuration[$plugin]) ? [] : $configuration[$plugin];
+    $entity->set('type_configuration', $plugin_configuration);
+
+    // Save field mapping.
+    $entity->set('field_map', array_filter(
+      $form_state->getValue('field_mapping', []),
+      function ($item) { return $item != '_none'; }
+    ));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
+    $bundle = $this->entity;
+    $status = $bundle->save();
+
+    $t_args = ['%name' => $bundle->label()];
+    if ($status == SAVED_UPDATED) {
+      drupal_set_message($this->t('The media bundle %name has been updated.', $t_args));
+    }
+    elseif ($status == SAVED_NEW) {
+      drupal_set_message($this->t('The media bundle %name has been added.', $t_args));
+      $this->logger('media')->notice('Added bundle %name.', $t_args);
+    }
+
+    // Override the "status" base field default value, for this bundle.
+    $fields = $this->entityFieldManager->getFieldDefinitions('media', $bundle->id());
+    $media = $this->entityTypeManager->getStorage('media')->create(array('bundle' => $bundle->id()));
+    $value = (bool) $form_state->getValue(['options', 'status']);
+    if ($media->status->value != $value) {
+      $fields['status']->getConfig($bundle->id())->setDefaultValue($value)->save();
+    }
+
+    $form_state->setRedirectUrl($bundle->toUrl('collection'));
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaBundleInterface.php b/core/modules/media_entity/src/MediaBundleInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a2332cecaee359ea5dc71e73ec290730669a845
--- /dev/null
+++ b/core/modules/media_entity/src/MediaBundleInterface.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface defining a media bundle entity.
+ */
+interface MediaBundleInterface extends ConfigEntityInterface {
+
+  /**
+   * Returns the label.
+   *
+   * @param \Drupal\media_entity\MediaInterface $media
+   *   The Media entity.
+   *
+   * @return string|bool
+   *   Returns the label of the bundle that entity belongs to.
+   */
+  public static function getLabel(MediaInterface $media);
+
+  /**
+   * Checks if the bundle exists.
+   *
+   * @param int $id
+   *   The Media bundle ID.
+   *
+   * @return bool
+   *   TRUE if the bundle with the given ID exists, FALSE otherwise.
+   */
+  public static function exists($id);
+
+  /**
+   * Returns whether thumbnail downloads are queued.
+   *
+   * @return bool
+   *   Returns download now or later.
+   */
+  public function getQueueThumbnailDownloads();
+
+  /**
+   * Sets a flag to indicate that thumbnails should be downloaded via a queue.
+   *
+   * @param bool $queue_thumbnail_downloads
+   *   The queue downloads flag.
+   */
+  public function setQueueThumbnailDownloads($queue_thumbnail_downloads);
+
+  /**
+   * Returns the Media bundle description.
+   *
+   * @return string
+   *   Returns the Media bundle description.
+   */
+  public function getDescription();
+
+  /**
+   * Returns the media type plugin.
+   *
+   * @return \Drupal\media_entity\MediaTypeInterface
+   *   The type.
+   */
+  public function getType();
+
+  /**
+   * Returns the media type configuration.
+   *
+   * @return array
+   *   The type configuration.
+   */
+  public function getTypeConfiguration();
+
+  /**
+   * Sets the media type configuration.
+   *
+   * @param array $configuration
+   *   The type configuration.
+   */
+  public function setTypeConfiguration($configuration);
+
+  /**
+   * Returns the media type status.
+   *
+   * @return bool
+   *   The status.
+   */
+  public function getStatus();
+
+  /**
+   * Gets whether a new revision should be created by default.
+   *
+   * @return bool
+   *   TRUE if a new revision should be created by default.
+   */
+  public function shouldCreateNewRevision();
+
+  /**
+   * Sets whether a new revision should be created by default.
+   *
+   * @param bool $new_revision
+   *   TRUE if a new revision should be created by default.
+   */
+  public function setNewRevision($new_revision);
+
+  /**
+   * Returns the metadata field map.
+   *
+   * @return array
+   *   Field mapping array with fields provided by the type plugin as keys and
+   *   Drupal Entity fields as values.
+   */
+  public function getFieldMap();
+
+}
diff --git a/core/modules/media_entity/src/MediaBundleListBuilder.php b/core/modules/media_entity/src/MediaBundleListBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5158b131a1c86f95b6c5e6293518c6ab034cb3f
--- /dev/null
+++ b/core/modules/media_entity/src/MediaBundleListBuilder.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Url;
+
+/**
+ * Provides a listing of media bundles.
+ */
+class MediaBundleListBuilder extends ConfigEntityListBuilder implements EntityHandlerInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['title'] = $this->t('Name');
+    $header['description'] = [
+      'data' => $this->t('Description'),
+      'class' => [RESPONSIVE_PRIORITY_MEDIUM],
+    ];
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['title'] = [
+      'data' => $entity->label(),
+      'class' => ['menu-label'],
+    ];
+    $row['description'] = Xss::filterAdmin($entity->getDescription());
+    return $row + parent::buildRow($entity);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    $build = parent::render();
+    $build['#empty'] = $this->t('No media bundle available. <a href="@link">Add media bundle</a>.', [
+      '@link' => Url::fromRoute('entity.media_bundle.add_form')->toString(),
+    ]);
+    return $build;
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaForm.php b/core/modules/media_entity/src/MediaForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d77f997980ee1ae90f4bfb907ccf8ee99faeee1
--- /dev/null
+++ b/core/modules/media_entity/src/MediaForm.php
@@ -0,0 +1,242 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Form controller for the media edit forms.
+ */
+class MediaForm extends ContentEntityForm {
+
+  /**
+   * Default settings for this media bundle.
+   *
+   * @var array
+   */
+  protected $settings;
+
+  /**
+   * The entity being used by this form.
+   *
+   * @var \Drupal\media_entity\Entity\Media
+   */
+  protected $entity;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareEntity() {
+    parent::prepareEntity();
+
+    // Set up default values, if required.
+    if (!$this->getEntity()->isNew()) {
+      $this->entity->setRevisionLogMessage(NULL);
+      $this->entity->setOwnerId($this->currentUser()->id());
+      $this->entity->setCreatedTime(\Drupal::time()->getRequestTime());
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+    $entity_type = $this->getEntity()->getEntityType();
+    /** @var \Drupal\media_entity\Entity\MediaBundle $bundle_entity */
+    $bundle_entity = $this->entity->bundle->entity;
+    $account = $this->currentUser();
+    $new_revision = $this->entity->bundle->entity->shouldCreateNewRevision();
+
+    if ($this->operation == 'edit') {
+      $form['#title'] = $this->t('Edit %bundle_label @label', [
+        '%bundle_label' => $bundle_entity->label(),
+        '@label' => $this->entity->label(),
+      ]);
+    }
+
+    $form['advanced'] = [
+      '#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'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Revision information'),
+      // Open by default when "Create new revision" is checked.
+      '#open' => $new_revision,
+      '#group' => 'advanced',
+      '#weight' => 20,
+      '#access' => $new_revision || $account->hasPermission($entity_type->get('admin_permission')),
+      '#attributes' => [
+        'class' => ['media-form-revision-information'],
+      ],
+    ];
+
+    $form['revision_information']['revision'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Create new revision'),
+      '#default_value' => $new_revision,
+      '#access' => $account->hasPermission($entity_type->get('admin_permission')),
+    ];
+
+    // 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 (!$new_revision) {
+      $form['revision_information']['revision']['#states'] = [
+        'checked' => [
+          'textarea[name="revision_log"]' => ['empty' => FALSE],
+        ],
+      ];
+    }
+
+    $form['revision_information']['revision_log'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Revision log message'),
+      '#rows' => 4,
+      '#default_value' => $this->entity->getRevisionLogMessage(),
+      '#description' => $this->t('Briefly describe the changes you have made.'),
+    ];
+
+    // Media author information for administrators.
+    if (isset($form['uid']) || isset($form['created'])) {
+      $form['author'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Authoring information'),
+        '#group' => 'advanced',
+        '#attributes' => [
+          'class' => ['media-form-author'],
+        ],
+        '#weight' => 90,
+        '#optional' => TRUE,
+      ];
+    }
+
+    if (isset($form['uid'])) {
+      $form['uid']['#group'] = 'author';
+    }
+
+    if (isset($form['created'])) {
+      $form['created']['#group'] = 'author';
+    }
+
+    $form['#attached']['library'][] = 'media_entity/media_form';
+
+    $form['#entity_builders']['update_status'] = [$this, 'updateStatus'];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(array $form, FormStateInterface $form_state) {
+    $element = parent::actions($form, $form_state);
+    $media = $this->entity;
+
+    // Add a "Publish" button.
+    $element['publish'] = $element['submit'];
+    // If the "Publish" button is clicked, we want to update the status to
+    // "published".
+    $element['publish']['#published_status'] = TRUE;
+    $element['publish']['#dropbutton'] = 'save';
+    if ($media->isNew()) {
+      $element['publish']['#value'] = $this->t('Save and publish');
+    }
+    else {
+      $element['publish']['#value'] = $media->isPublished() ? $this->t('Save and keep published') : $this->t('Save and publish');
+    }
+    $element['publish']['#weight'] = 0;
+
+    // Add a "Unpublish" button.
+    $element['unpublish'] = $element['submit'];
+    // If the "Unpublish" button is clicked, we want to update the status to
+    // "unpublished".
+    $element['unpublish']['#published_status'] = FALSE;
+    $element['unpublish']['#dropbutton'] = 'save';
+    if ($media->isNew()) {
+      $element['unpublish']['#value'] = $this->t('Save as unpublished');
+    }
+    else {
+      $element['unpublish']['#value'] = !$media->isPublished() ? $this->t('Save and keep unpublished') : $this->t('Save and unpublish');
+    }
+    $element['unpublish']['#weight'] = 10;
+
+    // If already published, the 'publish' button is primary.
+    if ($media->isPublished()) {
+      unset($element['unpublish']['#button_type']);
+    }
+    // Otherwise, the 'unpublish' button is primary and should come first.
+    else {
+      unset($element['publish']['#button_type']);
+      $element['unpublish']['#weight'] = -10;
+    }
+
+    // Remove the "Save" button.
+    $element['submit']['#access'] = FALSE;
+
+    $element['delete']['#access'] = $media->access('delete');
+    $element['delete']['#weight'] = 100;
+
+    return $element;
+  }
+
+  /**
+   * Entity builder updating the media status with the submitted value.
+   *
+   * @param string $entity_type_id
+   *   The entity type identifier.
+   * @param \Drupal\media_entity\MediaInterface $media
+   *   The media updated with the submitted values.
+   * @param array $form
+   *   The complete form array.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @see \Drupal\media_entity\MediaForm::form()
+   */
+  public function updateStatus($entity_type_id, MediaInterface $media, array $form, FormStateInterface $form_state) {
+    $element = $form_state->getTriggeringElement();
+    if (isset($element['#published_status'])) {
+      if ((bool) $element['#published_status']) {
+        $media->setPublished();
+      }
+      else {
+        $media->setUnpublished();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    // Save as a new revision if requested to do so.
+    if (!$form_state->isValueEmpty('revision')) {
+      $this->entity->setNewRevision();
+      $this->entity->setRevisionUserId(\Drupal::currentUser()->id());
+    }
+
+    $insert = $this->entity->isNew();
+    $this->entity->save();
+    $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()];
+    $logger = $this->logger($this->entity->id());
+    $t_args = ['@type' => $this->entity->bundle->entity->label(), '%info' => $this->entity->label()];
+
+    if ($insert) {
+      $logger->notice('@type: added %info.', $context);
+      drupal_set_message($this->t('@type %info has been created.', $t_args));
+    }
+    else {
+      $logger->notice('@type: updated %info.', $context);
+      drupal_set_message($this->t('@type %info has been updated.', $t_args));
+    }
+
+    $form_state->setRedirectUrl($this->entity->toUrl('canonical'));
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaInterface.php b/core/modules/media_entity/src/MediaInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..fed1a65656f757bbb876e5a92245ada68424596d
--- /dev/null
+++ b/core/modules/media_entity/src/MediaInterface.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\RevisionLogInterface;
+use Drupal\user\EntityOwnerInterface;
+
+/**
+ * Provides an interface defining a media entity.
+ */
+interface MediaInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityOwnerInterface, EntityPublishedInterface {
+
+  /**
+   * Returns the media creation timestamp.
+   *
+   * @return int
+   *   Creation timestamp of the media.
+   */
+  public function getCreatedTime();
+
+  /**
+   * Sets the media creation timestamp.
+   *
+   * @param int $timestamp
+   *   The media creation timestamp.
+   *
+   * @return \Drupal\media_entity\MediaInterface
+   *   The called media entity.
+   */
+  public function setCreatedTime($timestamp);
+
+  /**
+   * Returns the media publisher user entity.
+   *
+   * @return \Drupal\user\UserInterface
+   *   The author user entity.
+   *
+   * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use
+   *   \Drupal\user\EntityOwnerInterface::getOwner() instead.
+   */
+  public function getPublisher();
+
+  /**
+   * Returns the media publisher user ID.
+   *
+   * @return int
+   *   The author user ID.
+   *
+   * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use
+   *   \Drupal\user\EntityOwnerInterface::getOwnerId() instead.
+   */
+  public function getPublisherId();
+
+  /**
+   * Sets the media publisher user ID.
+   *
+   * @param int $uid
+   *   The author user id.
+   *
+   * @return \Drupal\media_entity\MediaInterface
+   *   The called media entity.
+   *
+   * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use
+   *   \Drupal\user\EntityOwnerInterface::setOwnerId() instead.
+   */
+  public function setPublisherId($uid);
+
+  /**
+   * Returns the media type.
+   *
+   * @return \Drupal\media_entity\MediaTypeInterface
+   *   The media type.
+   */
+  public function getType();
+
+  /**
+   * Automatically determines the most appropriate thumbnail.
+   *
+   * Determines (with help of type plugin) the thumbnail image and sets the
+   * value of the thumbnail base field.
+   */
+  public function automaticallySetThumbnail();
+
+}
diff --git a/core/modules/media_entity/src/MediaTypeBase.php b/core/modules/media_entity/src/MediaTypeBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..26a483054b264ec4e32903cb3be2447e3e70cdcf
--- /dev/null
+++ b/core/modules/media_entity/src/MediaTypeBase.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Component\Utility\NestedArray;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Base implementation of media type plugin.
+ */
+abstract class MediaTypeBase extends PluginBase implements MediaTypeInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * Plugin label.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface;
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity field manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface;
+   */
+  protected $entityFieldManager;
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a new class instance.
+   *
+   * @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\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   Entity type manager service.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   Entity field manager service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->configFactory = $config_factory;
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager'),
+      $container->get('config.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = NestedArray::mergeDeep(
+      $this->defaultConfiguration(),
+      $configuration
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultThumbnail() {
+    return $this->configFactory->get('media_entity.settings')->get('icon_base') . '/generic.png';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label() {
+    return $this->label;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attachConstraints(MediaInterface $media) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultName(MediaInterface $media) {
+    return 'media:' . $media->bundle() . ':' . $media->uuid();
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaTypeInterface.php b/core/modules/media_entity/src/MediaTypeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a171c2c60906e8e6a638ce9b1aea65fa183adb6
--- /dev/null
+++ b/core/modules/media_entity/src/MediaTypeInterface.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Core\Plugin\PluginFormInterface;
+
+/**
+ * Defines the interface for media types.
+ */
+interface MediaTypeInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface {
+
+  /**
+   * Returns the display label.
+   *
+   * @return string
+   *   The display label.
+   */
+  public function label();
+
+  /**
+   * Gets list of fields provided by this plugin.
+   *
+   * @return array
+   *   Associative array with field names as keys and descriptions as values.
+   */
+  public function providedFields();
+
+  /**
+   * Gets a media-related field/value.
+   *
+   * @param MediaInterface $media
+   *   Media object.
+   * @param string $name
+   *   Name of field to fetch.
+   *
+   * @return mixed
+   *   Field value or FALSE if data unavailable.
+   */
+  public function getField(MediaInterface $media, $name);
+
+  /**
+   * Attaches type-specific constraints to media.
+   *
+   * @param MediaInterface $media
+   *   Media entity.
+   */
+  public function attachConstraints(MediaInterface $media);
+
+  /**
+   * Gets thumbnail image.
+   *
+   * Media type plugin is responsible for returning URI of the generic thumbnail
+   * if no other is available. This functions should always return a valid URI.
+   *
+   * @param MediaInterface $media
+   *   Media.
+   *
+   * @return string
+   *   URI of the thumbnail.
+   */
+  public function thumbnail(MediaInterface $media);
+
+  /**
+   * Gets the default thumbnail image.
+   *
+   * @return string
+   *   Uri of the default thumbnail image.
+   */
+  public function getDefaultThumbnail();
+
+  /**
+   * Provide a default name for the media.
+   *
+   * Plugins defining media bundles are suggested to override this method and
+   * provide a default name, to be used when there is no user-defined label
+   * available.
+   *
+   * @param \Drupal\media_entity\MediaInterface $media
+   *   The media object.
+   *
+   * @return string
+   *   The string that should be used as default media name.
+   */
+  public function getDefaultName(MediaInterface $media);
+
+}
diff --git a/core/modules/media_entity/src/MediaTypeManager.php b/core/modules/media_entity/src/MediaTypeManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ec95cc03873ee65d6e89c0d74fe7292ed66b02a
--- /dev/null
+++ b/core/modules/media_entity/src/MediaTypeManager.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages media entity type plugins.
+ */
+class MediaTypeManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new MediaTypeManager.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/MediaEntity/Type', $namespaces, $module_handler, 'Drupal\media_entity\MediaTypeInterface', 'Drupal\media_entity\Annotation\MediaType');
+
+    $this->alterInfo('media_entity_type_info');
+    $this->setCacheBackend($cache_backend, 'media_entity_type_plugins');
+  }
+
+}
diff --git a/core/modules/media_entity/src/MediaViewsData.php b/core/modules/media_entity/src/MediaViewsData.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1a4e6f38882711f2d446fae46569d4ba8807e94
--- /dev/null
+++ b/core/modules/media_entity/src/MediaViewsData.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\media_entity;
+
+use Drupal\views\EntityViewsData;
+
+/**
+ * Provides the views data for the media entity type.
+ */
+class MediaViewsData extends EntityViewsData {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getViewsData() {
+    $data = parent::getViewsData();
+
+    $data['media_field_data']['table']['wizard_id'] = 'media';
+    $data['media_field_revision']['table']['wizard_id'] = 'media_revision';
+
+    return $data;
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/Action/DeleteMedia.php b/core/modules/media_entity/src/Plugin/Action/DeleteMedia.php
new file mode 100644
index 0000000000000000000000000000000000000000..b2a98d2ebf9ef8c3fa9cd9bfe45b17a1acb95ec9
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/Action/DeleteMedia.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\Action;
+
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Redirects to a media deletion form.
+ *
+ * @Action(
+ *   id = "media_delete_action",
+ *   label = @Translation("Delete media"),
+ *   type = "media",
+ *   confirm_form_route_name = "entity.media.multiple_delete_confirm"
+ * )
+ */
+class DeleteMedia extends ActionBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The tempstore object.
+   *
+   * @var \Drupal\user\SharedTempStore
+   */
+  protected $tempStore;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new DeleteMedia object.
+   *
+   * @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\user\PrivateTempStoreFactory $temp_store_factory
+   *   The tempstore factory.
+   * @param AccountInterface $current_user
+   *   Current user.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->currentUser = $current_user;
+    $this->tempStore = $temp_store_factory->get('media_multiple_delete_confirm');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('user.private_tempstore'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function executeMultiple(array $entities) {
+    $info = [];
+    /** @var \Drupal\media_entity\MediaInterface $media */
+    foreach ($entities as $media) {
+      $langcode = $media->language()->getId();
+      $info[$media->id()][$langcode] = $langcode;
+    }
+    $this->tempStore->set($this->currentUser->id(), $info);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($object = NULL) {
+    $this->executeMultiple(array($object));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    /** @var \Drupal\media_entity\MediaInterface $object */
+    return $object->access('delete', $account, $return_as_object);
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/Action/PublishMedia.php b/core/modules/media_entity/src/Plugin/Action/PublishMedia.php
new file mode 100644
index 0000000000000000000000000000000000000000..fcea6c30f35fdaee07bb0858559f3a3513e33145
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/Action/PublishMedia.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\Action;
+
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\media_entity\Entity\Media;
+
+/**
+ * Publishes a media entity.
+ *
+ * @Action(
+ *   id = "media_publish_action",
+ *   label = @Translation("Publish media"),
+ *   type = "media"
+ * )
+ */
+class PublishMedia extends ActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute(Media $entity = NULL) {
+    $entity->setPublished()->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    /** @var \Drupal\media_entity\MediaInterface $object */
+    $result = $object->access('update', $account, TRUE)
+      ->andIf($object->status->access('update', $account, TRUE));
+
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/Action/SaveMedia.php b/core/modules/media_entity/src/Plugin/Action/SaveMedia.php
new file mode 100644
index 0000000000000000000000000000000000000000..a061cfc4da1477771bb5c0b481a532fa10628285
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/Action/SaveMedia.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\Action;
+
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides an action that can save any entity.
+ *
+ * @Action(
+ *   id = "media_save_action",
+ *   label = @Translation("Save media"),
+ *   type = "media"
+ * )
+ */
+class SaveMedia extends ActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($entity = NULL) {
+    // We need to change at least one value, otherwise the changed timestamp
+    // will not be updated.
+    $entity->changed = 0;
+    $entity->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    /** @var \Drupal\media_entity\MediaInterface $object */
+    return $object->access('update', $account, $return_as_object);
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php b/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb9821f61609e76b746a926381ffc05f289d36b8
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/Action/UnpublishMedia.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\Action;
+
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\media_entity\Entity\Media;
+
+/**
+ * Unpublishes a media entity.
+ *
+ * @Action(
+ *   id = "media_unpublish_action",
+ *   label = @Translation("Unpublish media"),
+ *   type = "media"
+ * )
+ */
+class UnpublishMedia extends ActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute(Media $entity = NULL) {
+    $entity->setUnpublished()->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    /** @var \Drupal\media_entity\MediaInterface $object */
+    $result = $object->access('update', $account, TRUE)
+      ->andIf($object->status->access('update', $account, TRUE));
+
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php b/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php
new file mode 100644
index 0000000000000000000000000000000000000000..a94d121762f824868a41a306ddeebfbb49ecebef
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;
+use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\Core\Render\RendererInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+
+/**
+ * Plugin implementation of the 'media_thumbnail' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "media_thumbnail",
+ *   label = @Translation("Thumbnail"),
+ *   field_types = {
+ *     "entity_reference"
+ *   }
+ * )
+ */
+class MediaThumbnailFormatter extends ImageFormatter {
+
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs an ImageFormatter object.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the formatter.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The definition of the field to which the formatter is associated.
+   * @param array $settings
+   *   The formatter settings.
+   * @param string $label
+   *   The formatter label display setting.
+   * @param string $view_mode
+   *   The view mode.
+   * @param array $third_party_settings
+   *   Any third party settings settings.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer service.
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, RendererInterface $renderer) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $current_user, $image_style_storage);
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $plugin_id,
+      $plugin_definition,
+      $configuration['field_definition'],
+      $configuration['settings'],
+      $configuration['label'],
+      $configuration['view_mode'],
+      $configuration['third_party_settings'],
+      $container->get('current_user'),
+      $container->get('entity.manager')->getStorage('image_style'),
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This has to be overriden because FileFormatterBase expects $item to be
+   * of type \Drupal\file\Plugin\Field\FieldType\FileItem and calls
+   * isDisplayed() which is not in FieldItemInterface.
+   */
+  protected function needsEntityLoad(EntityReferenceItem $item) {
+    return !$item->hasNewEntity();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $element = parent::settingsForm($form, $form_state);
+
+    $link_types = [
+      'content' => $this->t('Content'),
+      'media' => $this->t('Media entity'),
+    ];
+    $element['image_link']['#options'] = $link_types;
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    $summary = parent::settingsSummary();
+
+    $link_types = [
+      'content' => $this->t('Linked to content'),
+      'media' => $this->t('Linked to media entity'),
+    ];
+    // Display this setting only if image is linked.
+    $image_link_setting = $this->getSetting('image_link');
+    if (isset($link_types[$image_link_setting])) {
+      $summary[] = $link_types[$image_link_setting];
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items, $langcode) {
+    $elements = [];
+    $media = $this->getEntitiesToView($items, $langcode);
+
+    // Early opt-out if the field is empty.
+    if (empty($media)) {
+      return $elements;
+    }
+
+    $url = NULL;
+    $image_link_setting = $this->getSetting('image_link');
+    // Check if the formatter involves a link.
+    if ($image_link_setting == 'content') {
+      $entity = $items->getEntity();
+      if (!$entity->isNew()) {
+        $url = $entity->toUrl();
+      }
+    }
+    elseif ($image_link_setting == 'media') {
+      $link_media = TRUE;
+    }
+
+    $image_style_setting = $this->getSetting('image_style');
+
+    /** @var \Drupal\media_entity\MediaInterface $media_item */
+    foreach ($media as $delta => $media_item) {
+      if (isset($link_media)) {
+        $url = $media_item->toUrl();
+      }
+
+      $elements[$delta] = [
+        '#theme' => 'image_formatter',
+        '#item' => $media_item->get('thumbnail'),
+        '#item_attributes' => [],
+        '#image_style' => $image_style_setting,
+        '#url' => $url,
+      ];
+
+      // Collect cache tags to be added for each item in the field.
+      $this->renderer->addCacheableDependency($elements[$delta], $media_item);
+    }
+
+    // Collect cache tags related to the image style setting.
+    if ($image_link_setting && ($image_style = $this->imageStyleStorage->load($image_style_setting))) {
+      $this->renderer->addCacheableDependency($elements, $image_style);
+    }
+
+    return $elements;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function isApplicable(FieldDefinitionInterface $field_definition) {
+    // This formatter is only available for entity types that reference
+    // media entities.
+    $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
+    return $target_type == 'media';
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/MediaEntity/Type/Document.php b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Document.php
new file mode 100644
index 0000000000000000000000000000000000000000..e68d1552380091088ed4b12e07ca77b572f4cc60
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Document.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\MediaEntity\Type;
+
+use Drupal\media_entity\MediaInterface;
+use Drupal\media_entity\MediaTypeBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides media type plugin for Document.
+ *
+ * @MediaType(
+ *   id = "document",
+ *   label = @Translation("Document"),
+ *   description = @Translation("Provides business logic and metadata for local documents.")
+ * )
+ */
+class Document extends MediaTypeBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providedFields() {
+    return [
+      'mime' => $this->t('MIME type'),
+      'size' => $this->t('Size'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getField(MediaInterface $media, $name) {
+    $source_field = $this->configuration['source_field'];
+
+    // Get the file document.
+    /** @var \Drupal\file\FileInterface $file */
+    $file = $media->{$source_field}->entity;
+
+    // Return the field.
+    switch ($name) {
+      case 'mime':
+        return $file->getMimeType() ?: FALSE;
+
+      case 'size':
+        $size = $file->getSize();
+        return is_numeric($size) ? $size : FALSE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
+    $bundle = $form_state->getFormObject()->getEntity();
+    $options = [];
+    $allowed_field_types = ['file'];
+
+    /** @var \Drupal\Core\Field\FieldDefinitionInterface $field */
+    foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
+      if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
+        $options[$field_name] = $field->getLabel();
+      }
+    }
+
+    $form['source_field'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Field with source information'),
+      '#description' => $this->t('Field on media entity that stores Document file. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'),
+      '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'],
+      '#options' => $options,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function thumbnail(MediaInterface $media) {
+    $source_field = $this->configuration['source_field'];
+    /** @var \Drupal\file\FileInterface $file */
+    $file = $media->{$source_field}->entity;
+    $icon_base = $this->configFactory->get('media_entity.settings')->get('icon_base');
+    $thumbnail = FALSE;
+    if ($file) {
+      $mimetype = $file->getMimeType();
+      $mimetype = explode('/', $mimetype);
+      $thumbnail = $icon_base . "/{$mimetype[0]}--{$mimetype[1]}.png";
+
+      if (!is_file($thumbnail)) {
+        $thumbnail = $icon_base . "/{$mimetype[1]}.png";
+      }
+    }
+
+    if (!is_file($thumbnail)) {
+      $thumbnail = $icon_base . '/generic.png';
+    }
+
+    return $thumbnail;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultName(MediaInterface $media) {
+    // The default name will be the filename of the source_field, if present.
+    $source_field = $this->configuration['source_field'];
+
+    /** @var \Drupal\file\FileInterface $file */
+    if (!empty($source_field) && ($file = $media->{$source_field}->entity)) {
+      return $file->getFilename();
+    }
+
+    return parent::getDefaultName($media);
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php
new file mode 100644
index 0000000000000000000000000000000000000000..021fac6dae91f2826eff3ae27b54d978e22b949e
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Generic.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\MediaEntity\Type;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\media_entity\MediaInterface;
+use Drupal\media_entity\MediaTypeBase;
+
+/**
+ * Provides generic media type.
+ *
+ * @MediaType(
+ *   id = "generic",
+ *   label = @Translation("Generic media"),
+ *   description = @Translation("Generic media type.")
+ * )
+ */
+class Generic extends MediaTypeBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providedFields() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getField(MediaInterface $media, $name) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function thumbnail(MediaInterface $media) {
+    return $this->getDefaultThumbnail();
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/QueueWorker/ThumbnailDownloader.php b/core/modules/media_entity/src/Plugin/QueueWorker/ThumbnailDownloader.php
new file mode 100644
index 0000000000000000000000000000000000000000..165f6ab1a59dea0b761ae1459897cfb4a51ed21e
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/QueueWorker/ThumbnailDownloader.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\QueueWorker;
+
+use Drupal\media_entity\Entity\Media;
+use Drupal\Core\Queue\QueueWorkerBase;
+
+/**
+ * Download images.
+ *
+ * @QueueWorker(
+ *   id = "media_entity_thumbnail",
+ *   title = @Translation("Thumbnail downloader"),
+ *   cron = {"time" = 60}
+ * )
+ */
+class ThumbnailDownloader extends QueueWorkerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processItem($data) {
+    /** @var \Drupal\media_entity\MediaInterface $entity */
+    if ($entity = Media::load($data['id'])) {
+      $entity->automaticallySetThumbnail();
+      $entity->save();
+    }
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/views/wizard/Media.php b/core/modules/media_entity/src/Plugin/views/wizard/Media.php
new file mode 100644
index 0000000000000000000000000000000000000000..3a2d9da92275586b4bc9607665bb31a3976ba96b
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/views/wizard/Media.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\views\wizard;
+
+use Drupal\views\Plugin\views\wizard\WizardPluginBase;
+
+/**
+ * Tests creating media views with the wizard.
+ *
+ * @ViewsWizard(
+ *   id = "media",
+ *   base_table = "media_field_data",
+ *   title = @Translation("Media")
+ * )
+ */
+class Media extends WizardPluginBase {
+
+  /**
+   * Set the created column.
+   */
+  protected $createdColumn = 'media_field_data-created';
+
+  /**
+   * Set default values for the filters.
+   */
+  protected $filters = [
+    'status' => [
+      'value' => TRUE,
+      'table' => 'media_field_data',
+      'field' => 'status',
+      'plugin_id' => 'boolean',
+      'entity_type' => 'media',
+      'entity_field' => 'status',
+    ],
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableSorts() {
+    return [
+      'media_field_data-name:DESC' => $this->t('Media name'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defaultDisplayOptions() {
+    $display_options = parent::defaultDisplayOptions();
+
+    // Add permission-based access control.
+    $display_options['access']['type'] = 'perm';
+    $display_options['access']['options']['perm'] = 'view media';
+
+    // Remove the default fields, since we are customizing them here.
+    unset($display_options['fields']);
+
+    // Add the name field, so that the display has content if the user switches
+    // to a row style that uses fields.
+    /* Field: Media: Name */
+    $display_options['fields']['name']['id'] = 'name';
+    $display_options['fields']['name']['table'] = 'media_field_data';
+    $display_options['fields']['name']['field'] = 'name';
+    $display_options['fields']['name']['entity_type'] = 'media';
+    $display_options['fields']['name']['entity_field'] = 'media';
+    $display_options['fields']['name']['label'] = '';
+    $display_options['fields']['name']['alter']['alter_text'] = 0;
+    $display_options['fields']['name']['alter']['make_link'] = 0;
+    $display_options['fields']['name']['alter']['absolute'] = 0;
+    $display_options['fields']['name']['alter']['trim'] = 0;
+    $display_options['fields']['name']['alter']['word_boundary'] = 0;
+    $display_options['fields']['name']['alter']['ellipsis'] = 0;
+    $display_options['fields']['name']['alter']['strip_tags'] = 0;
+    $display_options['fields']['name']['alter']['html'] = 0;
+    $display_options['fields']['name']['hide_empty'] = 0;
+    $display_options['fields']['name']['empty_zero'] = 0;
+    $display_options['fields']['name']['settings']['link_to_entity'] = 1;
+    $display_options['fields']['name']['plugin_id'] = 'field';
+
+    return $display_options;
+  }
+
+}
diff --git a/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php b/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php
new file mode 100644
index 0000000000000000000000000000000000000000..72bceb0996d853474eec2ede4fe2ad52e267210c
--- /dev/null
+++ b/core/modules/media_entity/src/Plugin/views/wizard/MediaRevision.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Drupal\media_entity\Plugin\views\wizard;
+
+use Drupal\views\Plugin\views\wizard\WizardPluginBase;
+
+/**
+ * Tests creating media revision views with the wizard.
+ *
+ * @ViewsWizard(
+ *   id = "media_revision",
+ *   base_table = "media_field_revision",
+ *   title = @Translation("Media revisions")
+ * )
+ */
+class MediaRevision extends WizardPluginBase {
+
+  /**
+   * Set the created column.
+   */
+  protected $createdColumn = 'changed';
+
+  /**
+   * Set default values for the filters.
+   */
+  protected $filters = [
+    'status' => [
+      'value' => TRUE,
+      'table' => 'media_field_revision',
+      'field' => 'status',
+      'plugin_id' => 'boolean',
+      'entity_type' => 'media',
+      'entity_field' => 'status',
+    ],
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defaultDisplayOptions() {
+    $display_options = parent::defaultDisplayOptions();
+
+    // Add permission-based access control.
+    $display_options['access']['type'] = 'perm';
+    $display_options['access']['options']['perm'] = 'view all revisions';
+
+    // Remove the default fields, since we are customizing them here.
+    unset($display_options['fields']);
+
+    /* Field: Media revision: Created date */
+    $display_options['fields']['changed']['id'] = 'changed';
+    $display_options['fields']['changed']['table'] = 'media_field_revision';
+    $display_options['fields']['changed']['field'] = 'changed';
+    $display_options['fields']['changed']['entity_type'] = 'media';
+    $display_options['fields']['changed']['entity_field'] = 'changed';
+    $display_options['fields']['changed']['alter']['alter_text'] = FALSE;
+    $display_options['fields']['changed']['alter']['make_link'] = FALSE;
+    $display_options['fields']['changed']['alter']['absolute'] = FALSE;
+    $display_options['fields']['changed']['alter']['trim'] = FALSE;
+    $display_options['fields']['changed']['alter']['word_boundary'] = FALSE;
+    $display_options['fields']['changed']['alter']['ellipsis'] = FALSE;
+    $display_options['fields']['changed']['alter']['strip_tags'] = FALSE;
+    $display_options['fields']['changed']['alter']['html'] = FALSE;
+    $display_options['fields']['changed']['hide_empty'] = FALSE;
+    $display_options['fields']['changed']['empty_zero'] = FALSE;
+    $display_options['fields']['changed']['plugin_id'] = 'field';
+    $display_options['fields']['changed']['type'] = 'timestamp';
+    $display_options['fields']['changed']['settings']['date_format'] = 'medium';
+    $display_options['fields']['changed']['settings']['custom_date_format'] = '';
+    $display_options['fields']['changed']['settings']['timezone'] = '';
+
+    /* Field: Media revision: Name */
+    $display_options['fields']['name']['id'] = 'name';
+    $display_options['fields']['name']['table'] = 'media_field_revision';
+    $display_options['fields']['name']['field'] = 'name';
+    $display_options['fields']['name']['entity_type'] = 'media';
+    $display_options['fields']['name']['entity_field'] = 'name';
+    $display_options['fields']['name']['label'] = '';
+    $display_options['fields']['name']['alter']['alter_text'] = 0;
+    $display_options['fields']['name']['alter']['make_link'] = 0;
+    $display_options['fields']['name']['alter']['absolute'] = 0;
+    $display_options['fields']['name']['alter']['trim'] = 0;
+    $display_options['fields']['name']['alter']['word_boundary'] = 0;
+    $display_options['fields']['name']['alter']['ellipsis'] = 0;
+    $display_options['fields']['name']['alter']['strip_tags'] = 0;
+    $display_options['fields']['name']['alter']['html'] = 0;
+    $display_options['fields']['name']['hide_empty'] = 0;
+    $display_options['fields']['name']['empty_zero'] = 0;
+    $display_options['fields']['name']['settings']['link_to_entity'] = 0;
+    $display_options['fields']['name']['plugin_id'] = 'field';
+
+    return $display_options;
+  }
+
+}
diff --git a/core/modules/media_entity/templates/media.html.twig b/core/modules/media_entity/templates/media.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..adbc176bfbe81775cd8130c157e07e30b85eb098
--- /dev/null
+++ b/core/modules/media_entity/templates/media.html.twig
@@ -0,0 +1,48 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a media entity.
+ *
+ * Available variables:
+ * - media: The entity with limited access to object properties and methods.
+ *   Only method names starting with "get", "has", or "is" and a few common
+ *   methods such as "id", "label", and "bundle" are available. For example:
+ *   - entity.getEntityTypeId() will return the entity type ID.
+ *   - entity.hasField('field_example') returns TRUE if the entity includes
+ *     field_example. (This does not indicate the presence of a value in this
+ *     field.)
+ *   Calling other methods, such as entity.delete(), will result in an exception.
+ *   See \Drupal\Core\Entity\EntityInterface for a full list of methods.
+ * - name: Name of the media.
+ * - content: Media content.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - attributes: HTML attributes for the containing element.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ *
+ * @see template_preprocess_media()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {#
+    In the 'full' view mode the entity label is assumed to be displayed as the
+    page title, so we do not display it here.
+  #}
+  {{ title_prefix }}
+  {% if label and view_mode != 'full' %}
+    <h2{{ title_attributes }}>
+      {{ label }}
+    </h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {% if content %}
+    {{ content }}
+  {% endif %}
+</article>
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml b/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c0a0f70480079cb0bdeaf7871c86d82d75f23748
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_bundle/config/install/media_entity.bundle.test.yml
@@ -0,0 +1,9 @@
+id: test
+label: 'Test bundle'
+description: 'Test bundle.'
+type: generic
+type_configuration: {  }
+status: true
+langcode: en
+dependencies: {  }
+field_map: {  }
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9969dadcad8be8c067f36ff1d0f38b58f4ea7c6c
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_bundle/media_entity_test_bundle.info.yml
@@ -0,0 +1,6 @@
+name: 'Media entity test bundle'
+type: module
+description: 'Provides test bundle for media entity.'
+core: 8.x
+package: Testing
+version: VERSION
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml b/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6ff7e145a2980a76d1e014b3acb75fd1cf8d87a7
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_type/config/schema/media_entity_test_type.schema.yml
@@ -0,0 +1,7 @@
+media_entity.bundle.type.test_type:
+  type: mapping
+  label: 'Test type configuration'
+  mapping:
+    test_config_value:
+      type: string
+      label: 'Test config value'
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ed82c4c71e35c78164fb29ae6374bd7bd8a3e521
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_type/media_entity_test_type.info.yml
@@ -0,0 +1,6 @@
+name: 'Test media type'
+type: module
+description: 'Provides test media type plugin to test configuration forms.'
+core: 8.x
+package: Testing
+version: VERSION
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php b/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php
new file mode 100644
index 0000000000000000000000000000000000000000..60d68a06da4c6506fbc7ee671aa42861a8de19c6
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\media_entity_test_type\Plugin\MediaEntity\Type;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\media_entity\Plugin\MediaEntity\Type\Generic;
+
+/**
+ * Provides generic media type.
+ *
+ * @MediaType(
+ *   id = "test_type",
+ *   label = @Translation("Test type"),
+ *   description = @Translation("Test media type.")
+ * )
+ */
+class TestType extends Generic {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providedFields() {
+    return [
+      'field_1' => $this->t('Field 1'),
+      'field_2' => $this->t('Field 2'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'test_config_value' => 'This is default value.',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['test_config_value'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Test config value'),
+      '#default_value' => empty($this->configuration['test_config_value']) ? NULL : $this->configuration['test_config_value'],
+    ];
+
+    return $form;
+  }
+
+}
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_views/config/install/views.view.test_media_entity_bulk_form.yml b/core/modules/media_entity/tests/modules/media_entity_test_views/config/install/views.view.test_media_entity_bulk_form.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5c068b60ad5d3934149634efdb2a094d76965da7
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_views/config/install/views.view.test_media_entity_bulk_form.yml
@@ -0,0 +1,154 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+    - user
+id: test_media_entity_bulk_form
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: media_field_data
+base_field: mid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      style:
+        type: table
+      row:
+        type: fields
+      fields:
+        media_bulk_form:
+          id: media_bulk_form
+          table: media
+          field: media_bulk_form
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          action_title: 'With selection'
+          include_exclude: exclude
+          selected_actions: {  }
+          entity_type: media
+          plugin_id: media_bulk_form
+        name:
+          id: name
+          table: media_field_data
+          field: name
+          entity_type: media
+          entity_field: media
+          hide_empty: false
+          empty_zero: false
+          settings:
+            link_to_entity: false
+          plugin_id: field
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: 'Media name'
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+        status:
+          id: status
+          table: media_field_data
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Status
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: boolean
+          settings:
+            format: custom
+            format_custom_true: Published
+            format_custom_false: Unpublished
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          entity_type: media
+          entity_field: status
+          plugin_id: field
+      sorts:
+        mid:
+          id: mid
+          table: media_field_data
+          field: mid
+          relationship: none
+          group_type: group
+          admin_label: ''
+          order: ASC
+          exposed: false
+          expose:
+            label: ''
+          entity_type: media
+          entity_field: mid
+          plugin_id: standard
+      title: 'Entity bulk form test view'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: 1
+    display_options:
+      path: test-media-entity-bulk-form
diff --git a/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml b/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3c93c118e79cf9f27c7c902080a2b76792a3be57
--- /dev/null
+++ b/core/modules/media_entity/tests/modules/media_entity_test_views/media_entity_test_views.info.yml
@@ -0,0 +1,9 @@
+name: 'Media Entity test views'
+type: module
+description: 'Provides default views for views media entity tests.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - media_entity
+  - views
diff --git a/core/modules/media_entity/tests/src/Functional/MediaAccessTest.php b/core/modules/media_entity/tests/src/Functional/MediaAccessTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b774fb599aa49a2f74d3238de7183ccb3c57ae6b
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Functional/MediaAccessTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Functional;
+
+use Drupal\media_entity\Entity\Media;
+use Drupal\user\Entity\Role;
+
+/**
+ * Basic access tests for Media Entity.
+ *
+ * @group media_entity
+ */
+class MediaAccessTest extends MediaEntityFunctionalTestBase {
+
+  /**
+   * The test media bundle.
+   *
+   * @var \Drupal\media_entity\MediaBundleInterface
+   */
+  protected $testBundle;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->testBundle = $this->drupalCreateMediaBundle();
+  }
+
+  /**
+   * Test some access control functionality.
+   */
+  public function testMediaAccess() {
+
+    $assert_session = $this->assertSession();
+
+    // Create media.
+    $media = Media::create([
+      'bundle' => $this->testBundle->id(),
+      'name' => 'Unnamed',
+    ]);
+    $media->save();
+    $user_media = Media::create([
+      'bundle' => $this->testBundle->id(),
+      'name' => 'Unnamed',
+      'uid' => $this->nonAdminUser->id(),
+    ]);
+    $user_media->save();
+
+    // We are logged-in as admin, so test 'administer media' permission.
+    $this->drupalGet('media/' . $user_media->id());
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $user_media->id() . '/edit');
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $user_media->id() . '/delete');
+    $assert_session->statusCodeEquals(200);
+
+    $this->drupalLogin($this->nonAdminUser);
+    /** @var \Drupal\user\RoleInterface $role */
+    $role = Role::load('authenticated');
+
+    // Test 'view media' permission.
+    $this->drupalGet('media/' . $media->id());
+    $assert_session->statusCodeEquals(403);
+    $this->grantPermissions($role, ['view media']);
+    $this->drupalGet('media/' . $media->id());
+    $assert_session->statusCodeEquals(200);
+
+    // Test 'create media' permission.
+    $this->drupalGet('media/add/' . $this->testBundle->id());
+    $assert_session->statusCodeEquals(403);
+    $this->grantPermissions($role, ['create media']);
+    $this->drupalGet('media/add/' . $this->testBundle->id());
+    $assert_session->statusCodeEquals(200);
+
+    // Test 'update media' and 'delete media' permissions.
+    $this->drupalGet('media/' . $user_media->id() . '/edit');
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet('media/' . $user_media->id() . '/delete');
+    $assert_session->statusCodeEquals(403);
+    $this->grantPermissions($role, ['update media']);
+    $this->grantPermissions($role, ['delete media']);
+    $this->drupalGet('media/' . $user_media->id() . '/edit');
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $user_media->id() . '/delete');
+    $assert_session->statusCodeEquals(200);
+
+    // Test 'update any media' and 'delete any media' permissions.
+    $this->drupalGet('media/' . $media->id() . '/edit');
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet('media/' . $media->id() . '/delete');
+    $assert_session->statusCodeEquals(403);
+    $this->grantPermissions($role, ['update any media']);
+    $this->grantPermissions($role, ['delete any media']);
+    $this->drupalGet('media/' . $media->id() . '/edit');
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet('media/' . $media->id() . '/delete');
+    $assert_session->statusCodeEquals(200);
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Functional/MediaBulkFormTest.php b/core/modules/media_entity/tests/src/Functional/MediaBulkFormTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5429766fbce8059b1d4118669bdbf8637cd76804
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Functional/MediaBulkFormTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Functional;
+
+use Drupal\media_entity\Entity\Media;
+use Drupal\views\Views;
+
+/**
+ * Tests a media bulk form.
+ *
+ * @group media_entity
+ */
+class MediaBulkFormTest extends MediaEntityFunctionalTestBase {
+
+  /**
+   * Modules to be enabled.
+   *
+   * @var array
+   */
+  public static $modules = ['media_entity_test_views'];
+
+  /**
+   * The test media bundle.
+   *
+   * @var \Drupal\media_entity\MediaBundleInterface
+   */
+  protected $testBundle;
+
+  /**
+   * The test media entities.
+   *
+   * @var \Drupal\media_entity\MediaInterface[]
+   */
+  protected $mediaEntities;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->testBundle = $this->drupalCreateMediaBundle();
+
+    // Create some test media entities.
+    $this->mediaEntities = [];
+    for ($i = 1; $i <= 5; $i++) {
+      $media = Media::create([
+        'bundle' => $this->testBundle->id(),
+      ]);
+      $media->save();
+      $this->mediaEntities[] = $media;
+    }
+
+  }
+
+  /**
+   * Tests the media bulk form.
+   */
+  public function testBulkForm() {
+
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    // Check that all created entities are present in the test view.
+    $view = Views::getView('test_media_entity_bulk_form');
+    $view->execute();
+    $this->assertEquals($view->total_rows, 5);
+
+    // Check the operations are accessible to the logged in user.
+    $this->drupalGet('test-media-entity-bulk-form');
+    // Current available actions: Delete, Save, Publish, Unpublish.
+    $available_actions = [
+      'media_delete_action',
+      'media_publish_action',
+      'media_save_action',
+      'media_unpublish_action',
+    ];
+    foreach ($available_actions as $action_name) {
+      $assert_session->optionExists('action', $action_name);
+    }
+
+    // Test unpublishing in bulk.
+    $page->checkField('media_bulk_form[0]');
+    $page->checkField('media_bulk_form[1]');
+    $page->checkField('media_bulk_form[2]');
+    $page->selectFieldOption('action', 'media_unpublish_action');
+    $page->pressButton('Apply to selected items');
+    $assert_session->pageTextContains('Unpublish media was applied to 3 items');
+    for ($i = 1; $i <= 3; $i++) {
+      $this->assertFalse($this->storage->loadUnchanged($i)->isPublished(), 'The unpublish action failed in some of the media entities.');
+    }
+
+    // Test publishing in bulk.
+    $page->checkField('media_bulk_form[0]');
+    $page->checkField('media_bulk_form[1]');
+    $page->selectFieldOption('action', 'media_publish_action');
+    $page->pressButton('Apply to selected items');
+    $assert_session->pageTextContains('Publish media was applied to 2 items');
+    for ($i = 1; $i <= 2; $i++) {
+      $this->assertTrue($this->storage->loadUnchanged($i)->isPublished(), 'The publish action failed in some of the media entities.');
+    }
+
+    // Test deletion in bulk.
+    $page->checkField('media_bulk_form[0]');
+    $page->checkField('media_bulk_form[1]');
+    $page->selectFieldOption('action', 'media_delete_action');
+    $page->pressButton('Apply to selected items');
+    $assert_session->pageTextContains('Are you sure you want to delete these items?');
+    $page->pressButton('Delete');
+    $assert_session->pageTextContains('Deleted 2 media entities.');
+    for ($i = 1; $i <= 2; $i++) {
+      $this->assertNull($this->storage->loadUnchanged($i), 'Could not delete some of the media entities.');
+    }
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestBase.php b/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ddac2f250dc84180e2b2ce480ef677696ebfc6c
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestBase.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Base class for Media Entity functional tests.
+ *
+ * @package Drupal\Tests\media_entity\Functional
+ */
+abstract class MediaEntityFunctionalTestBase extends BrowserTestBase {
+
+  use MediaEntityFunctionalTestTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'system',
+    'node',
+    'field_ui',
+    'views_ui',
+    'media_entity',
+  ];
+
+  /**
+   * Permissions for the admin user that will be logged-in for test.
+   *
+   * @var array
+   */
+  protected static $adminUserPermissions = [
+    // Media entity permissions.
+    'administer media',
+    'administer media fields',
+    'administer media form display',
+    'administer media display',
+    'administer media bundles',
+    'view media',
+    'create media',
+    'update media',
+    'update any media',
+    'delete media',
+    'delete any media',
+    // Other permissions.
+    'administer views',
+    'access content overview',
+    'view all revisions',
+    'administer content types',
+    'administer node fields',
+    'administer node form display',
+    'bypass node access',
+  ];
+
+  /**
+   * An admin test user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface;
+   */
+  protected $adminUser;
+
+  /**
+   * A non-admin test user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface;
+   */
+  protected $nonAdminUser;
+
+  /**
+   * The storage service.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface;
+   */
+  protected $storage;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Have two users ready to be used in tests.
+    $this->adminUser = $this->drupalCreateUser(static::$adminUserPermissions);
+    $this->nonAdminUser = $this->drupalCreateUser([]);
+    // Start off logged in as admin.
+    $this->drupalLogin($this->adminUser);
+
+    $this->storage = $this->container->get('entity_type.manager')->getStorage('media');
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestTrait.php b/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e2eedf05892758d86a7c733b3d14438e414b48d
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Functional/MediaEntityFunctionalTestTrait.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Functional;
+
+use Drupal\media_entity\Entity\MediaBundle;
+
+/**
+ * Trait with helpers for Media Entity functional tests.
+ *
+ * @package Drupal\Tests\media_entity\Functional
+ */
+trait MediaEntityFunctionalTestTrait {
+
+  /**
+   * Creates a media bundle.
+   *
+   * @param array $values
+   *   The media bundle values.
+   * @param string $type_name
+   *   (optional) The media type provider plugin that is responsible for
+   *   additional logic related to this media).
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   Returns newly created media bundle.
+   */
+  protected function drupalCreateMediaBundle(array $values = [], $type_name = 'generic') {
+    if (!isset($values['bundle'])) {
+      $id = strtolower($this->randomMachineName());
+    }
+    else {
+      $id = $values['bundle'];
+    }
+    $values += [
+      'id' => $id,
+      'label' => $id,
+      'type' => $type_name,
+      'type_configuration' => [],
+      'field_map' => [],
+      'new_revision' => FALSE,
+    ];
+
+    $bundle = MediaBundle::create($values);
+    $status = $bundle->save();
+
+    $this->assertEquals($status, SAVED_NEW, 'Could not create a media bundle of type ' . $type_name . '.');
+
+    return $bundle;
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Functional/MediaUiFunctionalTest.php b/core/modules/media_entity/tests/src/Functional/MediaUiFunctionalTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..930633455822f30cd3fb00b1d671362d9ec6212d
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Functional/MediaUiFunctionalTest.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Functional;
+
+use Drupal\media_entity\Entity\Media;
+
+/**
+ * Ensures that media UI works correctly.
+ *
+ * @group media_entity
+ */
+class MediaUiFunctionalTest extends MediaEntityFunctionalTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'block',
+    'media_entity_test_type',
+  ];
+
+  /**
+   * The test media bundle.
+   *
+   * @var \Drupal\media_entity\MediaBundleInterface
+   */
+  protected $testBundle;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->drupalPlaceBlock('local_actions_block');
+    $this->drupalPlaceBlock('local_tasks_block');
+  }
+
+  /**
+   * Tests the media actions (add/edit/delete).
+   */
+  public function testMediaWithOnlyOneBundle() {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
+    $bundle = $this->drupalCreateMediaBundle(['new_revision' => TRUE]);
+
+    $this->drupalGet('media/add');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->addressEquals('media/add/' . $bundle->id());
+    $assert_session->checkboxChecked('edit-revision');
+
+    // Tests media item add form.
+    $media_name = $this->randomMachineName();
+    $page->fillField('name[0][value]', $media_name);
+    $revision_log_message = $this->randomString();
+    $page->fillField('revision_log', $revision_log_message);
+    $page->pressButton('Save and publish');
+    $media_id = $this->container->get('entity.query')->get('media')->execute();
+    $media_id = reset($media_id);
+    /** @var \Drupal\media_entity\MediaInterface $media */
+    $media = $this->container->get('entity_type.manager')
+      ->getStorage('media')
+      ->loadUnchanged($media_id);
+    $this->assertEquals($media->getRevisionLogMessage(), $revision_log_message);
+    $assert_session->titleEquals($media->label() . ' | Drupal');
+
+    // Tests media edit form.
+    $bundle->setNewRevision(FALSE);
+    $bundle->save();
+    $media_name2 = $this->randomMachineName();
+    $this->drupalGet('media/' . $media_id . '/edit');
+    $assert_session->checkboxNotChecked('edit-revision');
+    $media_name = $this->randomMachineName();
+    $page->fillField('name[0][value]', $media_name2);
+    $page->pressButton('Save and keep published');
+    $assert_session->titleEquals($media_name2 . ' | Drupal');
+
+    // Test that there is no empty vertical tabs element, if the container is
+    // empty (see #2750697).
+    // Make the "Publisher ID" and "Created" fields hidden.
+    $this->drupalGet('/admin/structure/media/manage/' . $bundle->id() . '/form-display');
+    $page->selectFieldOption('fields[created][parent]', 'hidden');
+    $page->selectFieldOption('fields[uid][parent]', 'hidden');
+    $page->pressButton('Save');
+    // Assure we are testing with a user without permission to manage revisions.
+    $this->drupalLogin($this->nonAdminUser);
+    // Check the container is not present.
+    $this->drupalGet('media/' . $media_id . '/edit');
+    // An empty tab container would look like this.
+    $raw_html = '<div data-drupal-selector="edit-advanced" data-vertical-tabs-panes><input class="vertical-tabs__active-tab" data-drupal-selector="edit-advanced-active-tab" type="hidden" name="advanced__active_tab" value="" />' . "\n" . '</div>';
+    $assert_session->responseNotContains($raw_html);
+    // Continue testing as admin.
+    $this->drupalLogin($this->adminUser);
+
+    // Enable revisions by default.
+    $bundle->setNewRevision(TRUE);
+    $bundle->save();
+    $this->drupalGet('media/' . $media_id . '/edit');
+    $assert_session->checkboxChecked('edit-revision');
+    $page->fillField('name[0][value]', $media_name);
+    $page->fillField('revision_log', $revision_log_message);
+    $page->pressButton('Save and keep published');
+    $assert_session->titleEquals($media_name . ' | Drupal');
+    /** @var \Drupal\media_entity\MediaInterface $media */
+    $media = $this->container->get('entity_type.manager')
+      ->getStorage('media')
+      ->loadUnchanged($media_id);
+    $this->assertEquals($media->getRevisionLogMessage(), $revision_log_message);
+
+    // Tests media delete form.
+    $this->drupalGet('media/' . $media_id . '/edit');
+    $page->clickLink('Delete');
+    $assert_session->pageTextContains('This action cannot be undone');
+    $page->pressButton('Delete');
+    $media_id = \Drupal::entityQuery('media')->execute();
+    $this->assertFalse($media_id);
+  }
+
+  /**
+   * Tests the "media/add" and "media/mid" pages.
+   *
+   * Tests if the "media/add" page gives you a selecting option if there are
+   * multiple media bundles available.
+   */
+  public function testMediaWithMultipleBundles() {
+    $assert_session = $this->assertSession();
+
+    // Tests and creates the first media bundle.
+    $first_media_bundle = $this->drupalCreateMediaBundle(['description' => $this->randomMachineName(32)]);
+
+    // Test and create a second media bundle.
+    $second_media_bundle = $this->drupalCreateMediaBundle(['description' => $this->randomMachineName(32)]);
+
+    // Test if media/add displays two media bundle options.
+    $this->drupalGet('media/add');
+
+    // Checks for the first media bundle.
+    $assert_session->pageTextContains($first_media_bundle->label());
+    $assert_session->pageTextContains($first_media_bundle->getDescription());
+    // Checks for the second media bundle.
+    $assert_session->pageTextContains($second_media_bundle->label());
+    $assert_session->pageTextContains($second_media_bundle->getDescription());
+
+    // Continue testing media bundle filter.
+    $first_media_item = Media::create(['bundle' => $first_media_bundle->id()]);
+    $first_media_item->save();
+    $second_media_item = Media::create(['bundle' => $second_media_bundle->id()]);
+    $second_media_item->save();
+
+    // Go to first media item.
+    $this->drupalGet('media/' . $first_media_item->id());
+    $assert_session->statusCodeEquals(200);
+    $assert_session->pageTextContains($first_media_item->label());
+
+    // Go to second media item.
+    $this->drupalGet('media/' . $second_media_item->id());
+    $assert_session->statusCodeEquals(200);
+    $assert_session->pageTextContains($second_media_item->label());
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/DocumentTest.php b/core/modules/media_entity/tests/src/FunctionalJavascript/DocumentTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b02ad231c878508fa779f13df6f09cabdf1eb27b
--- /dev/null
+++ b/core/modules/media_entity/tests/src/FunctionalJavascript/DocumentTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\Tests\media_entity\FunctionalJavascript;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\media_entity\Entity\Media;
+
+/**
+ * Tests the document type plugin.
+ *
+ * @group media_entity
+ */
+class DocumentTest extends MediaTypeTestBase {
+
+  /**
+   * Tests the document type.
+   */
+  public function testDocumentType() {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    $bundle_name = strtolower($this->randomMachineName(12));
+    $provided_fields = ['mime', 'size'];
+    $this->createMediaBundleTest($bundle_name, 'document', $provided_fields);
+    // Create a supported and a non-supported field.
+    $fields = [
+      'field_string1' => 'string',
+      'field_file1' => 'file',
+    ];
+    $this->createMediaFields($fields, $bundle_name);
+    // Adjust the allowed extensions on the file field.
+    $file_field = FieldConfig::load("media.$bundle_name.field_file1");
+    $file_field->setSetting('file_extensions', 'txt')->save();
+    $this->drupalGet("admin/structure/media/manage/$bundle_name");
+    $this->assertSelectOptions("type_configuration[document][source_field]", ['field_file1'], ['field_string1']);
+    $page->selectFieldOption("type_configuration[document][source_field]", 'field_file1');
+    $page->pressButton('Save media bundle');
+    $this->drupalGet('admin/structure/media');
+    // Hide the media name to test default name generation.
+    $this->hideMediaField('name', $bundle_name);
+    // Create a media item.
+    $this->drupalGet("media/add/$bundle_name");
+    $page->attachFileToField("files[field_file1_0]", \Drupal::root() . '/sites/README.txt');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->pressButton('Save and publish');
+
+    $assert_session->addressEquals('media/1');
+    // Make sure the thumbnail shows up.
+    $assert_session->elementAttributeContains('css', '.image-style-thumbnail', 'src', 'generic.png');
+    // Load the media and check its default name.
+    $media = Media::load(1);
+    $this->assertEquals($media->label(), 'README.txt');
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f214f344086fc8431dbd5b4fd175c94573d4901
--- /dev/null
+++ b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Drupal\Tests\media_entity\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\Tests\media_entity\Functional\MediaEntityFunctionalTestTrait;
+
+/**
+ * Base class for Media Entity Javascript functional tests.
+ *
+ * @package Drupal\Tests\media_entity\FunctionalJavascript
+ */
+abstract class MediaEntityJavascriptTestBase extends JavascriptTestBase {
+
+  use MediaEntityFunctionalTestTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'system',
+    'node',
+    'field_ui',
+    'views_ui',
+    'media_entity',
+  ];
+
+  /**
+   * Permissions for the admin user that will be logged-in for test.
+   *
+   * @var array
+   */
+  protected static $adminUserPermissions = [
+    // Media entity permissions.
+    'administer media',
+    'administer media fields',
+    'administer media form display',
+    'administer media display',
+    'administer media bundles',
+    'view media',
+    'create media',
+    'update media',
+    'update any media',
+    'delete media',
+    'delete any media',
+    // Other permissions.
+    'administer views',
+    'access content overview',
+    'view all revisions',
+    'administer content types',
+    'administer node fields',
+    'administer node form display',
+    'bypass node access',
+  ];
+
+  /**
+   * An admin test user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface;
+   */
+  protected $adminUser;
+
+  /**
+   * A non-admin test user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface;
+   */
+  protected $nonAdminUser;
+
+  /**
+   * The storage service.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface;
+   */
+  protected $storage;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Have two users ready to be used in tests.
+    $this->adminUser = $this->drupalCreateUser(static::$adminUserPermissions);
+    $this->nonAdminUser = $this->drupalCreateUser([]);
+    // Start off logged in as admin.
+    $this->drupalLogin($this->adminUser);
+
+    $this->storage = $this->container->get('entity_type.manager')->getStorage('media');
+  }
+
+  /**
+   * Waits and asserts that a given element is visible.
+   *
+   * @param string $selector
+   *   The CSS selector.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 1000.
+   * @param string $message
+   *   (Optional) Message to pass to assertJsCondition().
+   */
+  protected function waitUntilVisible($selector, $timeout = 1000, $message = '') {
+    $condition = "jQuery('" . $selector . ":visible').length > 0";
+    $this->assertJsCondition($condition, $timeout, $message);
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeTestBase.php b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c5ad32f55dac14ce12ed1cbe7210aa659a6ea28
--- /dev/null
+++ b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeTestBase.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\Tests\media_entity\FunctionalJavascript;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+
+/**
+ * A base test class for plugin types.
+ *
+ * @group media_entity
+ */
+class MediaTypeTestBase extends MediaEntityJavascriptTestBase {
+
+  /**
+   * Create storage and field instance, attached to a given media bundle.
+   *
+   * @param string $field_name
+   *   The field name.
+   * @param string $field_type
+   *   The field storage type.
+   * @param string $bundle_name
+   *   The media bundle machine name.
+   */
+  protected function createMediaField($field_name, $field_type, $bundle_name) {
+    $storage = FieldStorageConfig::create([
+      'field_name' => $field_name,
+      'entity_type' => 'media',
+      'type' => $field_type,
+    ]);
+    $storage->save();
+    $instance = FieldConfig::create([
+      'field_storage' => $storage,
+      'bundle' => $bundle_name,
+    ]);
+    $instance->save();
+    // Make the field visible in the form display.
+    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
+    $component = \Drupal::service('plugin.manager.field.widget')
+      ->prepareConfiguration($field_type, []);
+    entity_get_form_display('media', $bundle_name, 'default')
+      ->setComponent($field_name, $component)
+      ->save();
+  }
+
+  /**
+   * Helper to create a set of fields in a bundle.
+   *
+   * @param array $fields
+   *   An associative array where keys are field names and values field types.
+   * @param string $bundle_name
+   *   The bundle machine name.
+   */
+  protected function createMediaFields(array $fields, $bundle_name) {
+    foreach ($fields as $field_name => $field_type) {
+      $this->createMediaField($field_name, $field_type, $bundle_name);
+    }
+  }
+
+  /**
+   * Hide a component from the default form display config.
+   *
+   * @param string $field_name
+   *   The field name.
+   * @param string $bundle_name
+   *   The media bundle machine name.
+   */
+  protected function hideMediaField($field_name, $bundle_name) {
+    $form_display = entity_get_form_display('media', $bundle_name, 'default');
+    $form_display->removeComponent($field_name)->save();
+  }
+
+  /**
+   * Helper to test a generic bundle creation.
+   *
+   * @param string $bundle_name
+   *   The bundle machine name.
+   * @param string $bundle_type
+   *   The bundle type ID.
+   * @param array $provided_fields
+   *   (optional) An array of field machine names this type provides.
+   * @param bool $check_source_field_element
+   *   (optional) Whether this type provides a "source_field". Defaults to TRUE.
+   */
+  public function createMediaBundleTest($bundle_name, $bundle_type, array $provided_fields = [], $check_source_field_element = TRUE) {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    $this->drupalGet('admin/structure/media/add');
+    $page->fillField('label', $bundle_name);
+    // assertWaitOnAjaxRequest() doesn't work on the machine name element.
+    $session->wait(5000, "jQuery('.machine-name-value').text() === '$bundle_name'");
+
+    // Make sure the bundle type is available as plugin type.
+    $assert_session->optionExists('type', $bundle_type);
+    $page->selectFieldOption('type', $bundle_type);
+    $assert_session->assertWaitOnAjaxRequest();
+
+    // Select our plugin type, and make sure the form gets updated.
+    if ($check_source_field_element) {
+      $assert_session->selectExists("type_configuration[$bundle_type][source_field]");
+    }
+
+    // Make sure the provided fields are visible on the form.
+    if (!empty($provided_fields)) {
+      foreach ($provided_fields as $provided_field) {
+        $assert_session->selectExists("field_mapping[$provided_field]");
+      }
+    }
+
+    // Save the page to create the bundle.
+    $page->pressButton('Save media bundle');
+    $assert_session->pageTextContains('The media bundle ' . $bundle_name . ' has been added.');
+    $this->drupalGet('admin/structure/media');
+    $assert_session->pageTextContains($bundle_name);
+
+  }
+
+  /**
+   * Helper to assert presence/absence of select options.
+   *
+   * @param string $select
+   *   One of id|name|label|value for the select field.
+   * @param array $expected_options
+   *   An indexed array of expected option names.
+   * @param array $non_expected_options
+   *   An indexed array of non-expected option names.
+   *
+   * @see \Drupal\Tests\WebAssert::optionExists()
+   * @see \Drupal\Tests\WebAssert::optionNotExists()
+   */
+  protected function assertSelectOptions($select, $expected_options = [], $non_expected_options = []) {
+    $assert_session = $this->assertSession();
+    foreach ($expected_options as $expected_option) {
+      $assert_session->optionExists($select, $expected_option);
+    }
+    foreach ($non_expected_options as $non_expected_option) {
+      $assert_session->optionNotExists($select, $non_expected_option);
+    }
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..671118122002ebe4a00bf17b39d53b5f6b25e474
--- /dev/null
+++ b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Drupal\Tests\media_entity\FunctionalJavascript;
+
+use Drupal\media_entity\Entity\Media;
+
+/**
+ * Ensures that media UI works correctly.
+ *
+ * @group media_entity
+ */
+class MediaUiJavascriptTest extends MediaEntityJavascriptTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'block',
+    'media_entity_test_type',
+  ];
+
+  /**
+   * The test media bundle.
+   *
+   * @var \Drupal\media_entity\MediaBundleInterface
+   */
+  protected $testBundle;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->drupalPlaceBlock('local_actions_block');
+    $this->drupalPlaceBlock('local_tasks_block');
+  }
+
+  /**
+   * Tests a media bundle administration.
+   */
+  public function testMediaBundles() {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    // Test the creation of a media bundle using the UI.
+    $name = $this->randomMachineName();
+    $description = $this->randomMachineName();
+    $this->drupalGet('admin/structure/media/add');
+    $page->fillField('label', $name);
+    $session->wait(2000);
+    $page->selectFieldOption('type', 'generic');
+    $page->fillField('description', $description);
+    $page->pressButton('Save media bundle');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->pageTextContains('The media bundle ' . $name . ' has been added.');
+    $this->drupalGet('admin/structure/media');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->pageTextContains($name);
+    $assert_session->pageTextContains($description);
+
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle_storage */
+    $bundle_storage = $this->container->get('entity_type.manager')->getStorage('media_bundle');
+    $this->testBundle = $bundle_storage->load(strtolower($name));
+
+    // Check if all action links exist.
+    $assert_session->linkByHrefExists('admin/structure/media/add');
+    $assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testBundle->id());
+    $assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testBundle->id() . '/fields');
+    $assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testBundle->id() . '/form-display');
+    $assert_session->linkByHrefExists('admin/structure/media/manage/' . $this->testBundle->id() . '/display');
+
+    // Assert that fields have expected values before editing.
+    $page->clickLink('Edit');
+    $assert_session->fieldValueEquals('label', $name);
+    $assert_session->fieldValueEquals('description', $description);
+    $assert_session->fieldValueEquals('type', 'generic');
+    $assert_session->fieldValueEquals('label', $name);
+    $assert_session->checkboxNotChecked('edit-options-new-revision');
+    $assert_session->checkboxChecked('edit-options-status');
+    $assert_session->checkboxNotChecked('edit-options-queue-thumbnail-downloads');
+    $assert_session->pageTextContains('Create new revision');
+    $assert_session->pageTextContains('Automatically create a new revision of media entities. Users with the Administer media permission will be able to override this option.');
+    $assert_session->pageTextContains('Download thumbnails via a queue.');
+    $assert_session->pageTextContains('Entities will be automatically published when they are created.');
+    $assert_session->pageTextContains('No metadata fields available.');
+    $assert_session->pageTextContains('Media type plugins can provide metadata fields such as title, caption, size information, credits, ... Media entity can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.');
+
+    // Try to change media type and check if new configuration sub-form appears.
+    $page->selectFieldOption('type', 'test_type');
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->fieldExists('Test config value');
+    $assert_session->fieldValueEquals('Test config value', 'This is default value.');
+    $assert_session->fieldExists('Field 1');
+    $assert_session->fieldExists('Field 2');
+
+    // Test if the edit machine name is not editable.
+    $assert_session->fieldDisabled('Machine-readable name');
+
+    // Edit and save media bundle form fields with new values.
+    $new_name = $this->randomMachineName();
+    $new_description = $this->randomMachineName();
+    $page->fillField('label', $new_name);
+    $page->fillField('description', $new_description);
+    $page->selectFieldOption('type', 'test_type');
+    $page->fillField('Test config value', 'This is new config value.');
+    $page->selectFieldOption('field_mapping[field_1]', 'name');
+    $page->checkField('options[new_revision]');
+    $page->uncheckField('options[status]');
+    $page->checkField('options[queue_thumbnail_downloads]');
+    $page->pressButton('Save media bundle');
+    $assert_session->statusCodeEquals(200);
+
+    // Test if edit worked and if new field values have been saved as expected.
+    $this->drupalGet('admin/structure/media/manage/' . $this->testBundle->id());
+    $assert_session->fieldValueEquals('label', $new_name);
+    $assert_session->fieldValueEquals('description', $new_description);
+    $assert_session->fieldValueEquals('type', 'test_type');
+    $assert_session->checkboxChecked('options[new_revision]');
+    $assert_session->checkboxNotChecked('options[status]');
+    $assert_session->checkboxChecked('options[queue_thumbnail_downloads]');
+    $assert_session->fieldValueEquals('Test config value', 'This is new config value.');
+    $assert_session->fieldValueEquals('Field 1', 'name');
+    $assert_session->fieldValueEquals('Field 2', '_none');
+
+    /** @var \Drupal\media_entity\MediaBundleInterface $loaded_bundle */
+    $loaded_bundle = $this->container->get('entity_type.manager')
+      ->getStorage('media_bundle')
+      ->load($this->testBundle->id());
+    $this->assertEquals($loaded_bundle->id(), $this->testBundle->id());
+    $this->assertEquals($loaded_bundle->label(), $new_name);
+    $this->assertEquals($loaded_bundle->getDescription(), $new_description);
+    $this->assertEquals($loaded_bundle->getType()->getPluginId(), 'test_type');
+    $this->assertEquals($loaded_bundle->getType()->getConfiguration()['test_config_value'], 'This is new config value.');
+    $this->assertTrue($loaded_bundle->shouldCreateNewRevision());
+    $this->assertTrue($loaded_bundle->getQueueThumbnailDownloads());
+    $this->assertFalse($loaded_bundle->getStatus());
+    $this->assertEquals($loaded_bundle->getFieldMap(), ['field_1' => 'name']);
+
+    // Test that a media being created with default status to "FALSE" will be
+    // created unpublished.
+    /** @var \Drupal\media_entity\MediaInterface $unpublished_media */
+    $unpublished_media = Media::create(['name' => 'unpublished test media', 'bundle' => $loaded_bundle->id()]);
+    $this->assertFalse($unpublished_media->isPublished());
+    $unpublished_media->delete();
+
+    // Tests media bundle delete form.
+    $page->clickLink('Delete');
+    $assert_session->addressEquals('admin/structure/media/manage/' . $this->testBundle->id() . '/delete');
+    $page->pressButton('Delete');
+    $assert_session->addressEquals('admin/structure/media');
+    $assert_session->pageTextContains('The media bundle ' . $new_name . ' has been deleted.');
+
+    // Test bundle delete prevention when there is existing media.
+    $bundle2 = $this->drupalCreateMediaBundle();
+    $label2 = $bundle2->label();
+    $media = Media::create(['name' => 'lorem ipsum', 'bundle' => $bundle2->id()]);
+    $media->save();
+    $this->drupalGet('admin/structure/media/manage/' . $bundle2->id());
+    $page->clickLink('Delete');
+    $assert_session->addressEquals('admin/structure/media/manage/' . $bundle2->id() . '/delete');
+    $assert_session->fieldNotExists('edit-submit');
+    $assert_session->pageTextContains("$label2 is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the $label2 content.");
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/MediaViewsWizardTest.php b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaViewsWizardTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac734cfe0e93e265ac80ee4fc11f347e97bcaa8c
--- /dev/null
+++ b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaViewsWizardTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\Tests\media_entity\FunctionalJavascript;
+
+use Drupal\views\Views;
+
+/**
+ * Tests the media entity type integration into the wizard.
+ *
+ * @group media_entity
+ *
+ * @see \Drupal\media_entity\Plugin\views\wizard\Media
+ * @see \Drupal\media_entity\Plugin\views\wizard\MediaRevision
+ */
+class MediaViewsWizardTest extends MediaEntityJavascriptTestBase {
+
+  /**
+   * Tests adding a view of media.
+   */
+  public function testMediaWizard() {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    $view_id = strtolower($this->randomMachineName(16));
+    $this->drupalGet('admin/structure/views/add');
+    $page->fillField('label', $view_id);
+    $this->waitUntilVisible('.machine-name-value');
+    $page->selectFieldOption('show[wizard_key]', 'media');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->checkField('page[create]');
+    $page->fillField('page[path]', $this->randomMachineName(16));
+    $page->pressButton('Save and edit');
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertEquals($session->getCurrentUrl(), $this->baseUrl . '/admin/structure/views/view/' . $view_id);
+
+    $view = Views::getView($view_id);
+    $view->initHandlers();
+    $row = $view->display_handler->getOption('row');
+    $this->assertEquals($row['type'], 'fields');
+    // Check for the default filters.
+    $this->assertEquals($view->filter['status']->table, 'media_field_data');
+    $this->assertEquals($view->filter['status']->field, 'status');
+    $this->assertTrue($view->filter['status']->value);
+    // Check for the default fields.
+    $this->assertEquals($view->field['name']->table, 'media_field_data');
+    $this->assertEquals($view->field['name']->field, 'name');
+
+  }
+
+  /**
+   * Tests adding a view of media revisions.
+   */
+  public function testMediaRevisionWizard() {
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $assert_session = $this->assertSession();
+
+    $view_id = strtolower($this->randomMachineName(16));
+    $this->drupalGet('admin/structure/views/add');
+    $page->fillField('label', $view_id);
+    $this->waitUntilVisible('.machine-name-value');
+    $page->selectFieldOption('show[wizard_key]', 'media_revision');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->checkField('page[create]');
+    $page->fillField('page[path]', $this->randomMachineName(16));
+    $page->pressButton('Save and edit');
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertEquals($session->getCurrentUrl(), $this->baseUrl . '/admin/structure/views/view/' . $view_id);
+
+    $view = Views::getView($view_id);
+    $view->initHandlers();
+    $row = $view->display_handler->getOption('row');
+    $this->assertEquals($row['type'], 'fields');
+
+    // Check for the default filters.
+    $this->assertEquals($view->filter['status']->table, 'media_field_revision');
+    $this->assertEquals($view->filter['status']->field, 'status');
+    $this->assertTrue($view->filter['status']->value);
+
+    // Check for the default fields.
+    $this->assertEquals($view->field['name']->table, 'media_field_revision');
+    $this->assertEquals($view->field['name']->field, 'name');
+    $this->assertEquals($view->field['changed']->table, 'media_field_revision');
+    $this->assertEquals($view->field['changed']->field, 'changed');
+
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Kernel/BasicCreationTest.php b/core/modules/media_entity/tests/src/Kernel/BasicCreationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d5aef442eff698e5b2c040e12d07951a8641c260
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Kernel/BasicCreationTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\media_entity\Entity\Media;
+use Drupal\media_entity\Entity\MediaBundle;
+
+/**
+ * Tests creation of Media Bundles and Media Entities.
+ *
+ * @group media_entity
+ */
+class BasicCreationTest extends KernelTestBase {
+
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'media_entity',
+    'image',
+    'user',
+    'field',
+    'system',
+    'file',
+  ];
+
+
+  /**
+   * The test media bundle.
+   *
+   * @var \Drupal\media_entity\MediaBundleInterface
+   */
+  protected $testBundle;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('file');
+    $this->installSchema('file', 'file_usage');
+    $this->installEntitySchema('media');
+    $this->installConfig(['field', 'system', 'image', 'file']);
+
+    // Create a test bundle.
+    $id = strtolower($this->randomMachineName());
+    $this->testBundle = MediaBundle::create([
+      'id' => $id,
+      'label' => $id,
+      'type' => 'generic',
+      'type_configuration' => [],
+      'field_map' => [],
+      'new_revision' => FALSE,
+    ]);
+    $this->testBundle->save();
+
+  }
+
+  /**
+   * Tests creating a media bundle programmatically.
+   */
+  public function testMediaBundleCreation() {
+    /** @var \Drupal\media_entity\MediaBundleInterface $bundle_storage */
+    $bundle_storage = $this->container->get('entity_type.manager')->getStorage('media_bundle');
+
+    $bundle_exists = (bool) $bundle_storage->load($this->testBundle->id());
+    $this->assertTrue($bundle_exists, 'The new media bundle has not been correctly created in the database.');
+
+    // Test default bundle created from default configuration.
+    $this->container->get('module_installer')->install(['media_entity_test_bundle']);
+    $test_bundle = $bundle_storage->load('test');
+    $this->assertTrue((bool) $test_bundle, 'The media bundle from default configuration has not been created in the database.');
+    $this->assertEquals($test_bundle->get('label'), 'Test bundle', 'Could not assure the correct bundle label.');
+    $this->assertEquals($test_bundle->get('description'), 'Test bundle.', 'Could not assure the correct bundle description.');
+    $this->assertEquals($test_bundle->get('type'), 'generic', 'Could not assure the correct bundle plugin type.');
+    $this->assertEquals($test_bundle->get('type_configuration'), [], 'Could not assure the correct plugin configuration.');
+    $this->assertEquals($test_bundle->get('field_map'), [], 'Could not assure the correct field map.');
+  }
+
+  /**
+   * Tests creating a media entity programmatically.
+   */
+  public function testMediaEntityCreation() {
+    $media = Media::create([
+      'bundle' => $this->testBundle->id(),
+      'name' => 'Unnamed',
+    ]);
+    $media->save();
+
+    $media_not_exist = (bool) Media::load(rand(1000, 9999));
+    $this->assertFalse($media_not_exist, 'Failed asserting a non-existent media.');
+
+    $media_exists = (bool) Media::load($media->id());
+    $this->assertTrue($media_exists, 'The new media entity has not been created in the database.');
+    $this->assertEquals($media->bundle(), $this->testBundle->id(), 'The media was not created with the correct bundle.');
+    $this->assertEquals($media->label(), 'Unnamed', 'The media was not created with the correct name.');
+
+    // Test the creation of a media without user-defined label and check if a
+    // default name is provided.
+    $media = Media::create([
+      'bundle' => $this->testBundle->id(),
+    ]);
+    $media->save();
+    $expected_name = 'media' . ':' . $this->testBundle->id() . ':' . $media->uuid();
+    $this->assertEquals($media->bundle(), $this->testBundle->id(), 'The media was not created with correct bundle.');
+    $this->assertEquals($media->label(), $expected_name, 'The media was not created with a default name.');
+  }
+
+}
diff --git a/core/modules/media_entity/tests/src/Kernel/TokensTest.php b/core/modules/media_entity/tests/src/Kernel/TokensTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d7032aadd6193b1581df47fdae2467919923893
--- /dev/null
+++ b/core/modules/media_entity/tests/src/Kernel/TokensTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\Tests\media_entity\Kernel;
+
+use Drupal\Core\Language\Language;
+use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
+use Drupal\media_entity\Entity\Media;
+use Drupal\media_entity\Entity\MediaBundle;
+
+/**
+ * Tests token handling.
+ *
+ * @group media_entity
+ */
+class TokensTest extends EntityKernelTestBase {
+
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'media_entity',
+    'path',
+    'file',
+    'image',
+    'datetime',
+    'language',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('file');
+    $this->installSchema('file', 'file_usage');
+    $this->installEntitySchema('media');
+    $this->installConfig(['language', 'datetime', 'field', 'system']);
+  }
+
+  /**
+   * Tests some of the tokens provided by media_entity.
+   */
+  public function testMediaEntityTokens() {
+    // Create a generic media bundle.
+    $bundle_name = $this->randomMachineName();
+
+    MediaBundle::create([
+      'id' => $bundle_name,
+      'label' => $bundle_name,
+      'type' => 'generic',
+      'type_configuration' => [],
+      'field_map' => [],
+      'status' => 1,
+      'new_revision' => FALSE,
+    ])->save();
+
+    // Create a media entity.
+    $media = Media::create([
+      'name' => $this->randomMachineName(),
+      'bundle' => $bundle_name,
+      'uid' => '1',
+      'langcode' => Language::LANGCODE_DEFAULT,
+      'status' => TRUE,
+    ]);
+    $media->save();
+
+    $token_service = $this->container->get('token');
+
+    $replaced_value = $token_service->replace('[media:name]', ['media' => $media]);
+    $this->assertEquals($media->label(), $replaced_value, 'Token replacement for the media label was sucessful.');
+
+  }
+
+}
diff --git a/core/profiles/standard/config/optional/media_entity.bundle.document.yml b/core/profiles/standard/config/optional/media_entity.bundle.document.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0812e8ab82e1ddd8ffc7b1917256fee8c93962ef
--- /dev/null
+++ b/core/profiles/standard/config/optional/media_entity.bundle.document.yml
@@ -0,0 +1,14 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media_entity
+id: document
+label: Document
+description: 'Use Document for uploading document files.'
+type: document
+queue_thumbnail_downloads: false
+new_revision: false
+type_configuration:
+  source_field: field_media_source_document
+field_map: { }
diff --git a/core/themes/classy/templates/content/media.html.twig b/core/themes/classy/templates/content/media.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..2bdd59e9604799b8b73372000217390d1143cebc
--- /dev/null
+++ b/core/themes/classy/templates/content/media.html.twig
@@ -0,0 +1,19 @@
+{#
+/**
+ * @file
+ * Theme override to display a media.
+ *
+ * Available variables:
+ * - name: Name of the media.
+ * - content: Media content.
+ *
+ * @see template_preprocess_media()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {% if content %}
+    {{ content }}
+  {% endif %}
+</article>
diff --git a/core/themes/stable/templates/content/media.html.twig b/core/themes/stable/templates/content/media.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..2bdd59e9604799b8b73372000217390d1143cebc
--- /dev/null
+++ b/core/themes/stable/templates/content/media.html.twig
@@ -0,0 +1,19 @@
+{#
+/**
+ * @file
+ * Theme override to display a media.
+ *
+ * Available variables:
+ * - name: Name of the media.
+ * - content: Media content.
+ *
+ * @see template_preprocess_media()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {% if content %}
+    {{ content }}
+  {% endif %}
+</article>
