views.post_update.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <?php
  2. /**
  3. * @file
  4. * Post update functions for Views.
  5. */
  6. use Drupal\Core\Config\Entity\ConfigEntityUpdater;
  7. use Drupal\Core\StringTranslation\TranslatableMarkup;
  8. use Drupal\views\Entity\View;
  9. use Drupal\views\Plugin\views\filter\NumericFilter;
  10. use Drupal\views\Plugin\views\filter\StringFilter;
  11. use Drupal\views\Views;
  12. /**
  13. * Update the cacheability metadata for all views.
  14. */
  15. function views_post_update_update_cacheability_metadata() {
  16. // Load all views.
  17. $views = \Drupal::entityManager()->getStorage('view')->loadMultiple();
  18. /* @var \Drupal\views\Entity\View[] $views */
  19. foreach ($views as $view) {
  20. $displays = $view->get('display');
  21. foreach (array_keys($displays) as $display_id) {
  22. $display =& $view->getDisplay($display_id);
  23. // Unset the cache_metadata key, so all cacheability metadata for the
  24. // display is recalculated.
  25. unset($display['cache_metadata']);
  26. }
  27. $view->save();
  28. }
  29. }
  30. /**
  31. * Update some views fields that were previously duplicated.
  32. */
  33. function views_post_update_cleanup_duplicate_views_data() {
  34. $config_factory = \Drupal::configFactory();
  35. $ids = [];
  36. $message = NULL;
  37. $data_tables = [];
  38. $base_tables = [];
  39. $revision_tables = [];
  40. $entities_by_table = [];
  41. $duplicate_fields = [];
  42. $handler_types = Views::getHandlerTypes();
  43. /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
  44. $entity_type_manager = \Drupal::service('entity_type.manager');
  45. // This will allow us to create an index of all entity types of the site.
  46. foreach ($entity_type_manager->getDefinitions() as $entity_type_id => $entity_type) {
  47. // Store the entity keyed by base table. If it has a data table, use that as
  48. // well.
  49. if ($data_table = $entity_type->getDataTable()) {
  50. $entities_by_table[$data_table] = $entity_type;
  51. }
  52. if ($base_table = $entity_type->getBaseTable()) {
  53. $entities_by_table[$base_table] = $entity_type;
  54. }
  55. // The following code basically contains the same kind of logic as
  56. // \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout() to
  57. // prefetch all tables (base, data, revision, and revision data).
  58. $base_tables[$entity_type_id] = $entity_type->getBaseTable() ?: $entity_type->id();
  59. $revisionable = $entity_type->isRevisionable();
  60. $revision_table = '';
  61. if ($revisionable) {
  62. $revision_table = $entity_type->getRevisionTable() ?: $entity_type->id() . '_revision';
  63. }
  64. $revision_tables[$entity_type_id] = $revision_table;
  65. $translatable = $entity_type->isTranslatable();
  66. $data_table = '';
  67. // For example the data table just exists, when the entity type is
  68. // translatable.
  69. if ($translatable) {
  70. $data_table = $entity_type->getDataTable() ?: $entity_type->id() . '_field_data';
  71. }
  72. $data_tables[$entity_type_id] = $data_table;
  73. $duplicate_fields[$entity_type_id] = array_intersect_key($entity_type->getKeys(), array_flip(['id', 'revision', 'bundle']));
  74. }
  75. foreach ($config_factory->listAll('views.view.') as $view_config_name) {
  76. $changed = FALSE;
  77. $view = $config_factory->getEditable($view_config_name);
  78. $displays = $view->get('display');
  79. if (isset($entities_by_table[$view->get('base_table')])) {
  80. $entity_type = $entities_by_table[$view->get('base_table')];
  81. $entity_type_id = $entity_type->id();
  82. $data_table = $data_tables[$entity_type_id];
  83. $base_table = $base_tables[$entity_type_id];
  84. $revision_table = $revision_tables[$entity_type_id];
  85. if ($data_table) {
  86. foreach ($displays as $display_name => &$display) {
  87. foreach ($handler_types as $handler_type) {
  88. if (!empty($display['display_options'][$handler_type['plural']])) {
  89. foreach ($display['display_options'][$handler_type['plural']] as $field_name => &$field) {
  90. $table = $field['table'];
  91. if (($table === $base_table || $table === $revision_table) && in_array($field_name, $duplicate_fields[$entity_type_id])) {
  92. $field['table'] = $data_table;
  93. $changed = TRUE;
  94. }
  95. }
  96. }
  97. }
  98. }
  99. }
  100. }
  101. if ($changed) {
  102. $view->set('display', $displays);
  103. $view->save();
  104. $ids[] = $view->get('id');
  105. }
  106. }
  107. if (!empty($ids)) {
  108. $message = new TranslatableMarkup('Updated tables for field handlers for views: @ids', ['@ids' => implode(', ', array_unique($ids))]);
  109. }
  110. return $message;
  111. }
  112. /**
  113. * Include field formatter dependencies in a view when the formatter is used.
  114. */
  115. function views_post_update_field_formatter_dependencies() {
  116. $views = View::loadMultiple();
  117. array_walk($views, function (View $view) {
  118. $view->save();
  119. });
  120. }
  121. /**
  122. * Fix views with dependencies on taxonomy terms that don't exist.
  123. */
  124. function views_post_update_taxonomy_index_tid() {
  125. $views = View::loadMultiple();
  126. array_walk($views, function (View $view) {
  127. $old_dependencies = $view->getDependencies();
  128. $new_dependencies = $view->calculateDependencies()->getDependencies();
  129. if ($old_dependencies !== $new_dependencies) {
  130. $view->save();
  131. }
  132. });
  133. }
  134. /**
  135. * Fix views with serializer dependencies.
  136. */
  137. function views_post_update_serializer_dependencies() {
  138. $views = View::loadMultiple();
  139. array_walk($views, function (View $view) {
  140. $old_dependencies = $view->getDependencies();
  141. $new_dependencies = $view->calculateDependencies()->getDependencies();
  142. if ($old_dependencies !== $new_dependencies) {
  143. $view->save();
  144. }
  145. });
  146. }
  147. /**
  148. * Set all boolean filter values to strings.
  149. */
  150. function views_post_update_boolean_filter_values() {
  151. $config_factory = \Drupal::configFactory();
  152. foreach ($config_factory->listAll('views.view.') as $view_config_name) {
  153. $view = $config_factory->getEditable($view_config_name);
  154. $save = FALSE;
  155. foreach ($view->get('display') as $display_name => $display) {
  156. if (isset($display['display_options']['filters'])) {
  157. foreach ($display['display_options']['filters'] as $filter_name => $filter) {
  158. if (isset($filter['plugin_id']) && $filter['plugin_id'] === 'boolean') {
  159. $new_value = FALSE;
  160. // Update all boolean and integer values to strings.
  161. if ($filter['value'] === TRUE || $filter['value'] === 1) {
  162. $new_value = '1';
  163. }
  164. elseif ($filter['value'] === FALSE || $filter['value'] === 0) {
  165. $new_value = '0';
  166. }
  167. if ($new_value !== FALSE) {
  168. $view->set("display.$display_name.display_options.filters.$filter_name.value", $new_value);
  169. $save = TRUE;
  170. }
  171. }
  172. }
  173. }
  174. }
  175. if ($save) {
  176. $view->save();
  177. }
  178. }
  179. }
  180. /**
  181. * Rebuild caches to ensure schema changes are read in.
  182. */
  183. function views_post_update_grouped_filters() {
  184. // Empty update to cause a cache rebuild so that the schema changes are read.
  185. }
  186. /**
  187. * Fix table names for revision metadata fields.
  188. *
  189. * @see https://www.drupal.org/node/2831499
  190. */
  191. function views_post_update_revision_metadata_fields() {
  192. // The table names are fixed automatically in
  193. // \Drupal\views\Entity\View::preSave(), so we just need to re-save all views.
  194. $views = View::loadMultiple();
  195. array_walk($views, function (View $view) {
  196. $view->save();
  197. });
  198. }
  199. /**
  200. * Add additional settings to the entity link field and convert node_path usage
  201. * to entity_link.
  202. */
  203. function views_post_update_entity_link_url() {
  204. // Load all views.
  205. $views = \Drupal::entityTypeManager()->getStorage('view')->loadMultiple();
  206. /* @var \Drupal\views\Entity\View[] $views */
  207. foreach ($views as $view) {
  208. $displays = $view->get('display');
  209. $changed = FALSE;
  210. foreach ($displays as $display_name => &$display) {
  211. if (isset($display['display_options']['fields'])) {
  212. foreach ($display['display_options']['fields'] as $field_name => &$field) {
  213. if (isset($field['plugin_id']) && $field['plugin_id'] === 'entity_link') {
  214. // Add any missing settings for entity_link.
  215. if (!isset($field['output_url_as_text'])) {
  216. $field['output_url_as_text'] = FALSE;
  217. $changed = TRUE;
  218. }
  219. if (!isset($field['absolute'])) {
  220. $field['absolute'] = FALSE;
  221. $changed = TRUE;
  222. }
  223. }
  224. elseif (isset($field['plugin_id']) && $field['plugin_id'] === 'node_path') {
  225. // Convert the use of node_path to entity_link.
  226. $field['plugin_id'] = 'entity_link';
  227. $field['field'] = 'view_node';
  228. $field['output_url_as_text'] = TRUE;
  229. $changed = TRUE;
  230. }
  231. }
  232. }
  233. }
  234. if ($changed) {
  235. $view->set('display', $displays);
  236. $view->save();
  237. }
  238. }
  239. }
  240. /**
  241. * Update dependencies for moved bulk field plugin.
  242. */
  243. function views_post_update_bulk_field_moved() {
  244. $views = View::loadMultiple();
  245. array_walk($views, function (View $view) {
  246. $old_dependencies = $view->getDependencies();
  247. $new_dependencies = $view->calculateDependencies()->getDependencies();
  248. if ($old_dependencies !== $new_dependencies) {
  249. $view->save();
  250. }
  251. });
  252. }
  253. /**
  254. * Add placeholder settings to string or numeric filters.
  255. */
  256. function views_post_update_filter_placeholder_text() {
  257. // Load all views.
  258. $views = \Drupal::entityTypeManager()->getStorage('view')->loadMultiple();
  259. /** @var \Drupal\views\Plugin\ViewsHandlerManager $filter_manager */
  260. $filter_manager = \Drupal::service('plugin.manager.views.filter');
  261. /* @var \Drupal\views\Entity\View[] $views */
  262. foreach ($views as $view) {
  263. $displays = $view->get('display');
  264. $save = FALSE;
  265. foreach ($displays as $display_name => &$display) {
  266. if (isset($display['display_options']['filters'])) {
  267. foreach ($display['display_options']['filters'] as $filter_name => &$filter) {
  268. // Any of the children of the modified classes will also be inheriting
  269. // the new settings.
  270. $filter_instance = $filter_manager->getHandler($filter);
  271. if ($filter_instance instanceof StringFilter) {
  272. if (!isset($filter['expose']['placeholder'])) {
  273. $filter['expose']['placeholder'] = '';
  274. $save = TRUE;
  275. }
  276. }
  277. elseif ($filter_instance instanceof NumericFilter) {
  278. if (!isset($filter['expose']['placeholder'])) {
  279. $filter['expose']['placeholder'] = '';
  280. $save = TRUE;
  281. }
  282. if (!isset($filter['expose']['min_placeholder'])) {
  283. $filter['expose']['min_placeholder'] = '';
  284. $save = TRUE;
  285. }
  286. if (!isset($filter['expose']['max_placeholder'])) {
  287. $filter['expose']['max_placeholder'] = '';
  288. $save = TRUE;
  289. }
  290. }
  291. }
  292. }
  293. }
  294. if ($save) {
  295. $view->set('display', $displays);
  296. $view->save();
  297. }
  298. }
  299. }
  300. /**
  301. * Include views data table provider in views dependencies.
  302. */
  303. function views_post_update_views_data_table_dependencies(&$sandbox = NULL) {
  304. $storage = \Drupal::entityTypeManager()->getStorage('view');
  305. if (!isset($sandbox['views'])) {
  306. $sandbox['views'] = $storage->getQuery()->accessCheck(FALSE)->execute();
  307. $sandbox['count'] = count($sandbox['views']);
  308. }
  309. // Process 10 views at a time.
  310. $views = $storage->loadMultiple(array_splice($sandbox['views'], 0, 10));
  311. foreach ($views as $view) {
  312. $original_dependencies = $view->getDependencies();
  313. // Only re-save if dependencies have changed.
  314. if ($view->calculateDependencies()->getDependencies() !== $original_dependencies) {
  315. // We can trust the data because we've already recalculated the
  316. // dependencies.
  317. $view->trustData();
  318. $view->save();
  319. }
  320. }
  321. $sandbox['#finished'] = empty($sandbox['views']) ? 1 : ($sandbox['count'] - count($sandbox['views'])) / $sandbox['count'];
  322. }
  323. /**
  324. * Fix cache max age for table displays.
  325. */
  326. function views_post_update_table_display_cache_max_age(&$sandbox = NULL) {
  327. \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) {
  328. /** @var \Drupal\views\ViewEntityInterface $view */
  329. $displays = $view->get('display');
  330. foreach ($displays as $display_name => &$display) {
  331. if (isset($display['display_options']['style']['type']) && $display['display_options']['style']['type'] === 'table') {
  332. return TRUE;
  333. }
  334. }
  335. return FALSE;
  336. });
  337. }