‪TYPO3CMS  10.4
PopulatePageSlugs.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 
26 
38 {
42  protected ‪$table = 'pages';
43 
47  protected ‪$fieldName = 'slug';
48 
52  public function ‪getIdentifier(): string
53  {
54  return 'pagesSlugs';
55  }
56 
60  public function ‪getTitle(): string
61  {
62  return 'Introduce URL parts ("slugs") to all existing pages';
63  }
64 
68  public function ‪getDescription(): string
69  {
70  return 'TYPO3 includes native URL handling. Every page record has its own speaking URL path'
71  . ' called "slug" which can be edited in TYPO3 Backend. However, it is necessary that all pages have'
72  . ' a URL pre-filled. This is done by evaluating the page title / navigation title and all of its rootline.';
73  }
74 
80  public function ‪updateNecessary(): bool
81  {
82  $updateNeeded = false;
83  // Check if the database table even exists
84  if ($this->‪checkIfWizardIsRequired()) {
85  $updateNeeded = true;
86  }
87  return $updateNeeded;
88  }
89 
93  public function ‪getPrerequisites(): array
94  {
95  return [
96  DatabaseUpdatedPrerequisite::class
97  ];
98  }
99 
105  public function ‪executeUpdate(): bool
106  {
107  $this->‪populateSlugs();
108  return true;
109  }
110 
115  protected function ‪populateSlugs()
116  {
117  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
118  $queryBuilder = $connection->createQueryBuilder();
119  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
120  $statement = $queryBuilder
121  ->select('*')
122  ->from($this->table)
123  ->where(
124  $queryBuilder->expr()->orX(
125  $queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
126  $queryBuilder->expr()->isNull($this->fieldName)
127  )
128  )
129  // Ensure that live workspace records are handled first
130  ->addOrderBy('t3ver_wsid', 'asc')
131  // Ensure that all pages are run through "per parent page" field, and in the correct sorting values
132  ->addOrderBy('pid', 'asc')
133  ->addOrderBy('sorting', 'asc')
134  ->execute();
135 
136  // Check for existing slugs from realurl
137  $suggestedSlugs = [];
138  if ($this->‪checkIfTableExists('tx_realurl_pathdata')) {
139  $suggestedSlugs = $this->‪getSuggestedSlugs('tx_realurl_pathdata');
140  } elseif ($this->‪checkIfTableExists('tx_realurl_pathcache')) {
141  $suggestedSlugs = $this->‪getSuggestedSlugs('tx_realurl_pathcache', 'cache_id');
142  }
143 
144  $fieldConfig = $this->‪getSlugFieldConfig();
145  $evalInfo = !empty($fieldConfig['eval']) ? ‪GeneralUtility::trimExplode(',', $fieldConfig['eval'], true) : [];
146  $hasToBeUniqueInSite = in_array('uniqueInSite', $evalInfo, true);
147  $hasToBeUniqueInPid = in_array('uniqueInPid', $evalInfo, true);
148  $slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $this->table, $this->fieldName, $fieldConfig);
149  while ($record = $statement->fetch()) {
150  $recordId = (int)$record['uid'];
151  $pid = (int)$record['pid'];
152  $languageId = (int)$record['sys_language_uid'];
153  $pageIdInDefaultLanguage = $languageId > 0 ? (int)$record['l10n_parent'] : $recordId;
154  $slug = $suggestedSlugs[$pageIdInDefaultLanguage][$languageId] ?? '';
155 
156  // see if an alias field was used, then let's build a slug out of that. This field does not exist in v10
157  // anymore, so this will only be necessary in edge-cases when upgrading from earlier versions with aliases
158  if (!empty($record['alias'])) {
159  $slug = $slugHelper->sanitize('/' . $record['alias']);
160  }
161 
162  if (empty($slug)) {
163  // Resolve the live "pid"
164  if ($record['t3ver_oid'] > 0) {
165  $queryBuilder = $connection->createQueryBuilder();
166  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
167  $liveVersion = $queryBuilder
168  ->select('pid')
169  ->from('pages')
170  ->where(
171  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($record['t3ver_oid'], \PDO::PARAM_INT))
172  )->execute()->fetch();
173  $pid = (int)$liveVersion['pid'];
174  }
175  $slug = $slugHelper->generate($record, $pid);
176  }
177 
178  $state = ‪RecordStateFactory::forName($this->table)
179  ->fromArray($record, $pid, $recordId);
180  if ($hasToBeUniqueInSite && !$slugHelper->isUniqueInSite($slug, $state)) {
181  $slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
182  }
183  if ($hasToBeUniqueInPid && !$slugHelper->isUniqueInPid($slug, $state)) {
184  $slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
185  }
186 
187  $connection->update(
188  $this->table,
189  [$this->fieldName => $slug],
190  ['uid' => $recordId]
191  );
192  }
193  }
194 
201  protected function ‪checkIfWizardIsRequired(): bool
202  {
203  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
204  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
205  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
206 
207  $numberOfEntries = $queryBuilder
208  ->count('uid')
209  ->from($this->table)
210  ->where(
211  $queryBuilder->expr()->orX(
212  $queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
213  $queryBuilder->expr()->isNull($this->fieldName)
214  )
215  )
216  ->execute()
217  ->fetchColumn();
218  return $numberOfEntries > 0;
219  }
220 
228  protected function ‪getSuggestedSlugs(string $tableName, string $identityField = 'uid'): array
229  {
230  $context = GeneralUtility::makeInstance(Context::class);
231  $currentTimestamp = $context->getPropertyFromAspect('date', 'timestamp');
232 
233  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
234  $statement = $queryBuilder
235  ->select('*')
236  ->from($tableName)
237  ->where(
238  $queryBuilder->expr()->eq('mpvar', $queryBuilder->createNamedParameter('')),
239  $queryBuilder->expr()->orX(
240  $queryBuilder->expr()->eq('expire', $queryBuilder->createNamedParameter(0)),
241  $queryBuilder->expr()->gt('expire', $queryBuilder->createNamedParameter($currentTimestamp))
242  )
243  )
244  ->orderBy('expire', 'ASC')
245  ->execute();
246  $suggestedSlugs = [];
247  while ($row = $statement->fetch()) {
248  // rawurldecode ensures that non-ASCII arguments are also migrated
249  $pagePath = rawurldecode($row['pagepath']);
250  if (!isset($suggestedSlugs[(int)$row['page_id']][(int)$row['language_id']])) { // keep only first result
251  $suggestedSlugs[(int)$row['page_id']][(int)$row['language_id']] = '/' . trim($pagePath, '/');
252  }
253  }
254  return $suggestedSlugs;
255  }
256 
263  protected function ‪checkIfTableExists(‪$table)
264  {
265  $tableExists = GeneralUtility::makeInstance(ConnectionPool::class)
266  ->getConnectionForTable(‪$table)
267  ->getSchemaManager()
268  ->tablesExist([‪$table]);
269 
270  return $tableExists;
271  }
272 
279  protected function ‪getSlugFieldConfig(): array
280  {
281  $fieldConfig = ‪$GLOBALS['TCA'][‪$this->table]['columns'][‪$this->fieldName]['config'];
282 
283  // Add the EXT:realurl specific field to generatorOptions
284  $fieldConfig['generatorOptions']['fields'] = ['tx_realurl_pathsegment,title'];
285 
286  return $fieldConfig;
287  }
288 }
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getIdentifier
‪string getIdentifier()
Definition: PopulatePageSlugs.php:50
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getSlugFieldConfig
‪array getSlugFieldConfig()
Definition: PopulatePageSlugs.php:277
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs
Definition: PopulatePageSlugs.php:38
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\executeUpdate
‪bool executeUpdate()
Definition: PopulatePageSlugs.php:103
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory
Definition: RecordStateFactory.php:26
‪TYPO3\CMS\Install\Updates
Definition: AbstractDownloadExtensionUpdate.php:16
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\$table
‪string $table
Definition: PopulatePageSlugs.php:41
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getSuggestedSlugs
‪array getSuggestedSlugs(string $tableName, string $identityField='uid')
Definition: PopulatePageSlugs.php:226
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\checkIfWizardIsRequired
‪bool checkIfWizardIsRequired()
Definition: PopulatePageSlugs.php:199
‪TYPO3\CMS\Core\DataHandling\SlugHelper
Definition: SlugHelper.php:42
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\$fieldName
‪string $fieldName
Definition: PopulatePageSlugs.php:45
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getTitle
‪string getTitle()
Definition: PopulatePageSlugs.php:58
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\updateNecessary
‪bool updateNecessary()
Definition: PopulatePageSlugs.php:78
‪TYPO3\CMS\Install\Updates\UpgradeWizardInterface
Definition: UpgradeWizardInterface.php:24
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\populateSlugs
‪populateSlugs()
Definition: PopulatePageSlugs.php:113
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\checkIfTableExists
‪bool checkIfTableExists($table)
Definition: PopulatePageSlugs.php:261
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getDescription
‪string getDescription()
Definition: PopulatePageSlugs.php:66
‪TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory\forName
‪static static forName(string $name)
Definition: RecordStateFactory.php:35
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getPrerequisites
‪string[] getPrerequisites()
Definition: PopulatePageSlugs.php:91