Upgraded imagemagick and manually altered pdf to image module to handle changes....
[yaffs-website] / web / modules / contrib / media_entity / media_entity.install
1 <?php
2
3 /**
4  * @file
5  * Install, uninstall and update hooks for Media entity module.
6  */
7
8 use Drupal\Core\Utility\UpdateException;
9 use Drupal\Core\Config\Entity\Query\QueryFactory;
10 use Drupal\Core\Database\Database;
11 use Drupal\Core\Url;
12 use Drupal\user\Entity\Role;
13
14 /**
15  * Checks if required version of the Entity API is installed.
16  *
17  * @return bool
18  *   TRUE if dependency is met and FALSE if not.
19  */
20 function _media_entity_check_entity_version() {
21   if (\Drupal::moduleHandler()->moduleExists('entity')) {
22     $info = system_get_info('module', 'entity');
23     if (version_compare($info['version'], '8.x-1.0-alpha3') >= 0) {
24       return TRUE;
25     }
26   }
27
28   return FALSE;
29 }
30
31 /**
32  * Checks if all contrib modules depending on media_entity were updated.
33  *
34  * @return array
35  *   An empty array if all modules that depend on media_entity were updated, or
36  *   an array of incompatible modules otherwise. This array will be keyed by
37  *   either "providers" or "modules", depending if the incompatible module is a
38  *   contrib module that provides a type plugin (in which case it is expected to
39  *   have been upgraded to its 2.x branch) or just a module that depends on
40  *   "media_entity", respectively.
41  */
42 function _media_entity_get_incompatible_modules() {
43   \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions();
44   $incompatible_modules = [];
45   // Modules that provide provider plugins need to have ben updated and be
46   // implementing now @MediaSource instead of @MediaType plugins.
47   $old_plugins = \Drupal::service('plugin.manager.media_entity.type')->getDefinitions();
48   // The main media_entity module defines a "generic" type. We are directly
49   // handling this provider's configs in the update hook.
50   unset($old_plugins['generic']);
51   foreach ($old_plugins as $definition) {
52     $incompatible_modules['providers'][$definition['provider']] = $definition['provider'];
53   }
54   // None of the enabled modules in the system should at this point depend on
55   // media_entity anymore.
56   /** @var \Drupal\Core\Extension\Extension[] $module_list */
57   $module_list = \Drupal::moduleHandler()->getModuleList();
58   foreach (array_keys($module_list) as $module_name) {
59     $info = system_get_info('module', $module_name);
60     if (!empty($info['dependencies'])) {
61       foreach ($info['dependencies'] as $dependency) {
62         if ($dependency === 'media_entity' || $dependency === 'media_entity:media_entity') {
63           $incompatible_modules['modules'][$module_name] = $module_name;
64         }
65       }
66     }
67   }
68
69   // Disregard "media_entity_document" and "media_entity_image", once we will
70   // uninstall them ourselves as part of the update hook.
71   unset($incompatible_modules['providers']['media_entity_document']);
72   unset($incompatible_modules['modules']['media_entity_document']);
73   unset($incompatible_modules['providers']['media_entity_image']);
74   unset($incompatible_modules['modules']['media_entity_image']);
75   if (empty($incompatible_modules['providers'])) {
76     unset($incompatible_modules['providers']);
77   }
78   if (empty($incompatible_modules['modules'])) {
79     unset($incompatible_modules['modules']);
80   }
81
82   return $incompatible_modules;
83 }
84
85 /**
86  * Helper function to rename config dependencies.
87  *
88  * @param string $dependency_type
89  *   The type of the dependency, such as "module" or "config".
90  * @param string $dependency_id
91  *   The name of the dependency to be updated.
92  * @param callable $map
93  *   A callback to be passed to array_map() to actually perform the config name
94  *   substitution.
95  */
96 function _media_entity_fix_dependencies($dependency_type, $dependency_id, callable $map) {
97   $dependents = \Drupal::service('config.manager')
98     ->findConfigEntityDependents($dependency_type, [$dependency_id]);
99
100   $key = 'dependencies.' . $dependency_type;
101
102   foreach (array_keys($dependents) as $config) {
103     $config = \Drupal::configFactory()->getEditable($config);
104     $dependencies = $config->get($key);
105     if (is_array($dependencies)) {
106       $config->set($key, array_map($map, $dependencies))->save();
107     }
108   }
109 }
110
111 /**
112  * Helper function to determine the name of a source field.
113  *
114  * @return string
115  *   The source field name. If one is already stored in configuration, it is
116  *   returned. Otherwise, a new, unused one is generated.
117  */
118 function _media_get_source_field_name($plugin_id) {
119   $base_id = 'field_media_' . $plugin_id;
120   $tries = 0;
121   $storage = \Drupal::entityTypeManager()->getStorage('field_storage_config');
122
123   // Iterate at least once, until no field with the generated ID is found.
124   do {
125     $id = $base_id;
126     // If we've tried before, increment and append the suffix.
127     if ($tries) {
128       $id .= '_' . $tries;
129     }
130     $field = $storage->load('media.' . $id);
131     $tries++;
132   } while ($field);
133
134   return $id;
135 }
136
137 /**
138  * Gets the names of all media bundles that use a particular type plugin.
139  *
140  * @param string $plugin_id
141  *   The type plugin ID.
142  *
143  * @return string[]
144  *   The media bundle IDs which use the specified plugin.
145  */
146 function _media_entity_get_bundles_by_plugin($plugin_id) {
147   $types = [];
148   foreach (\Drupal::configFactory()->listAll('media_entity.bundle') as $name) {
149     if (\Drupal::config($name)->get('type') == $plugin_id) {
150       $types[] = explode('.', $name, 3)[2];
151     }
152   }
153   return $types;
154 }
155
156 /**
157  * Checks whether this site has image types with EXIF handling enabled.
158  *
159  * @return string[]
160  *   The media bundle IDs which have the EXIF handling enabled, or an empty
161  *   array if none have it so.
162  */
163 function _media_entity_get_bundles_using_exif() {
164   $bundles = [];
165
166   foreach (_media_entity_get_bundles_by_plugin('image') as $bundle_name) {
167     $gather_exif = \Drupal::config("media_entity.bundle.$bundle_name")->get('type_configuration.gather_exif');
168     if ($gather_exif) {
169       $bundles[] = $bundle_name;
170     }
171   }
172
173   return $bundles;
174 }
175
176 /**
177  * Implements hook_requirements().
178  */
179 function media_entity_requirements($phase) {
180   $requirements = [];
181   if ($phase == 'update' && !_media_entity_check_entity_version()) {
182     $requirements['entity'] = [
183       'title' => t('Media entity'),
184       'value' => t('Entity API missing'),
185       'description' => t(
186         '<a href=":url">Entity API >= 8.x-1.0-alpha3</a> module is now a dependency and needs to be installed before running updates.',
187         [':url' => 'https://www.drupal.org/project/entity']
188       ),
189       'severity' => REQUIREMENT_ERROR,
190     ];
191   }
192
193   // Prevent this branch from being installed on new sites.
194   if ($phase == 'install') {
195     $requirements['media_entity_update_only'] = [
196       'title' => t('Media entity'),
197       'description' => t('This branch of Media Entity is intended for site upgrades only. Please use the 1.x branch or Drupal core >= 8.4.x if you are building a new site.'),
198       'severity' => REQUIREMENT_ERROR,
199     ];
200   }
201
202   if ($phase == 'update') {
203     // Here we want to ensure that a series of requirements are met before
204     // letting the DB updates continue. However, the batch processing of
205     // hook_update_N() triggers this validation again during the update process.
206     // Because of that, we want to make sure that these requirements checks are
207     // only evaluated once, and we use a state variable for that.
208     if (!\Drupal::state()->get('media_entity_core_upgrade_started')) {
209       $checks = \Drupal::service('media_entity.cli')->validateDbUpdateRequirements();
210       foreach ($checks['errors'] as $key => $error_msg) {
211         $requirements['media_entity_upgrade_' . $key] = [
212           'title' => t('Media Entity'),
213           'value' => t('Please fix the error below and try again.'),
214           'description' => $error_msg,
215           'severity' => REQUIREMENT_ERROR,
216         ];
217       }
218     }
219   }
220
221   if ($phase == 'runtime') {
222     if (drupal_get_installed_schema_version('media_entity') < 8201) {
223       $requirements['media_entity_update_status'] = [
224         'title' => t('Media Entity'),
225         'value' => t('DB updates for Media Entity pending.'),
226         'description' => t('After updating the Media Entity code, you need to run the <a href=":update">database update script</a> as soon as possible.', [
227           ':update' => Url::fromRoute('system.db_update')->toString(),
228         ]),
229         'severity' => REQUIREMENT_WARNING,
230       ];
231     }
232     else {
233       $requirements['media_entity_update_status'] = [
234         'title' => t('Media Entity'),
235         'value' => t('DB updates for Media Entity were run.'),
236         'description' => t('The Media Entity upgrade path was executed, you can now uninstall and remove the Media Entity module from the codebase.'),
237         'severity' => REQUIREMENT_OK,
238       ];
239     }
240   }
241
242   return $requirements;
243 }
244
245 /**
246  * Implements hook_install().
247  */
248 function media_entity_install() {
249   $source = drupal_get_path('module', 'media_entity') . '/images/icons';
250   $destination = \Drupal::config('media_entity.settings')->get('icon_base');
251   media_entity_copy_icons($source, $destination);
252 }
253
254 /**
255  * Remove "type" base field.
256  */
257 function media_entity_update_8001() {
258   $fields = \Drupal::database()->query('DESCRIBE {media_field_data}')->fetchCol();
259   if (in_array('type', $fields)) {
260     \Drupal::database()->update('media_field_data')
261       ->fields(['type' => NULL])
262       ->execute();
263   }
264
265   $manager = \Drupal::entityDefinitionUpdateManager();
266   if ($field = $manager->getFieldStorageDefinition('type', 'media')) {
267     $manager->uninstallFieldStorageDefinition($field);
268   }
269 }
270
271 /**
272  * Ensure media entity status value (defaulting to 1).
273  */
274 function media_entity_update_8002() {
275   // Ensure status values in 'media_field_data' table.
276   if (\Drupal::database()->schema()->tableExists('media_field_data')) {
277     \Drupal::database()
278       ->update('media_field_data')
279       ->fields(['status' => 1])
280       ->condition('status', NULL, 'IS NULL')
281       ->execute();
282   }
283
284   // Ensure status values in 'media_field_revision' table.
285   if (\Drupal::database()->schema()->tableExists('media_field_revision')) {
286     \Drupal::database()
287       ->update('media_field_revision')
288       ->fields(['status' => 1])
289       ->condition('status', NULL, 'IS NULL')
290       ->execute();
291   }
292
293   // Flush all caches.
294   drupal_flush_all_caches();
295 }
296
297 /**
298  * Ensure Entity API is installed.
299  */
300 function media_entity_update_8003() {
301   if (!_media_entity_check_entity_version()) {
302     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.');
303   }
304 }
305
306 /**
307  * Clears the module handler's hook implementation cache.
308  */
309 function media_entity_update_8200() {
310   \Drupal::moduleHandler()->resetImplementations();
311   \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions();
312 }
313
314 /**
315  * Replace Media Entity with Media.
316  */
317 function media_entity_update_8201() {
318   $config_factory = \Drupal::configFactory();
319
320   \Drupal::state()->set('media_entity_core_upgrade_started', TRUE);
321
322   // When Media is installed, it assumes that it needs to create media bundles
323   // and fields. Because this is an upgrade from Media Entity, that's not the
324   // case. Track existing media types and fields, so that later when we delete
325   // the auto-created ones, we don't throw the baby out with the bathwater.
326   $preexisting_media_config = [];
327   $prefixes = [
328     'media.type.',
329     'field.field.media.',
330     'field.storage.media.',
331     'core.entity_form_display.media.',
332     'core.entity_view_display.media.',
333   ];
334   foreach ($prefixes as $prefix) {
335     foreach ($config_factory->listAll($prefix) as $name) {
336       $preexisting_media_config[] = $name;
337     }
338   }
339
340   $snapshots = _media_entity_snapshot_config([
341     'core.entity_form_display.media.file.default',
342     'core.entity_form_display.media.image.default',
343     'core.entity_view_display.media.file.default',
344     'core.entity_view_display.media.image.default',
345   ], TRUE);
346
347   $install = ['media'];
348   // Install media_entity_generic if available. It stands to reason that this
349   // module will only be available if you have at least one media type that uses
350   // the generic plugin, since it has been split out into its own module and is
351   // only requested if there are media bundles that use it.
352   // See media_entity_requirements().
353   $module_data = system_rebuild_module_data();
354   if (isset($module_data['media_entity_generic'])) {
355     $install[] = 'media_entity_generic';
356   }
357   // Install media_entity_actions, if not enabled yet. Actions were not included
358   // in Media core, so we need this module to fill that gap until generic entity
359   // actions are part of core, likely in 8.5.x.
360   if (isset($module_data['media_entity_actions'])) {
361     $install[] = 'media_entity_actions';
362   }
363
364   // EXIF image handling was dropped from the patch that moved ME + MEI into
365   // core. Enable "Media Entity Image EXIF" if needed, which fills in that gap.
366   $bundles_with_exif = _media_entity_get_bundles_using_exif();
367   if (!empty($bundles_with_exif) && isset($module_data['media_entity_image_exif'])) {
368     $install[] = 'media_entity_image_exif';
369   }
370
371   \Drupal::service('module_installer')->install($install);
372
373   foreach ($snapshots as $name => $data) {
374     $config_factory->getEditable($name)->setData($data)->save(TRUE);
375   }
376   unset($snapshots);
377
378   // Fix the schema.
379   /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_definitions */
380   $field_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('media');
381   $db = Database::getConnection()->schema();
382   $db->changeField('media_revision', 'revision_uid', 'revision_user', $field_definitions['revision_user']->getColumns()[$field_definitions['revision_user']->getMainPropertyName()]);
383   $db->changeField('media_revision', 'revision_timestamp', 'revision_created', $field_definitions['revision_created']->getColumns()[$field_definitions['revision_created']->getMainPropertyName()]);
384   $db->changeField('media_revision', 'revision_log', 'revision_log_message', $field_definitions['revision_log_message']->getColumns()[$field_definitions['revision_log_message']->getMainPropertyName()]);
385
386   // Delete file/image media types automatically created by core media and
387   // associated fields.
388   $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
389   foreach ($prefixes as $prefix) {
390     foreach ($config_factory->listAll($prefix) as $name) {
391       if (!in_array($name, $preexisting_media_config)) {
392         $config_factory->getEditable($name)->delete();
393         if ($prefix === 'field.storage.media.') {
394           $field_name = substr($name, 20);
395           $storage_definition = $entity_definition_update_manager->getFieldStorageDefinition($field_name, 'media');
396           $entity_definition_update_manager->uninstallFieldStorageDefinition($storage_definition);
397         }
398       }
399     }
400   }
401
402   // Move all module dependencies on existing config entities from
403   // "media_entity" to "media".
404   _media_entity_fix_dependencies('module', 'media_entity', function ($module) {
405     return $module === 'media_entity' ? 'media' : $module;
406   });
407   // Move all module dependencies on existing config entities from
408   // "media_entity_document" to "media".
409   _media_entity_fix_dependencies('module', 'media_entity_document', function ($module) {
410     return $module === 'media_entity_document' ? 'media' : $module;
411   });
412   // Move all module dependencies on existing config entities from
413   // "media_entity_image" to "media".
414   _media_entity_fix_dependencies('module', 'media_entity_image', function ($module) {
415     return $module === 'media_entity_image' ? 'media' : $module;
416   });
417
418   // Move media_entity.settings to media.settings. Note that we don't read and
419   // save in bulk because the key "icon_base" moved to "icon_base_uri".
420   $config_factory->getEditable('media.settings')
421     ->set('icon_base_uri', $config_factory->get('media_entity.settings')->get('icon_base'))
422     ->save();
423   $config_factory->getEditable('media_entity.settings')->delete();
424
425   // Move all bundle configs to the new plugin namespace. This means moving all
426   // "media_entity.bundle.*" to "media.type.*".
427   foreach ($config_factory->listAll('media_entity.bundle.') as $original_name) {
428     $search = '/^media_entity\.bundle\./';
429     $replace = 'media.type.';
430
431     $new_name = preg_replace($search, $replace, $original_name);
432     $config_factory->rename($original_name, $new_name);
433
434     $config = $config_factory->getEditable($new_name);
435     $source_id = $config->get('type');
436     $config
437       ->set('source_configuration', $config->get('type_configuration'))
438       ->clear('type_configuration')
439       ->set('source', $source_id == 'document' ? 'file' : $source_id)
440       ->clear('type')
441       ->save();
442
443     _media_entity_fix_dependencies('config', $original_name, function ($bundle_id) use ($search, $replace) {
444       return preg_replace($search, $replace, $bundle_id);
445     });
446
447     /** @var \Drupal\media\MediaTypeInterface $media_type */
448     $media_type = \Drupal::entityTypeManager()->getStorage('media_type')
449       ->load($config->get('id'));
450     $media_source = $media_type->getSource();
451     $source_field = $media_source->getSourceFieldDefinition($media_type);
452     if (!$source_field) {
453       $source_field = $media_source->createSourceField($media_type);
454       $source_field->getFieldStorageDefinition()->save();
455       $source_field->save();
456
457       $media_type
458         ->set('source_configuration', [
459           'source_field' => $source_field->getName(),
460         ]);
461     }
462     $media_type->save();
463   }
464   // Clear the old UUID map.
465   \Drupal::keyValue(QueryFactory::CONFIG_LOOKUP_PREFIX . 'media_bundle')->deleteAll();
466
467   // Update any views that use the entity:media_bundle argument validator.
468   _media_entity_update_views();
469
470   /** @var \Drupal\user\Entity\Role $role */
471   foreach (Role::loadMultiple() as $role) {
472     if ($role->hasPermission('administer media bundles')) {
473       $role
474         ->revokePermission('administer media bundles')
475         ->grantPermission('administer media types')
476         ->save();
477     }
478   }
479
480   // Disable media_entity_image, media_entity_document, and media_entity. They
481   // are all superseded by core Media.
482   if (isset($module_data['media_entity_image'])) {
483     \Drupal::service('module_installer')->uninstall(['media_entity_image']);
484   }
485   if (isset($module_data['media_entity_document'])) {
486     \Drupal::service('module_installer')->uninstall(['media_entity_document']);
487   }
488   \Drupal::service('module_installer')->uninstall(['media_entity']);
489 }
490
491 /**
492  * Updates any views that use the entity:media_bundle argument validator.
493  */
494 function _media_entity_update_views() {
495   $config_factory = \Drupal::configFactory();
496
497   foreach ($config_factory->listAll('views.view') as $name) {
498     $view = $config_factory->getEditable($name);
499     $changed = FALSE;
500
501     foreach ($view->get('display') as $display_id => $display) {
502       $key = "display.$display_id.display_options.arguments";
503
504       // If there are no arguments, get() will return NULL, which is [] when
505       // cast to an array.
506       $arguments = (array) $view->get($key);
507
508       foreach ($arguments as $id => $argument) {
509         if ($argument['validate']['type'] == 'entity:media_bundle') {
510           $view->set("$key.$id.validate.type", 'entity:media_type');
511           $changed = TRUE;
512         }
513       }
514     }
515     if ($changed) {
516       $view->save();
517     }
518   }
519 }
520
521 /**
522  * Collects snapshots of config objects.
523  *
524  * @param string[] $names
525  *   The names of the config objects to snapshot.
526  * @param bool $delete
527  *   (optional) Whether to delete the original config objects. Defaults to
528  *   FALSE.
529  *
530  * @return array
531  *   The config data, keyed by object name.
532  */
533 function _media_entity_snapshot_config(array $names, $delete = FALSE) {
534   $snapshots = [];
535   foreach ($names as $name) {
536     $config = \Drupal::configFactory()->getEditable($name);
537
538     if (!$config->isNew()) {
539       $snapshots[$name] = $config->get();
540
541       if ($delete) {
542         $config->delete();
543       }
544     }
545   }
546   return $snapshots;
547 }
548
549 /**
550  * Implements hook_update_dependencies().
551  */
552 function media_entity_update_dependencies() {
553   $dependencies = [];
554
555   // Ensure that system_update_8402() is aware of the media entity type, which
556   // is declared dynamically in hook_entity_type_build(). We need to clear the
557   // module handler's hook implementation cache so as to guarantee that it is
558   // aware of media_entity_entity_type_build().
559   $dependencies['system'][8402]['media_entity'] = 8200;
560
561   // Ensure that system_update_8501() before the media update, so that the
562   // new revision_default field is installed in the correct table.
563   $dependencies['media_entity'][8201]['system'] = 8501;
564
565   return $dependencies;
566 }