media_entity.install in Media entity 8.2
Same filename and directory in other branches
Install, uninstall and update hooks for Media entity module.
File
media_entity.installView source
<?php
/**
* @file
* Install, uninstall and update hooks for Media entity module.
*/
use Drupal\Core\Utility\UpdateException;
use Drupal\Core\Config\Entity\Query\QueryFactory;
use Drupal\Core\Database\Database;
use Drupal\Core\Url;
use Drupal\user\Entity\Role;
/**
* Checks if required version of the Entity API is installed.
*
* @return int
* REQUIREMENT_OK if dependency is met and REQUIREMENT_ERROR if not.
* REQUIREMENT_WARNING is returned if it can not be determined.
*/
function _media_entity_check_entity_version() {
if (\Drupal::moduleHandler()
->moduleExists('entity')) {
$info = system_get_info('module', 'entity');
if (is_null($info['version'])) {
return REQUIREMENT_WARNING;
}
if (version_compare($info['version'], '8.x-1.0-alpha3') >= 0) {
return REQUIREMENT_ERROR;
}
}
return REQUIREMENT_OK;
}
/**
* Checks if all contrib modules depending on media_entity were updated.
*
* @return array
* An empty array if all modules that depend on media_entity were updated, or
* an array of incompatible modules otherwise. This array will be keyed by
* either "providers" or "modules", depending if the incompatible module is a
* contrib module that provides a type plugin (in which case it is expected to
* have been upgraded to its 2.x branch) or just a module that depends on
* "media_entity", respectively.
*/
function _media_entity_get_incompatible_modules() {
\Drupal::service('plugin.cache_clearer')
->clearCachedDefinitions();
$incompatible_modules = [];
// Modules that provide provider plugins need to have ben updated and be
// implementing now @MediaSource instead of @MediaType plugins.
$old_plugins = \Drupal::service('plugin.manager.media_entity.type')
->getDefinitions();
// The main media_entity module defines a "generic" type. We are directly
// handling this provider's configs in the update hook.
unset($old_plugins['generic']);
foreach ($old_plugins as $definition) {
$incompatible_modules['providers'][$definition['provider']] = $definition['provider'];
}
// None of the enabled modules in the system should at this point depend on
// media_entity anymore.
/** @var \Drupal\Core\Extension\Extension[] $module_list */
$module_list = \Drupal::moduleHandler()
->getModuleList();
foreach (array_keys($module_list) as $module_name) {
$info = system_get_info('module', $module_name);
if (!empty($info['dependencies'])) {
foreach ($info['dependencies'] as $dependency) {
if ($dependency === 'media_entity' || $dependency === 'media_entity:media_entity') {
$incompatible_modules['modules'][$module_name] = $module_name;
}
}
}
}
// Disregard "media_entity_document" and "media_entity_image", once we will
// uninstall them ourselves as part of the update hook.
unset($incompatible_modules['providers']['media_entity_document']);
unset($incompatible_modules['modules']['media_entity_document']);
unset($incompatible_modules['providers']['media_entity_image']);
unset($incompatible_modules['modules']['media_entity_image']);
if (empty($incompatible_modules['providers'])) {
unset($incompatible_modules['providers']);
}
if (empty($incompatible_modules['modules'])) {
unset($incompatible_modules['modules']);
}
return $incompatible_modules;
}
/**
* Helper function to rename config dependencies.
*
* @param string $dependency_type
* The type of the dependency, such as "module" or "config".
* @param string $dependency_id
* The name of the dependency to be updated.
* @param callable $map
* A callback to be passed to array_map() to actually perform the config name
* substitution.
*/
function _media_entity_fix_dependencies($dependency_type, $dependency_id, callable $map) {
$dependents = \Drupal::service('config.manager')
->findConfigEntityDependents($dependency_type, [
$dependency_id,
]);
$key = 'dependencies.' . $dependency_type;
foreach (array_keys($dependents) as $config) {
$config = \Drupal::configFactory()
->getEditable($config);
$dependencies = $config
->get($key);
if (is_array($dependencies)) {
$config
->set($key, array_map($map, $dependencies))
->save();
}
}
}
/**
* Gets the names of all media bundles that use a particular type plugin.
*
* @param string $plugin_id
* The type plugin ID.
*
* @return string[]
* The media bundle IDs which use the specified plugin.
*/
function _media_entity_get_bundles_by_plugin($plugin_id) {
$types = [];
foreach (\Drupal::configFactory()
->listAll('media_entity.bundle') as $name) {
if (\Drupal::config($name)
->get('type') == $plugin_id) {
$types[] = explode('.', $name, 3)[2];
}
}
return $types;
}
/**
* Checks whether this site has image types with EXIF handling enabled.
*
* @return string[]
* The media bundle IDs which have the EXIF handling enabled, or an empty
* array if none have it so.
*/
function _media_entity_get_bundles_using_exif() {
$bundles = [];
foreach (_media_entity_get_bundles_by_plugin('image') as $bundle_name) {
$gather_exif = \Drupal::config("media_entity.bundle.{$bundle_name}")
->get('type_configuration.gather_exif');
if ($gather_exif) {
$bundles[] = $bundle_name;
}
}
return $bundles;
}
/**
* Gets all media base field overrides.
*
* @return array[]
* The key is the field name where the override belongs to and the value is
* an array of all bundles the field is used in.
*/
function _media_entity_get_base_field_overrides() {
$fields = [];
$prefix = 'core.base_field_override.media.';
foreach (\Drupal::configFactory()
->listAll($prefix) as $override) {
list($bundle, $field) = explode('.', mb_substr($override, mb_strlen($prefix)));
$fields[$field][] = $bundle;
}
return $fields;
}
/**
* Implements hook_requirements().
*/
function media_entity_requirements($phase) {
$requirements = [];
if ($phase == 'update' && !_media_entity_check_entity_version()) {
$requirements['entity'] = [
'title' => t('Media entity'),
'value' => t('Entity API missing'),
'description' => t('<a href=":url">Entity API >= 8.x-1.0-alpha3</a> module is now a dependency and needs to be installed before running updates.', [
':url' => 'https://www.drupal.org/project/entity',
]),
'severity' => _media_entity_check_entity_version(),
];
}
// Prevent this branch from being installed on new sites.
if ($phase == 'install') {
$requirements['media_entity_update_only'] = [
'title' => t('Media entity'),
'description' => t('This branch of Media Entity is intended for site upgrades only. Please use the 1.x branch or Drupal core >= 8.6.x if you are building a new site.'),
'severity' => REQUIREMENT_ERROR,
];
}
if ($phase == 'update') {
// Here we want to ensure that a series of requirements are met before
// letting the DB updates continue. However, the batch processing of
// hook_update_N() triggers this validation again during the update process.
// Because of that, we want to make sure that these requirements checks are
// only evaluated once, and we use a state variable for that.
if (!\Drupal::state()
->get('media_entity_core_upgrade_started')) {
$checks = \Drupal::service('media_entity.cli')
->validateDbUpdateRequirements();
foreach ($checks['errors'] as $key => $error_msg) {
$requirements['media_entity_upgrade_' . $key] = [
'title' => t('Media Entity'),
'value' => t('Please fix the error below and try again.'),
'description' => $error_msg,
'severity' => REQUIREMENT_ERROR,
];
}
}
}
if ($phase == 'runtime') {
if (drupal_get_installed_schema_version('media_entity') < 8201) {
$requirements['media_entity_update_status'] = [
'title' => t('Media Entity'),
'value' => t('DB updates for Media Entity pending.'),
'description' => t('After updating the Media Entity code, you need to run the <a href=":update">database update script</a> as soon as possible.', [
':update' => Url::fromRoute('system.db_update')
->toString(),
]),
'severity' => REQUIREMENT_WARNING,
];
}
else {
$requirements['media_entity_update_status'] = [
'title' => t('Media Entity'),
'value' => t('DB updates for Media Entity were run.'),
'description' => t('The Media Entity upgrade path was executed, you can now uninstall and remove the Media Entity module from the codebase.'),
'severity' => REQUIREMENT_OK,
];
}
}
return $requirements;
}
/**
* Remove "type" base field.
*/
function media_entity_update_8001() {
$fields = \Drupal::database()
->query('DESCRIBE {media_field_data}')
->fetchCol();
if (in_array('type', $fields)) {
\Drupal::database()
->update('media_field_data')
->fields([
'type' => NULL,
])
->execute();
}
$manager = \Drupal::entityDefinitionUpdateManager();
if ($field = $manager
->getFieldStorageDefinition('type', 'media')) {
$manager
->uninstallFieldStorageDefinition($field);
}
}
/**
* Ensure media entity status value (defaulting to 1).
*/
function media_entity_update_8002() {
// Ensure status values in 'media_field_data' table.
if (\Drupal::database()
->schema()
->tableExists('media_field_data')) {
\Drupal::database()
->update('media_field_data')
->fields([
'status' => 1,
])
->condition('status', NULL, 'IS NULL')
->execute();
}
// Ensure status values in 'media_field_revision' table.
if (\Drupal::database()
->schema()
->tableExists('media_field_revision')) {
\Drupal::database()
->update('media_field_revision')
->fields([
'status' => 1,
])
->condition('status', NULL, 'IS NULL')
->execute();
}
// Flush all caches.
drupal_flush_all_caches();
}
/**
* Ensure Entity API is installed.
*/
function media_entity_update_8003() {
if (_media_entity_check_entity_version() === REQUIREMENT_ERROR) {
throw new UpdateException('Entity API >= 8.x-1.0-alpha3 (drupal.org/project/entity) module is now a dependency and needs to be installed before running updates.');
}
}
/**
* Clears the module handler's hook implementation cache.
*/
function media_entity_update_8200() {
\Drupal::moduleHandler()
->resetImplementations();
\Drupal::service('plugin.cache_clearer')
->clearCachedDefinitions();
// Update the installed entity type so that system_update_8501() works.
$entity_type = \Drupal::entityTypeManager()
->getDefinition('media');
\Drupal::entityDefinitionUpdateManager()
->updateEntityType($entity_type);
}
/**
* Replace Media Entity with Media.
*/
function media_entity_update_8201() {
\Drupal::state()
->set('media_entity_core_upgrade_started', TRUE);
// When Media is installed, it assumes that it needs to create media bundles
// and fields. Because this is an upgrade from Media Entity, that's not the
// case. Track existing media types and fields, so that later when we delete
// the auto-created ones, we don't throw the baby out with the bathwater.
$preexisting_media_config = [];
$prefixes = [
'core.entity_form_display.media.',
'core.entity_view_display.media.',
'field.field.media.',
'field.storage.media.',
'media.type.',
];
foreach ($prefixes as $prefix) {
foreach (\Drupal::configFactory()
->listAll($prefix) as $name) {
$preexisting_media_config[] = $name;
}
}
$snapshots = _media_entity_snapshot_config([
'core.entity_view_mode.media.full',
'core.entity_form_display.media.file.default',
'core.entity_form_display.media.image.default',
'core.entity_view_display.media.file.default',
'core.entity_view_display.media.image.default',
], TRUE);
$install = [
'media',
];
// Install media_entity_generic if available. It stands to reason that this
// module will only be available if you have at least one media type that uses
// the generic plugin, since it has been split out into its own module and is
// only requested if there are media bundles that use it.
// See media_entity_requirements() and
// \Drupal\media_entity\CliService::validateDbUpdateRequirements()/
$module_data = system_rebuild_module_data();
if (isset($module_data['media_entity_generic'])) {
$install[] = 'media_entity_generic';
}
// EXIF image handling was dropped from the patch that moved ME + MEI into
// core. Enable "Media Entity Image EXIF" if needed, which fills in that gap.
// See media_entity_requirements() and
// \Drupal\media_entity\CliService::validateDbUpdateRequirements()/
$bundles_with_exif = _media_entity_get_bundles_using_exif();
if (!empty($bundles_with_exif) && isset($module_data['media_entity_image_exif'])) {
$install[] = 'media_entity_image_exif';
}
// The module installer rebuilds container after module install. All services
// previously instantiated could have the wrong state. Do not use any services
// via previously assign local variables.
\Drupal::service('module_installer')
->install($install);
$config_factory = \Drupal::configFactory();
foreach ($snapshots as $name => $data) {
$config_factory
->getEditable($name)
->setData($data)
->save(TRUE);
}
unset($snapshots);
// Renames the revision fields in the media_revision table and ensures that
// overrides of these fields are also changed properly.
/** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_definitions */
$field_definitions = \Drupal::service('entity_field.manager')
->getFieldStorageDefinitions('media');
$db = Database::getConnection()
->schema();
$field_renames = [
'revision_uid' => 'revision_user',
'revision_timestamp' => 'revision_created',
'revision_log' => 'revision_log_message',
];
$field_overrides = _media_entity_get_base_field_overrides();
foreach ($field_renames as $old_field => $new_field) {
$field_columns = $field_definitions[$new_field]
->getColumns();
$field_property_name = $field_definitions[$new_field]
->getMainPropertyName();
$db
->changeField('media_revision', $old_field, $new_field, $field_columns[$field_property_name]);
if (!empty($field_overrides[$old_field])) {
foreach ($field_overrides[$old_field] as $bundle) {
$config_factory
->getEditable("core.base_field_override.media.{$bundle}.{$old_field}")
->set('id', "media.{$bundle}.{$new_field}")
->set('field_name', $new_field)
->save(TRUE);
$config_factory
->rename("core.base_field_override.media.{$bundle}.{$old_field}", "core.base_field_override.media.{$bundle}.{$new_field}");
}
}
}
// Delete file/image media types automatically created by core media and
// associated fields.
foreach ($prefixes as $prefix) {
foreach ($config_factory
->listAll($prefix) as $name) {
if (!in_array($name, $preexisting_media_config)) {
// We should only do a proper entity delete on media types if we're not
// going to rename an existing media_entity bundle to take their place
// later.
if ($prefix === 'media.type.') {
$media_entity_bundle_name = preg_replace('/^media\\.type\\./', 'media_entity.bundle.', $name);
if (!$config_factory
->get($media_entity_bundle_name)
->isNew()) {
// Delete the media type directly from configuration to avoid
// triggering bundle delete hooks.
$config_factory
->getEditable($name)
->delete();
continue;
}
}
$entity = \Drupal::service('config.manager')
->loadConfigEntityByName($name);
// Check to see if the configuration entity exists. It's possible that
// previous deletes have deleted everything.
if ($entity) {
$entity
->delete();
}
}
}
}
// Move all module dependencies on existing config entities from
// "media_entity" to "media".
_media_entity_fix_dependencies('module', 'media_entity', function ($module) {
return $module === 'media_entity' ? 'media' : $module;
});
// Move all module dependencies on existing config entities from
// "media_entity_document" to "media".
_media_entity_fix_dependencies('module', 'media_entity_document', function ($module) {
return $module === 'media_entity_document' ? 'media' : $module;
});
// Move all module dependencies on existing config entities from
// "media_entity_image" to "media".
_media_entity_fix_dependencies('module', 'media_entity_image', function ($module) {
return $module === 'media_entity_image' ? 'media' : $module;
});
// Move media_entity.settings to media.settings. Note that we don't read and
// save in bulk because the key "icon_base" moved to "icon_base_uri".
$config_factory
->getEditable('media.settings')
->set('icon_base_uri', $config_factory
->get('media_entity.settings')
->get('icon_base'))
->set('standalone_url', TRUE)
->save(TRUE);
$config_factory
->getEditable('media_entity.settings')
->delete();
// Move all bundle configs to the new plugin namespace. This means moving all
// "media_entity.bundle.*" to "media.type.*".
foreach ($config_factory
->listAll('media_entity.bundle.') as $original_name) {
$search = '/^media_entity\\.bundle\\./';
$replace = 'media.type.';
$new_name = preg_replace($search, $replace, $original_name);
$config_factory
->rename($original_name, $new_name);
$config = $config_factory
->getEditable($new_name);
$source_id = $config
->get('type');
$config
->set('source_configuration', $config
->get('type_configuration'))
->clear('type_configuration')
->set('source', $source_id == 'document' ? 'file' : $source_id)
->clear('type')
->save();
_media_entity_fix_dependencies('config', $original_name, function ($bundle_id) use ($search, $replace) {
return preg_replace($search, $replace, $bundle_id);
});
/** @var \Drupal\media\MediaTypeInterface $media_type */
$media_type = \Drupal::entityTypeManager()
->getStorage('media_type')
->load($config
->get('id'));
$media_source = $media_type
->getSource();
$source_field = $media_source
->getSourceFieldDefinition($media_type);
if (!$source_field) {
$source_field = $media_source
->createSourceField($media_type);
$source_field
->getFieldStorageDefinition()
->save();
$source_field
->save();
$media_type
->set('source_configuration', [
'source_field' => $source_field
->getName(),
]);
}
$media_type
->save();
}
// Clear the old UUID map.
\Drupal::keyValue(QueryFactory::CONFIG_LOOKUP_PREFIX . 'media_bundle')
->deleteAll();
// Update any views that use the entity:media_bundle argument validator.
_media_entity_update_views();
// Update media action plugins.
$old_new_action_id_map = [
'media_delete_action' => 'entity:delete_action:media',
'media_publish_action' => 'entity:publish_action:media',
'media_unpublish_action' => 'entity:unpublish_action:media',
'media_save_action' => 'entity:save_action:media',
];
/** @var \Drupal\system\Entity\Action[] $actions */
$actions = \Drupal::entityTypeManager()
->getStorage('action')
->loadMultiple();
foreach ($actions as $action) {
if (isset($old_new_action_id_map[$action
->getPlugin()
->getPluginId()])) {
$action
->setPlugin($old_new_action_id_map[$action
->getPlugin()
->getPluginId()]);
$action
->save();
}
}
/** @var \Drupal\user\Entity\Role $role */
foreach (Role::loadMultiple() as $role) {
if ($role
->hasPermission('administer media bundles')) {
$role
->revokePermission('administer media bundles')
->grantPermission('administer media types')
->save();
}
}
// Disable media_entity_image, media_entity_document, and media_entity. They
// are all superseded by core Media.
if (isset($module_data['media_entity_image'])) {
\Drupal::service('module_installer')
->uninstall([
'media_entity_image',
]);
}
if (isset($module_data['media_entity_document'])) {
\Drupal::service('module_installer')
->uninstall([
'media_entity_document',
]);
}
\Drupal::service('module_installer')
->uninstall([
'media_entity',
]);
// @todo This can be removed when
// https://www.drupal.org/project/drupal/issues/3063734 lands.
// Remove media_entity from the update schema list.
register_shutdown_function(function () {
\Drupal::keyValue('system.schema')
->delete('media_entity');
});
}
/**
* Updates any views that use the entity:media_bundle argument validator.
*/
function _media_entity_update_views() {
$config_factory = \Drupal::configFactory();
foreach ($config_factory
->listAll('views.view') as $name) {
$view = $config_factory
->getEditable($name);
$changed = FALSE;
foreach ($view
->get('display') as $display_id => $display) {
$key = "display.{$display_id}.display_options.arguments";
// If there are no arguments, get() will return NULL, which is [] when
// cast to an array.
$arguments = (array) $view
->get($key);
foreach ($arguments as $id => $argument) {
if (isset($argument['validate']) && $argument['validate']['type'] == 'entity:media_bundle') {
$view
->set("{$key}.{$id}.validate.type", 'entity:media_type');
$changed = TRUE;
}
}
}
if ($changed) {
$view
->save();
}
}
}
/**
* Collects snapshots of config objects.
*
* @param string[] $names
* The names of the config objects to snapshot.
* @param bool $delete
* (optional) Whether to delete the original config objects. Defaults to
* FALSE.
*
* @return array
* The config data, keyed by object name.
*/
function _media_entity_snapshot_config(array $names, $delete = FALSE) {
$snapshots = [];
foreach ($names as $name) {
$config = \Drupal::configFactory()
->getEditable($name);
if (!$config
->isNew()) {
$snapshots[$name] = $config
->get();
if ($delete) {
$config
->delete();
}
}
}
return $snapshots;
}
/**
* Implements hook_update_dependencies().
*/
function media_entity_update_dependencies() {
$dependencies = [];
// Ensure that media_entity_update_8200() comes before other updates. This
// ensures that the fake media entity declared in
// media_entity_entity_type_build() is available and code doesn't try to use
// the old entity type that no longer exists. For example,
// system_update_8402() and system_update_8501() both fail if
// media_entity_update_8200() is not called first.
foreach (update_get_update_list() as $module => $updates) {
if ($module === 'media_entity') {
continue;
}
if (!empty($updates['pending'])) {
$first_update = key($updates['pending']);
$dependencies[$module][$first_update]['media_entity'] = 8200;
}
}
if (version_compare(\Drupal::VERSION, '8.8', '>=')) {
// Ensure that system_update_8803() run before the media update, so that the
// new path_alias module is installed.
$dependencies['media_entity'][8201]['system'] = 8803;
}
else {
// Ensure that system_update_8501() before the media update, so that the
// new revision_default field is installed in the correct table.
$dependencies['media_entity'][8201]['system'] = 8501;
}
return $dependencies;
}
Functions
Name![]() |
Description |
---|---|
media_entity_requirements | Implements hook_requirements(). |
media_entity_update_8001 | Remove "type" base field. |
media_entity_update_8002 | Ensure media entity status value (defaulting to 1). |
media_entity_update_8003 | Ensure Entity API is installed. |
media_entity_update_8200 | Clears the module handler's hook implementation cache. |
media_entity_update_8201 | Replace Media Entity with Media. |
media_entity_update_dependencies | Implements hook_update_dependencies(). |
_media_entity_check_entity_version | Checks if required version of the Entity API is installed. |
_media_entity_fix_dependencies | Helper function to rename config dependencies. |
_media_entity_get_base_field_overrides | Gets all media base field overrides. |
_media_entity_get_bundles_by_plugin | Gets the names of all media bundles that use a particular type plugin. |
_media_entity_get_bundles_using_exif | Checks whether this site has image types with EXIF handling enabled. |
_media_entity_get_incompatible_modules | Checks if all contrib modules depending on media_entity were updated. |
_media_entity_snapshot_config | Collects snapshots of config objects. |
_media_entity_update_views | Updates any views that use the entity:media_bundle argument validator. |