Completed
Push — master ( 5e5725...eff4fd )
by
unknown
40:58 queued 27:03
created

addPageTranslationsToPageList()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 20
nc 6
nop 4
dl 0
loc 37
rs 9.2888
c 1
b 0
f 0
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
18
namespace TYPO3\CMS\Linkvalidator\Result;
19
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
23
use TYPO3\CMS\Core\Localization\LanguageService;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Linkvalidator\LinkAnalyzer;
26
use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository;
27
use TYPO3\CMS\Linkvalidator\Repository\PagesRepository;
28
29
/**
30
 * Used to work with LinkAnalyzer results
31
 *
32
 * @internal
33
 */
34
class LinkAnalyzerResult
35
{
36
    /**
37
     * @var LinkAnalyzer
38
     */
39
    protected $linkAnalyzer;
40
41
    /**
42
     * @var BrokenLinkRepository
43
     */
44
    protected $brokenLinkRepository;
45
46
    /**
47
     * @var PagesRepository
48
     */
49
    protected $pagesRepository;
50
51
    /**
52
     * @var ConnectionPool
53
     */
54
    protected $connectionPool;
55
56
    /**
57
     * @var array
58
     */
59
    protected $brokenLinks = [];
60
61
    /**
62
     * @var array
63
     */
64
    protected $newBrokenLinkCounts = [];
65
66
    /**
67
     * @var array
68
     */
69
    protected $oldBrokenLinkCounts = [];
70
71
    /**
72
     * @var bool
73
     */
74
    protected $differentToLastResult = false;
75
76
    /**
77
     * Save language codes to reduce database requests
78
     *
79
     * @var array<int, string>
80
     */
81
    protected $languageCodes = ['default'];
82
83
    /**
84
     * Save localized pages to reduce database requests
85
     *
86
     * @var array<string, int>
87
     */
88
    protected $localizedPages = [];
89
90
    public function __construct(
91
        LinkAnalyzer $linkAnalyzer,
92
        BrokenLinkRepository $brokenLinkRepository,
93
        ConnectionPool $connectionPool,
94
        PagesRepository $pagesRepository
95
    ) {
96
        $this->linkAnalyzer = $linkAnalyzer;
97
        $this->brokenLinkRepository = $brokenLinkRepository;
98
        $this->connectionPool = $connectionPool;
99
        $this->pagesRepository = $pagesRepository;
100
    }
101
102
    /**
103
     * Call LinkAnalyzer with provided task configuration and process result values
104
     *
105
     * @param int    $page
106
     * @param int    $depth
107
     * @param array  $pageRow
108
     * @param array  $modTSconfig
109
     * @param array  $searchFields
110
     * @param array  $linkTypes
111
     * @param string $languages
112
     *
113
     * @return $this
114
     */
115
    public function getResultForTask(
116
        int $page,
117
        int $depth,
118
        array $pageRow,
119
        array $modTSconfig,
120
        array $searchFields = [],
121
        array $linkTypes = [],
122
        string $languages = ''
123
    ): self {
124
        $rootLineHidden = $this->pagesRepository->doesRootLineContainHiddenPages($pageRow);
125
        $checkHidden = $modTSconfig['checkhidden'] === 1;
126
127
        if ($rootLineHidden && !$checkHidden) {
128
            return $this;
129
        }
130
131
        $pageIds = $this->pagesRepository->getAllSubpagesForPage(
132
            $page,
133
            $depth,
134
            '',
135
            $checkHidden
136
        );
137
138
        if ($pageRow['hidden'] === 0 || $checkHidden) {
139
            $pageIds[] = $page;
140
        }
141
142
        if (empty($pageIds)) {
143
            return $this;
144
        }
145
146
        $languageIds = GeneralUtility::intExplode(',', $languages, true);
147
        $pageTranslations = $this->pagesRepository->getTranslationForPage(
148
            $page,
149
            '',
150
            $checkHidden,
151
            $languageIds
152
        );
153
154
        $pageIds = array_merge($pageIds, $pageTranslations);
155
156
        $this->linkAnalyzer->init($searchFields, implode(',', $pageIds), $modTSconfig);
157
        $this->oldBrokenLinkCounts = $this->linkAnalyzer->getLinkCounts();
158
159
        $this->linkAnalyzer->getLinkStatistics($linkTypes, $checkHidden);
160
        $this->newBrokenLinkCounts = $this->linkAnalyzer->getLinkCounts();
161
162
        $this->brokenLinks = $this->brokenLinkRepository->getAllBrokenLinksForPages(
163
            $pageIds,
164
            array_keys($linkTypes),
165
            $searchFields,
166
            $languageIds
167
        );
168
169
        $this
170
            ->processLinkCounts($linkTypes)
171
            ->processBrokenLinks();
172
173
        return $this;
174
    }
175
176
    public function setBrokenLinks(array $brokenLinks): void
177
    {
178
        $this->brokenLinks = $brokenLinks;
179
    }
180
181
    public function getBrokenLinks(): array
182
    {
183
        return $this->brokenLinks;
184
    }
185
186
    public function setNewBrokenLinkCounts(array $newBrokenLinkCounts): void
187
    {
188
        $this->newBrokenLinkCounts = $newBrokenLinkCounts;
189
    }
190
191
    public function getNewBrokenLinkCounts(): array
192
    {
193
        return $this->newBrokenLinkCounts;
194
    }
195
196
    public function setOldBrokenLinkCounts(array $oldBrokenLinkCounts): void
197
    {
198
        $this->oldBrokenLinkCounts = $oldBrokenLinkCounts;
199
    }
200
201
    public function getOldBrokenLinkCounts(): array
202
    {
203
        return $this->oldBrokenLinkCounts;
204
    }
205
206
    public function getTotalBrokenLinksCount(): int
207
    {
208
        return $this->newBrokenLinkCounts['total'] ?? 0;
209
    }
210
211
    public function isDifferentToLastResult(): bool
212
    {
213
        return $this->differentToLastResult;
214
    }
215
216
    /**
217
     * Process the link counts (old and new) and ensures that all link types are available in the array
218
     *
219
     * @param array<int, string> $linkTypes list of link types
220
     * @return LinkAnalyzerResult
221
     */
222
    protected function processLinkCounts(array $linkTypes): self
223
    {
224
        foreach (array_keys($linkTypes) as $linkType) {
225
            if (!isset($this->newBrokenLinkCounts[$linkType])) {
226
                $this->newBrokenLinkCounts[$linkType] = 0;
227
            }
228
            if (!isset($this->oldBrokenLinkCounts[$linkType])) {
229
                $this->oldBrokenLinkCounts[$linkType] = 0;
230
            }
231
            if ($this->newBrokenLinkCounts[$linkType] !== $this->oldBrokenLinkCounts[$linkType]) {
232
                $this->differentToLastResult = true;
233
            }
234
        }
235
236
        return $this;
237
    }
238
239
    /**
240
     * Process broken link values and assign them to new variables which are used in the templates
241
     * shipped by the core but can also be used in custom templates. The raw data is untouched and
242
     * can also still be used in custom templates.
243
     *
244
     * @return LinkAnalyzerResult
245
     */
246
    protected function processBrokenLinks(): self
247
    {
248
        foreach ($this->brokenLinks as $key => &$brokenLink) {
249
            $fullRecord = BackendUtility::getRecord($brokenLink['table_name'], $brokenLink['record_uid']);
250
251
            if ($fullRecord !== null) {
252
                $brokenLink['full_record'] = $fullRecord;
253
                $brokenLink['record_title'] = BackendUtility::getRecordTitle($brokenLink['table_name'], $fullRecord);
254
            }
255
256
            $brokenLink['real_pid'] = ((int)($brokenLink['language'] ?? 0) > 0 && (string)($brokenLink['table_name'] ?? '') !== 'pages')
257
                ? $this->getLocalizedPageId((int)$brokenLink['record_pid'], (int)$brokenLink['language'])
258
                : $brokenLink['record_pid'];
259
            $pageRecord = BackendUtility::getRecord('pages', $brokenLink['real_pid']);
260
261
            if ($pageRecord !== null) {
262
                $brokenLink['page_record'] = $pageRecord;
263
            }
264
265
            $brokenLink['record_type'] = $this->getLanguageService()->sL($GLOBALS['TCA'][$brokenLink['table_name']]['ctrl']['title'] ?? '');
266
            $brokenLink['target'] = (((string)($brokenLink['link_type'] ?? '') === 'db') ? 'id:' : '') . $brokenLink['url'];
267
            $brokenLink['language_code'] = $this->getLanguageCode((int)$brokenLink['language']);
268
        }
269
270
        return $this;
271
    }
272
273
    /**
274
     * Get language iso code and store it in the local property languageCodes
275
     *
276
     * @param int $languageId
277
     * @return string
278
     */
279
    protected function getLanguageCode(int $languageId): string
280
    {
281
        if ((bool)($this->languageCodes[$languageId] ?? false)) {
282
            return $this->languageCodes[$languageId];
283
        }
284
285
        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_language');
286
        $queryBuilder
287
            ->getRestrictions()
288
            ->removeAll()
289
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
290
291
        $langauge = $queryBuilder
292
            ->select('uid', 'language_isocode')
293
            ->from('sys_language')
294
            ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)))
