‪TYPO3CMS  ‪main
SiteWriter.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Symfony\Component\Yaml\Yaml;
32 
39 {
45  protected string ‪$configFileName = 'config.yaml';
46 
53  protected string ‪$settingsFileName = 'settings.yaml';
54 
60  protected string ‪$cacheIdentifier = 'sites-configuration';
61 
62  public function ‪__construct(
63  protected string $configPath,
64  protected EventDispatcherInterface $eventDispatcher,
65  protected ‪PhpFrontend $cache
66  ) {}
67 
73  public function ‪createNewBasicSite(string ‪$identifier, int $rootPageId, string $base): void
74  {
75  // Create a default site configuration called "main" as best practice
76  $this->‪write($identifier, [
77  'rootPageId' => $rootPageId,
78  'base' => $base,
79  'languages' => [
80  0 => [
81  'title' => 'English',
82  'enabled' => true,
83  'languageId' => 0,
84  'base' => '/',
85  'locale' => 'en_US.UTF-8',
86  'navigationTitle' => 'English',
87  'flag' => 'us',
88  ],
89  ],
90  'errorHandling' => [],
91  'routes' => [],
92  ]);
93  }
94 
95  public function ‪writeSettings(string ‪$siteIdentifier, array $settings): void
96  {
97  $fileName = $this->configPath . '/' . ‪$siteIdentifier . '/' . ‪$this->settingsFileName;
98  $yamlFileContents = Yaml::dump($settings, 99, 2);
99  if (!‪GeneralUtility::writeFile($fileName, $yamlFileContents)) {
100  throw new ‪SiteConfigurationWriteException('Unable to write site settings in sites/' . ‪$siteIdentifier . '/' . $this->configFileName, 1590487411);
101  }
102  }
103 
111  public function ‪write(string ‪$siteIdentifier, array $configuration, bool $protectPlaceholders = false): void
112  {
113  $folder = $this->configPath . '/' . ‪$siteIdentifier;
114  $fileName = $folder . '/' . ‪$this->configFileName;
115  $newConfiguration = $configuration;
116  if (!file_exists($folder)) {
118  if ($protectPlaceholders && $newConfiguration !== []) {
119  $newConfiguration = $this->protectPlaceholders([], $newConfiguration);
120  }
121  } elseif (file_exists($fileName)) {
122  $loader = GeneralUtility::makeInstance(YamlFileLoader::class);
123  // load without any processing to have the unprocessed base to modify
124  $newConfiguration = $loader->load(GeneralUtility::fixWindowsFilePath($fileName), 0);
125  // load the processed configuration to diff changed values
126  $processed = $loader->load(GeneralUtility::fixWindowsFilePath($fileName));
127  // find properties that were modified via GUI
128  $newModified = array_replace_recursive(
129  self::findRemoved($processed, $configuration),
130  self::findModified($processed, $configuration)
131  );
132  if ($protectPlaceholders && $newModified !== []) {
133  $newModified = $this->protectPlaceholders($newConfiguration, $newModified);
134  }
135  // change _only_ the modified keys, leave the original non-changed areas alone
136  ArrayUtility::mergeRecursiveWithOverrule($newConfiguration, $newModified);
137  }
138  $event = $this->eventDispatcher->dispatch(new ‪SiteConfigurationBeforeWriteEvent(‪$siteIdentifier, $newConfiguration));
139  $newConfiguration = $this->‪sortConfiguration($event->getConfiguration());
140  $yamlFileContents = Yaml::dump($newConfiguration, 99, 2);
141  if (!‪GeneralUtility::writeFile($fileName, $yamlFileContents)) {
142  throw new ‪SiteConfigurationWriteException('Unable to write site configuration in sites/' . ‪$siteIdentifier . '/' . $this->configFileName, 1590487011);
143  }
144  $this->cache->remove($this->cacheIdentifier);
145  $this->eventDispatcher->dispatch(new ‪SiteConfigurationChangedEvent(‪$siteIdentifier));
146  }
147 
153  public function ‪rename(string $currentIdentifier, string $newIdentifier): void
154  {
155  if (!‪rename($this->configPath . '/' . $currentIdentifier, $this->configPath . '/' . $newIdentifier)) {
156  throw new ‪SiteConfigurationWriteException('Unable to rename folder sites/' . $currentIdentifier, 1522491300);
157  }
158  $this->cache->remove($this->cacheIdentifier);
159  $this->eventDispatcher->dispatch(new ‪SiteConfigurationChangedEvent($newIdentifier));
160  }
161 
168  public function delete(string ‪$siteIdentifier): void
169  {
170  $fileName = $this->configPath . '/' . ‪$siteIdentifier . '/' . ‪$this->configFileName;
171  if (!file_exists($fileName)) {
172  throw new ‪SiteNotFoundException('Site configuration file ' . $this->configFileName . ' within the site ' . ‪$siteIdentifier . ' not found.', 1522866184);
173  }
174  if (!unlink($fileName)) {
175  throw new ‪SiteConfigurationWriteException('Unable to delete folder sites/' . ‪$siteIdentifier, 1596462020);
176  }
177  $this->cache->remove($this->cacheIdentifier);
178  $this->eventDispatcher->dispatch(new ‪SiteConfigurationChangedEvent(‪$siteIdentifier));
179  }
180 
190  protected function protectPlaceholders(array $existingConfiguration, array $modifiedConfiguration): array
191  {
192  try {
193  return GeneralUtility::makeInstance(YamlPlaceholderGuard::class, $existingConfiguration)
194  ->process($modifiedConfiguration);
195  } catch (‪YamlPlaceholderException $exception) {
196  throw new ‪SiteConfigurationWriteException($exception->getMessage(), 1670361271, $exception);
197  }
198  }
199 
200  protected function ‪sortConfiguration(array $newConfiguration): array
201  {
202  ksort($newConfiguration);
203  if (isset($newConfiguration['imports'])) {
204  $imports = $newConfiguration['imports'];
205  unset($newConfiguration['imports']);
206  $newConfiguration['imports'] = $imports;
207  }
208  return $newConfiguration;
209  }
210 
211  protected static function ‪findModified(array $currentConfiguration, array $newConfiguration): array
212  {
213  $differences = [];
214  foreach ($newConfiguration as $key => $value) {
215  if (!isset($currentConfiguration[$key]) || $currentConfiguration[$key] !== $newConfiguration[$key]) {
216  if (!isset($newConfiguration[$key]) && isset($currentConfiguration[$key])) {
217  $differences[$key] = '__UNSET';
218  } elseif (isset($currentConfiguration[$key])
219  && is_array($newConfiguration[$key])
220  && is_array($currentConfiguration[$key])
221  ) {
222  $differences[$key] = ‪self::findModified($currentConfiguration[$key], $newConfiguration[$key]);
223  } else {
224  $differences[$key] = $value;
225  }
226  }
227  }
228  return $differences;
229  }
230 
231  protected static function ‪findRemoved(array $currentConfiguration, array $newConfiguration): array
232  {
233  $removed = [];
234  foreach ($currentConfiguration as $key => $value) {
235  if (!isset($newConfiguration[$key])) {
236  $removed[$key] = '__UNSET';
237  } elseif (isset($currentConfiguration[$key]) && is_array($currentConfiguration[$key]) && is_array($newConfiguration[$key])) {
238  $removedInRecursion = ‪self::findRemoved($currentConfiguration[$key], $newConfiguration[$key]);
239  if (!empty($removedInRecursion)) {
240  $removed[$key] = $removedInRecursion;
241  }
242  }
243  }
244 
245  return $removed;
246  }
247 }
‪TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException
Definition: SiteConfigurationWriteException.php:27
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Configuration\SiteWriter\$cacheIdentifier
‪string $cacheIdentifier
Definition: SiteWriter.php:60
‪TYPO3\CMS\Core\Configuration\SiteWriter\$settingsFileName
‪string $settingsFileName
Definition: SiteWriter.php:53
‪TYPO3\CMS\Core\Configuration\SiteWriter\sortConfiguration
‪catch(YamlPlaceholderException $exception) sortConfiguration(array $newConfiguration)
Definition: SiteWriter.php:200
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep(string $directory)
Definition: GeneralUtility.php:1654
‪TYPO3\CMS\Core\Configuration\Loader\Exception\YamlPlaceholderException
Definition: YamlPlaceholderException.php:20
‪TYPO3\CMS\Core\Configuration\SiteWriter\__construct
‪__construct(protected string $configPath, protected EventDispatcherInterface $eventDispatcher, protected PhpFrontend $cache)
Definition: SiteWriter.php:62
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile(string $file, string $content, bool $changePermissions=false)
Definition: GeneralUtility.php:1469
‪TYPO3\CMS\Core\Configuration\Event\SiteConfigurationChangedEvent
Definition: SiteConfigurationChangedEvent.php:24
‪TYPO3\CMS\Core\Configuration\SiteWriter
Definition: SiteWriter.php:39
‪TYPO3\CMS\Core\Configuration\SiteWriter\findRemoved
‪static findRemoved(array $currentConfiguration, array $newConfiguration)
Definition: SiteWriter.php:231
‪TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader
Definition: YamlFileLoader.php:47
‪TYPO3\CMS\Core\Configuration\Event\SiteConfigurationBeforeWriteEvent
Definition: SiteConfigurationBeforeWriteEvent.php:25
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\Configuration
Definition: CKEditor5Migrator.php:18
‪TYPO3\CMS\Core\Configuration\SiteWriter\$configFileName
‪string $configFileName
Definition: SiteWriter.php:45
‪TYPO3\CMS\Core\Configuration\SiteWriter\findModified
‪static findModified(array $currentConfiguration, array $newConfiguration)
Definition: SiteWriter.php:211
‪TYPO3\CMS\Core\Configuration\SiteWriter\writeSettings
‪writeSettings(string $siteIdentifier, array $settings)
Definition: SiteWriter.php:95
‪TYPO3\CMS\Webhooks\Message\$siteIdentifier
‪identifier readonly int readonly array readonly string readonly string $siteIdentifier
Definition: PageModificationMessage.php:38
‪TYPO3\CMS\Core\Configuration\SiteWriter\rename
‪rename(string $currentIdentifier, string $newIdentifier)
Definition: SiteWriter.php:153
‪TYPO3\CMS\Core\Configuration\SiteWriter\write
‪write(string $siteIdentifier, array $configuration, bool $protectPlaceholders=false)
Definition: SiteWriter.php:111
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Configuration\Loader\YamlPlaceholderGuard
Definition: YamlPlaceholderGuard.php:27
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Configuration\SiteWriter\createNewBasicSite
‪createNewBasicSite(string $identifier, int $rootPageId, string $base)
Definition: SiteWriter.php:73