StaticMenuLinkOverrides.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. namespace Drupal\Core\Menu;
  3. use Drupal\Core\Config\ConfigFactoryInterface;
  4. /**
  5. * Defines an implementation of the menu link override using a config file.
  6. */
  7. class StaticMenuLinkOverrides implements StaticMenuLinkOverridesInterface {
  8. /**
  9. * The config name used to store the overrides.
  10. *
  11. * This configuration can not be overridden by configuration overrides because
  12. * menu links and these overrides are cached in a way that is not override
  13. * aware.
  14. *
  15. * @var string
  16. */
  17. protected $configName = 'core.menu.static_menu_link_overrides';
  18. /**
  19. * The menu link overrides config object.
  20. *
  21. * @var \Drupal\Core\Config\Config
  22. */
  23. protected $config;
  24. /**
  25. * The config factory object.
  26. *
  27. * @var \Drupal\Core\Config\ConfigFactoryInterface
  28. */
  29. protected $configFactory;
  30. /**
  31. * Constructs a StaticMenuLinkOverrides object.
  32. *
  33. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  34. * A configuration factory instance.
  35. */
  36. public function __construct(ConfigFactoryInterface $config_factory) {
  37. $this->configFactory = $config_factory;
  38. }
  39. /**
  40. * Gets the configuration object when needed.
  41. *
  42. * Since this service is injected into all static menu link objects, but
  43. * only used when updating one, avoid actually loading the config when it's
  44. * not needed.
  45. */
  46. protected function getConfig() {
  47. if (empty($this->config)) {
  48. // Get an override free and editable configuration object.
  49. $this->config = $this->configFactory->getEditable($this->configName);
  50. }
  51. return $this->config;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public function reload() {
  57. $this->config = NULL;
  58. $this->configFactory->reset($this->configName);
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function loadOverride($id) {
  64. $all_overrides = $this->getConfig()->get('definitions');
  65. $id = static::encodeId($id);
  66. return $id && isset($all_overrides[$id]) ? $all_overrides[$id] : [];
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public function deleteMultipleOverrides(array $ids) {
  72. $all_overrides = $this->getConfig()->get('definitions');
  73. $save = FALSE;
  74. foreach ($ids as $id) {
  75. $id = static::encodeId($id);
  76. if (isset($all_overrides[$id])) {
  77. unset($all_overrides[$id]);
  78. $save = TRUE;
  79. }
  80. }
  81. if ($save) {
  82. $this->getConfig()->set('definitions', $all_overrides)->save();
  83. }
  84. return $save;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function deleteOverride($id) {
  90. return $this->deleteMultipleOverrides([$id]);
  91. }
  92. /**
  93. * {@inheritdoc}
  94. */
  95. public function loadMultipleOverrides(array $ids) {
  96. $result = [];
  97. if ($ids) {
  98. $all_overrides = $this->getConfig()->get('definitions') ?: [];
  99. foreach ($ids as $id) {
  100. $encoded_id = static::encodeId($id);
  101. if (isset($all_overrides[$encoded_id])) {
  102. $result[$id] = $all_overrides[$encoded_id];
  103. }
  104. }
  105. }
  106. return $result;
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function saveOverride($id, array $definition) {
  112. // Only allow to override a specific subset of the keys.
  113. $expected = [
  114. 'menu_name' => '',
  115. 'parent' => '',
  116. 'weight' => 0,
  117. 'expanded' => FALSE,
  118. 'enabled' => FALSE,
  119. ];
  120. // Filter the overrides to only those that are expected.
  121. $definition = array_intersect_key($definition, $expected);
  122. // Ensure all values are set.
  123. $definition = $definition + $expected;
  124. if ($definition) {
  125. // Cast keys to avoid config schema during save.
  126. $definition['menu_name'] = (string) $definition['menu_name'];
  127. $definition['parent'] = (string) $definition['parent'];
  128. $definition['weight'] = (int) $definition['weight'];
  129. $definition['expanded'] = (bool) $definition['expanded'];
  130. $definition['enabled'] = (bool) $definition['enabled'];
  131. $id = static::encodeId($id);
  132. $all_overrides = $this->getConfig()->get('definitions');
  133. // Combine with any existing data.
  134. $all_overrides[$id] = $definition + $this->loadOverride($id);
  135. $this->getConfig()->set('definitions', $all_overrides)->save(TRUE);
  136. }
  137. return array_keys($definition);
  138. }
  139. /**
  140. * {@inheritdoc}
  141. */
  142. public function getCacheTags() {
  143. return $this->getConfig()->getCacheTags();
  144. }
  145. /**
  146. * Encodes the ID by replacing dots with double underscores.
  147. *
  148. * This is done because config schema uses dots for its internal type
  149. * hierarchy. Double underscores are converted to triple underscores to
  150. * avoid accidental conflicts.
  151. *
  152. * @param string $id
  153. * The menu plugin ID.
  154. *
  155. * @return string
  156. * The menu plugin ID with double underscore instead of dots.
  157. */
  158. protected static function encodeId($id) {
  159. return strtr($id, ['.' => '__', '__' => '___']);
  160. }
  161. }