295
            ->setMaxResults(1)
296
            ->execute()
297
            ->fetch() ?: [];
298
299
        if (is_array($langauge) && $langauge !== []) {
300
            $this->languageCodes[(int)$langauge['uid']] = $langauge['language_isocode'];
301
            return $langauge['language_isocode'];
302
        }
303
304
        return '';
305
    }
306
307
    /**
308
     * Get localized page id and store it in the local property localizedPages
309
     *
310
     * @param int $parentId
311
     * @param int $languageId
312
     * @return int
313
     */
314
    protected function getLocalizedPageId(int $parentId, int $languageId): int
315
    {
316
        $identifier = $parentId . '-' . $languageId;
317
318
        if ((bool)($this->localizedPages[$identifier] ?? false)) {
319
            return $this->localizedPages[$identifier];
320
        }
321
322
        $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
323
        $queryBuilder
324
            ->getRestrictions()
325
            ->removeAll()
326
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
327
328
        $localizedPageId = (int)$queryBuilder
329
            ->select('uid')
330
            ->from('pages')
331
            ->where(
332
                $queryBuilder->expr()->eq(
333
                    $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
334
                    $queryBuilder->createNamedParameter($parentId, \PDO::PARAM_INT)
335
                ),
336
                $queryBuilder->expr()->eq(
337
                    $GLOBALS['TCA']['pages']['ctrl']['languageField'],
338
                    $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
339
                )
340
            )
341
            ->setMaxResults(1)
342
            ->execute()
343
            ->fetch()['uid'] ?: 0;
344
345
        if ($localizedPageId) {
346
            $this->localizedPages[$identifier] = $localizedPageId;
347
            return $localizedPageId;
348
        }
349
350
        return $parentId;
351
    }
352
353
    /**
354
     * @return LanguageService
355
     */
356
    protected function getLanguageService(): LanguageService
357
    {
358
        return $GLOBALS['LANG'];
359
    }
360
}
361