‪TYPO3CMS  9.5
PopulatePageSlugs.php
Go to the documentation of this file.
1 <?php
2 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 
24 
37 {
38  protected ‪$table = 'pages';
39 
40  protected ‪$fieldName = 'slug';
41 
45  public function ‪getIdentifier(): string
46  {
47  return 'pagesSlugs';
48  }
49 
53  public function ‪getTitle(): string
54  {
55  return 'Introduce URL parts ("slugs") to all existing pages';
56  }
57 
61  public function ‪getDescription(): string
62  {
63  return 'TYPO3 includes native URL handling. Every page record has its own speaking URL path'
64  . ' called "slug" which can be edited in TYPO3 Backend. However, it is necessary that all pages have'
65  . ' a URL pre-filled. This is done by evaluating the page title / navigation title and all of its rootline.';
66  }
67 
73  public function ‪updateNecessary(): bool
74  {
75  $updateNeeded = false;
76  // Check if the database table even exists
77  if ($this->‪checkIfWizardIsRequired()) {
78  $updateNeeded = true;
79  }
80  return $updateNeeded;
81  }
82 
86  public function ‪getPrerequisites(): array
87  {
88  return [
89  DatabaseUpdatedPrerequisite::class
90  ];
91  }
92 
98  public function ‪executeUpdate(): bool
99  {
100  $this->‪populateSlugs();
101  return true;
102  }
103 
108  protected function ‪populateSlugs()
109  {
110  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
111  $queryBuilder = $connection->createQueryBuilder();
112  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
113  $statement = $queryBuilder
114  ->select('*')
115  ->from($this->table)
116  ->where(
117  $queryBuilder->expr()->orX(
118  $queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
119  $queryBuilder->expr()->isNull($this->fieldName)
120  )
121  )
122  // Ensure that fields with alias are managed first
123  ->orderBy('alias', 'desc')
124  // Ensure that live workspace records are handled first
125  ->addOrderBy('t3ver_wsid', 'asc')
126  // Ensure that all pages are run through "per parent page" field, and in the correct sorting values
127  ->addOrderBy('pid', 'asc')
128  ->addOrderBy('sorting', 'asc')
129  ->execute();
130 
131  // Check for existing slugs from realurl
132  $suggestedSlugs = [];
133  if ($this->‪checkIfTableExists('tx_realurl_pathdata')) {
134  $suggestedSlugs = $this->‪getSuggestedSlugs('tx_realurl_pathdata');
135  } elseif ($this->‪checkIfTableExists('tx_realurl_pathcache')) {
136  $suggestedSlugs = $this->‪getSuggestedSlugs('tx_realurl_pathcache', 'cache_id');
137  }
138 
139  $fieldConfig = ‪$GLOBALS['TCA'][‪$this->table]['columns'][‪$this->fieldName]['config'];
140  $fieldConfig['generatorOptions']['fields'] = ['tx_realurl_pathsegment,title'];
141  $evalInfo = !empty($fieldConfig['eval']) ? GeneralUtility::trimExplode(',', $fieldConfig['eval'], true) : [];
142  $hasToBeUniqueInSite = in_array('uniqueInSite', $evalInfo, true);
143  $hasToBeUniqueInPid = in_array('uniqueInPid', $evalInfo, true);
144  $slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $this->table, $this->fieldName, $fieldConfig);
145  while ($record = $statement->fetch()) {
146  $recordId = (int)$record['uid'];
147  $pid = (int)$record['pid'];
148  $languageId = (int)$record['sys_language_uid'];
149  $pageIdInDefaultLanguage = $languageId > 0 ? (int)$record['l10n_parent'] : $recordId;
150  $slug = $suggestedSlugs[$pageIdInDefaultLanguage][$languageId] ?? '';
151 
152  // see if an alias field was used, then let's build a slug out of that.
153  if (!empty($record['alias'])) {
154  $slug = $slugHelper->sanitize('/' . $record['alias']);
155  }
156 
157  if (empty($slug)) {
158  if ($pid === -1) {
159  $queryBuilder = $connection->createQueryBuilder();
160  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
161  $liveVersion = $queryBuilder
162  ->select('pid')
163  ->from('pages')
164  ->where(
165  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($record['t3ver_oid'], \PDO::PARAM_INT))
166  )->execute()->fetch();
167  $pid = (int)$liveVersion['pid'];
168  }
169  $slug = $slugHelper->generate($record, $pid);
170  }
171 
172  $state = ‪RecordStateFactory::forName($this->table)
173  ->fromArray($record, $pid, $recordId);
174  if ($hasToBeUniqueInSite && !$slugHelper->isUniqueInSite($slug, $state)) {
175  $slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
176  }
177  if ($hasToBeUniqueInPid && !$slugHelper->isUniqueInPid($slug, $state)) {
178  $slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
179  }
180 
181  $connection->update(
182  $this->table,
183  [$this->fieldName => $slug],
184  ['uid' => $recordId]
185  );
186  }
187  }
188 
195  protected function ‪checkIfWizardIsRequired(): bool
196  {
197  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
198  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
199  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
200 
201  $numberOfEntries = $queryBuilder
202  ->count('uid')
203  ->from($this->table)
204  ->where(
205  $queryBuilder->expr()->orX(
206  $queryBuilder->expr()->eq($this->fieldName, $queryBuilder->createNamedParameter('')),
207  $queryBuilder->expr()->isNull($this->fieldName)
208  )
209  )
210  ->execute()
211  ->fetchColumn();
212  return $numberOfEntries > 0;
213  }
214 
222  protected function ‪getSuggestedSlugs(string $tableName, string $identityField = 'uid'): array
223  {
224  $context = GeneralUtility::makeInstance(Context::class);
225  $currentTimestamp = $context->getPropertyFromAspect('date', 'timestamp');
226 
227  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
228  $statement = $queryBuilder
229  ->select('*')
230  ->from($tableName)
231  ->where(
232  $queryBuilder->expr()->eq('mpvar', $queryBuilder->createNamedParameter('')),
233  $queryBuilder->expr()->orX(
234  $queryBuilder->expr()->eq('expire', $queryBuilder->createNamedParameter(0)),
235  $queryBuilder->expr()->gt('expire', $queryBuilder->createNamedParameter($currentTimestamp))
236  )
237  )
238  ->orderBy('expire', 'ASC')
239  ->execute();
240  $suggestedSlugs = [];
241  while ($row = $statement->fetch()) {
242  // rawurldecode ensures that non-ASCII arguments are also migrated
243  $pagePath = rawurldecode($row['pagepath']);
244  if (!isset($suggestedSlugs[(int)$row['page_id']][(int)$row['language_id']])) { // keep only first result
245  $suggestedSlugs[(int)$row['page_id']][(int)$row['language_id']] = '/' . trim($pagePath, '/');
246  }
247  }
248  return $suggestedSlugs;
249  }
250 
257  protected function ‪checkIfTableExists(‪$table)
258  {
259  $tableExists = GeneralUtility::makeInstance(ConnectionPool::class)
260  ->getConnectionForTable(‪$table)
261  ->getSchemaManager()
262  ->tablesExist([‪$table]);
263 
264  return $tableExists;
265  }
266 }
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getIdentifier
‪string getIdentifier()
Definition: PopulatePageSlugs.php:45
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs
Definition: PopulatePageSlugs.php:37
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\executeUpdate
‪bool executeUpdate()
Definition: PopulatePageSlugs.php:98
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:49
‪TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory
Definition: RecordStateFactory.php:25
‪TYPO3\CMS\Install\Updates
Definition: AbstractDownloadExtensionUpdate.php:3
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getSuggestedSlugs
‪array getSuggestedSlugs(string $tableName, string $identityField='uid')
Definition: PopulatePageSlugs.php:222
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\checkIfWizardIsRequired
‪bool checkIfWizardIsRequired()
Definition: PopulatePageSlugs.php:195
‪TYPO3\CMS\Core\DataHandling\SlugHelper
Definition: SlugHelper.php:36
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\$table
‪$table
Definition: PopulatePageSlugs.php:38
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\$fieldName
‪$fieldName
Definition: PopulatePageSlugs.php:40
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getTitle
‪string getTitle()
Definition: PopulatePageSlugs.php:53
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\updateNecessary
‪bool updateNecessary()
Definition: PopulatePageSlugs.php:73
‪TYPO3\CMS\Install\Updates\UpgradeWizardInterface
Definition: UpgradeWizardInterface.php:22
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\populateSlugs
‪populateSlugs()
Definition: PopulatePageSlugs.php:108
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:26
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\checkIfTableExists
‪bool checkIfTableExists($table)
Definition: PopulatePageSlugs.php:257
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getDescription
‪string getDescription()
Definition: PopulatePageSlugs.php:61
‪TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory\forName
‪static static forName(string $name)
Definition: RecordStateFactory.php:34
‪TYPO3\CMS\Install\Updates\PopulatePageSlugs\getPrerequisites
‪string[] getPrerequisites()
Definition: PopulatePageSlugs.php:86