media.install 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. /**
  3. * @file
  4. * Install, uninstall and update hooks for Media module.
  5. */
  6. use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
  7. use Drupal\Core\File\Exception\FileException;
  8. use Drupal\Core\File\FileSystemInterface;
  9. use Drupal\Core\Url;
  10. use Drupal\media\Entity\MediaType;
  11. use Drupal\media\MediaTypeInterface;
  12. use Drupal\media\Plugin\media\Source\OEmbedInterface;
  13. use Drupal\user\Entity\Role;
  14. use Drupal\user\RoleInterface;
  15. use Drupal\image\Plugin\Field\FieldType\ImageItem;
  16. use Drupal\Core\StringTranslation\TranslatableMarkup;
  17. /**
  18. * Implements hook_install().
  19. */
  20. function media_install() {
  21. $source = drupal_get_path('module', 'media') . '/images/icons';
  22. $destination = \Drupal::config('media.settings')->get('icon_base_uri');
  23. /** @var \Drupal\Core\File\FileSystemInterface $file_system */
  24. $file_system = \Drupal::service('file_system');
  25. $file_system->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
  26. $files = $file_system->scanDirectory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/');
  27. foreach ($files as $file) {
  28. // When reinstalling the media module we don't want to copy the icons when
  29. // they already exist. The icons could be replaced (by a contrib module or
  30. // manually), so we don't want to replace the existing files. Removing the
  31. // files when we uninstall could also be a problem if the files are
  32. // referenced somewhere else. Since showing an error that it was not
  33. // possible to copy the files is also confusing, we silently do nothing.
  34. if (!file_exists($destination . DIRECTORY_SEPARATOR . $file->filename)) {
  35. try {
  36. $file_system->copy($file->uri, $destination, FileSystemInterface::EXISTS_ERROR);
  37. }
  38. catch (FileException $e) {
  39. // Ignore and continue.
  40. }
  41. }
  42. }
  43. // Grant the "view media" permission to all users by default.
  44. if (\Drupal::moduleHandler()->moduleExists('user')) {
  45. user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['view media']);
  46. user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['view media']);
  47. }
  48. }
  49. /**
  50. * Implements hook_requirements().
  51. */
  52. function media_requirements($phase) {
  53. $requirements = [];
  54. if ($phase == 'install') {
  55. $destination = 'public://media-icons/generic';
  56. \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
  57. $is_writable = is_writable($destination);
  58. $is_directory = is_dir($destination);
  59. if (!$is_writable || !$is_directory) {
  60. if (!$is_directory) {
  61. $error = t('The directory %directory does not exist.', ['%directory' => $destination]);
  62. }
  63. else {
  64. $error = t('The directory %directory is not writable.', ['%directory' => $destination]);
  65. }
  66. $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
  67. if (!empty($error)) {
  68. $description = $error . ' ' . $description;
  69. $requirements['media']['description'] = $description;
  70. $requirements['media']['severity'] = REQUIREMENT_ERROR;
  71. }
  72. }
  73. // Prevent installation if the 1.x branch of the contrib module is enabled.
  74. if (\Drupal::moduleHandler()->moduleExists('media_entity')) {
  75. $info = \Drupal::service('extension.list.module')->getExtensionInfo('media_entity');
  76. if (version_compare($info['version'], '8.x-2') < 0) {
  77. $requirements['media_module_incompatibility'] = [
  78. 'title' => t('Media'),
  79. 'description' => t('The Media module is not compatible with contrib <a href=":url">Media Entity</a> 1.x branch. Please check the 2.x branch of that module for an upgrade path.', [
  80. ':url' => 'https://drupal.org/project/media_entity',
  81. ]),
  82. 'severity' => REQUIREMENT_ERROR,
  83. ];
  84. }
  85. }
  86. }
  87. elseif ($phase === 'runtime') {
  88. // Check that oEmbed content is served in an iframe on a different domain,
  89. // and complain if it isn't.
  90. $domain = \Drupal::config('media.settings')->get('iframe_domain');
  91. if (!\Drupal::service('media.oembed.iframe_url_helper')->isSecure($domain)) {
  92. // Find all media types which use a source plugin that implements
  93. // OEmbedInterface.
  94. $media_types = \Drupal::entityTypeManager()
  95. ->getStorage('media_type')
  96. ->loadMultiple();
  97. $oembed_types = array_filter($media_types, function (MediaTypeInterface $media_type) {
  98. return $media_type->getSource() instanceof OEmbedInterface;
  99. });
  100. if ($oembed_types) {
  101. // @todo Potentially allow site administrators to suppress this warning
  102. // permanently. See https://www.drupal.org/project/drupal/issues/2962753
  103. // for more information.
  104. $requirements['media_insecure_iframe'] = [
  105. 'title' => t('Media'),
  106. 'description' => t('It is potentially insecure to display oEmbed content in a frame that is served from the same domain as your main Drupal site, as this may allow execution of third-party code. <a href=":url">You can specify a different domain for serving oEmbed content here</a>.', [
  107. ':url' => Url::fromRoute('media.settings')->setAbsolute()->toString(),
  108. ]),
  109. 'severity' => REQUIREMENT_WARNING,
  110. ];
  111. }
  112. }
  113. // When a new media type with an image source is created we're configuring
  114. // the default entity view display using the 'large' image style.
  115. // Unfortunately, if a site builder has deleted the 'large' image style,
  116. // we need some other image style to use, but at this point, we can't
  117. // really know the site builder's intentions. So rather than do something
  118. // surprising, we're leaving the embedded media without an image style and
  119. // adding a warning that the site builder might want to add an image style.
  120. // @see Drupal\media\Plugin\media\Source\Image::prepareViewDisplay
  121. $module_handler = \Drupal::service('module_handler');
  122. foreach (MediaType::loadMultiple() as $type) {
  123. // Load the default display.
  124. $display = \Drupal::service('entity_display.repository')
  125. ->getViewDisplay('media', $type->id());
  126. $source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
  127. if (!is_a($source_field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE)) {
  128. continue;
  129. }
  130. $component = $display->getComponent($source_field_definition->getName());
  131. if (empty($component) || $component['type'] !== 'image' || !empty($component['settings']['image_style'])) {
  132. continue;
  133. }
  134. $action_item = '';
  135. if ($module_handler->moduleExists('field_ui') && \Drupal::currentUser()->hasPermission('administer media display')) {
  136. $url = Url::fromRoute('entity.entity_view_display.media.default', [
  137. 'media_type' => $type->id(),
  138. ])->toString();
  139. $action_item = new TranslatableMarkup('If you would like to change this, <a href=":display">add an image style to the %field_name field</a>.',
  140. [
  141. '%field_name' => $source_field_definition->label(),
  142. ':display' => $url,
  143. ]);
  144. }
  145. $requirements['media_default_image_style_' . $type->id()] = [
  146. 'title' => t('Media'),
  147. 'description' => new TranslatableMarkup('The default display for the %type media type is not currently using an image style on the %field_name field. Not using an image style can lead to much larger file downloads. @action_item',
  148. [
  149. '%field_name' => $source_field_definition->label(),
  150. '@action_item' => $action_item,
  151. '%type' => $type->label(),
  152. ]
  153. ),
  154. 'severity' => REQUIREMENT_WARNING,
  155. ];
  156. }
  157. }
  158. return $requirements;
  159. }
  160. /**
  161. * Introduce per-bundle permissions.
  162. */
  163. function media_update_8500() {
  164. $media_types = \Drupal::entityQuery('media_type')->execute();
  165. /** @var \Drupal\user\RoleInterface $role */
  166. foreach (Role::loadMultiple() as $role) {
  167. if ($role->hasPermission('update media')) {
  168. foreach ($media_types as $media_type) {
  169. $role->grantPermission("edit own $media_type media");
  170. }
  171. }
  172. if ($role->hasPermission('update any media')) {
  173. foreach ($media_types as $media_type) {
  174. $role->grantPermission("edit any $media_type media");
  175. }
  176. }
  177. if ($role->hasPermission('delete media')) {
  178. foreach ($media_types as $media_type) {
  179. $role->grantPermission("delete own $media_type media");
  180. }
  181. }
  182. if ($role->hasPermission('delete any media')) {
  183. foreach ($media_types as $media_type) {
  184. $role->grantPermission("delete any $media_type media");
  185. }
  186. }
  187. if ($role->hasPermission('create media')) {
  188. foreach ($media_types as $media_type) {
  189. $role->grantPermission("create $media_type media");
  190. }
  191. }
  192. $role->save();
  193. }
  194. }
  195. /**
  196. * Updates media.settings to support OEmbed.
  197. */
  198. function media_update_8600() {
  199. \Drupal::configFactory()->getEditable('media.settings')
  200. ->set('iframe_domain', '')
  201. ->set('oembed_providers_url', 'https://oembed.com/providers.json')
  202. ->save(TRUE);
  203. }
  204. /**
  205. * Set the 'owner' entity key and update the field.
  206. */
  207. function media_update_8700() {
  208. $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  209. $entity_type = $definition_update_manager->getEntityType('media');
  210. $database = \Drupal::database();
  211. if (\Drupal::entityTypeManager()->getStorage('media') instanceof SqlEntityStorageInterface) {
  212. if ($database->schema()->tableExists($entity_type->getDataTable())) {
  213. $database->update($entity_type->getDataTable())
  214. ->fields(['uid' => 0])
  215. ->isNull('uid')
  216. ->execute();
  217. }
  218. if ($database->schema()->tableExists($entity_type->getRevisionDataTable())) {
  219. $database->update($entity_type->getRevisionDataTable())
  220. ->fields(['uid' => 0])
  221. ->isNull('uid')
  222. ->execute();
  223. }
  224. }
  225. $keys = $entity_type->getKeys();
  226. $keys['owner'] = 'uid';
  227. $entity_type->set('entity_keys', $keys);
  228. $definition_update_manager->updateEntityType($entity_type);
  229. $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'media'));
  230. }