Completed
Push — master ( a48ec2...4c6d80 )
by
unknown
13:40
created

BrokenLinkRepository::addBrokenLink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 3
dl 0
loc 10
rs 10
c 0
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\Repository;
19
20
use Doctrine\DBAL\Exception\TableNotFoundException;
21
use TYPO3\CMS\Core\Database\Connection;
22
use TYPO3\CMS\Core\Database\ConnectionPool;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Linkvalidator\QueryRestrictions\EditableRestriction;
25
26
/**
27
 * Repository for finding broken links that were detected previously.
28
 */
29
class BrokenLinkRepository
30
{
31
    protected const TABLE = 'tx_linkvalidator_link';
32
33
    /**
34
     * Check if linkTarget is in list of broken links.
35
     *
36
     * @param string $linkTarget Url to check for. Can be a URL (for external links)
37
     *   a page uid (for db links), a file reference (for file links), etc.
38
     * @return bool is the link target a broken link
39
     */
40
    public function isLinkTargetBrokenLink(string $linkTarget): bool
41
    {
42
        try {
43
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
44
                ->getQueryBuilderForTable(static::TABLE);
45
            $queryBuilder
46
                ->count('uid')
47
                ->from(static::TABLE)
48
                ->where(
49
                    $queryBuilder->expr()->eq('url', $queryBuilder->createNamedParameter($linkTarget))
50
                );
51
            return (bool)$queryBuilder
52
                    ->execute()
53
                    ->fetchColumn(0);
54
        } catch (TableNotFoundException $e) {
55
            return false;
56
        }
57
    }
58
59
    /**
60
     * Returns all broken links found on the page record and all records on a page (or multiple pages)
61
     * grouped by the link_type.
62
     *
63
     * @param array $pageIds
64
     * @param array $searchFields [ table => [field1, field2, ...], ...]
65
     * @return array
66
     */
67
    public function getNumberOfBrokenLinksForRecordsOnPages(array $pageIds, array $searchFields): array
68
    {
69
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
70
            ->getQueryBuilderForTable(static::TABLE);
71
        $queryBuilder->getRestrictions()->removeAll();
72
        if (!$GLOBALS['BE_USER']->isAdmin()) {
73
            $queryBuilder->getRestrictions()
74
                ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder));
75
        }
76
        $statement = $queryBuilder->select('link_type')
77
            ->addSelectLiteral($queryBuilder->expr()->count(static::TABLE . '.uid', 'amount'))
78
            ->from(static::TABLE)
79
            ->join(
80
                static::TABLE,
81
                'pages',
82
                'pages',
83
                $queryBuilder->expr()->eq('record_pid', $queryBuilder->quoteIdentifier('pages.uid'))
84
            )
85
            ->where(
86
                $queryBuilder->expr()->orX(
87
                    $queryBuilder->expr()->andX(
88
                        $queryBuilder->expr()->in(
89
                            'record_uid',
90
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
91
                        ),
92
                        $queryBuilder->expr()->eq('table_name', $queryBuilder->createNamedParameter('pages'))
93
                    ),
94
                    $queryBuilder->expr()->andX(
95
                        $queryBuilder->expr()->in(
96
                            'record_pid',
97
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
98
                        ),
99
                        $queryBuilder->expr()->neq('table_name', $queryBuilder->createNamedParameter('pages'))
100
                    )
101
                )
102
            )
103
            ->groupBy('link_type')
104
            ->execute();
105
106
        $result = [
107
            'total' => 0
108
        ];
109
        while ($row = $statement->fetch()) {
110
            $result[$row['link_type']] = $row['amount'];
111
            $result['total']+= $row['amount'];
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "+="; 0 found
Loading history...
112
        }
113
        return $result;
114
    }
115
116
    public function setNeedsRecheckForRecord(int $recordUid, string $tableName): void
117
    {
118
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
119
            ->getQueryBuilderForTable(static::TABLE);
120
121
        $queryBuilder->update(static::TABLE)
122
            ->where(
123
                $queryBuilder->expr()->eq(
124
                    'record_uid',
125
                    $queryBuilder->createNamedParameter($recordUid, \PDO::PARAM_INT)
126
                ),
127
                $queryBuilder->expr()->eq(
128
                    'table_name',
129
                    $queryBuilder->createNamedParameter($tableName)
130
                )
131
            )
132
            ->set('needs_recheck', 1)
133
            ->execute();
134
    }
135
136
    public function removeBrokenLinksForRecord(string $tableName, int $recordUid): void
137
    {
138
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
139
            ->getQueryBuilderForTable(static::TABLE);
140
141
        $queryBuilder->delete(static::TABLE)
142
            ->where(
143
                $queryBuilder->expr()->eq(
144
                    'record_uid',
145
                    $queryBuilder->createNamedParameter($recordUid, \PDO::PARAM_INT)
146
                ),
147
                $queryBuilder->expr()->eq(
148
                    'table_name',
149
                    $queryBuilder->createNamedParameter($tableName)
150
                )
151
            )
152
            ->execute();
153
    }
154
155
    public function removeAllBrokenLinksOfRecordsOnPageIds(array $pageIds, array $linkTypes): void
156
    {
157
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
158
            ->getQueryBuilderForTable(static::TABLE);
159
160
        $queryBuilder->delete(static::TABLE)
161
            ->where(
162
                $queryBuilder->expr()->orX(
163
                    $queryBuilder->expr()->andX(
164
                        $queryBuilder->expr()->in(
165
                            'record_uid',
166
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
167
                        ),
168
                        $queryBuilder->expr()->eq('table_name', $queryBuilder->createNamedParameter('pages'))
169
                    ),
170
                    $queryBuilder->expr()->andX(
171
                        $queryBuilder->expr()->in(
172
                            'record_pid',
173
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
174
                        ),
175
                        $queryBuilder->expr()->neq(
176
                            'table_name',
177
                            $queryBuilder->createNamedParameter('pages')
178
                        )
179
                    )
180
                ),
181
                $queryBuilder->expr()->in(
182
                    'link_type',
183
                    $queryBuilder->createNamedParameter($linkTypes, Connection::PARAM_STR_ARRAY)
184
                )
185
            )
186
            ->execute();
187
    }
188
189
    /**
190
     * Prepare database query with pageList and keyOpt data.
191
     *
192
     * This takes permissions of current BE user into account
193
     *
194
     * @param int[] $pageIds Pages to check for broken links
195
     * @param string[] $linkTypes Link types to validate
196
     * @param string[] $searchFields table => [fields1, field2, ...], ... : fields in which linkvalidator should
197
     *   search for broken links
198
     * @return array
199
     */
200
    public function getAllBrokenLinksForPages(array $pageIds, array $linkTypes, array $searchFields = []): array
201
    {
202
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
203
            ->getQueryBuilderForTable(self::TABLE);
204
        if (!$GLOBALS['BE_USER']->isAdmin()) {
205
            $queryBuilder->getRestrictions()
206
                ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder));
207
        }
208
        $records = $queryBuilder
209
            ->select(self::TABLE . '.*')
210
            ->from(self::TABLE)
211
            ->join(
212
                'tx_linkvalidator_link',
213
                'pages',
214
                'pages',
215
                $queryBuilder->expr()->eq('tx_linkvalidator_link.record_pid', $queryBuilder->quoteIdentifier('pages.uid'))
216
            )
217
            ->where(
218
                $queryBuilder->expr()->orX(
219
                    $queryBuilder->expr()->andX(
220
                        $queryBuilder->expr()->in(
221
                            'record_uid',
222
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
223
                        ),
224
                        $queryBuilder->expr()->eq('table_name', $queryBuilder->createNamedParameter('pages'))
225
                    ),
226
                    $queryBuilder->expr()->andX(
227
                        $queryBuilder->expr()->in(
228
                            'record_pid',
229
                            $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
230
                        ),
231
                        $queryBuilder->expr()->neq('table_name', $queryBuilder->createNamedParameter('pages'))
232
                    )
233
                ),
234
                $queryBuilder->expr()->in(
235
                    'link_type',
236
                    $queryBuilder->createNamedParameter($linkTypes, Connection::PARAM_STR_ARRAY)
237
                )
238
            )
239
            ->orderBy('tx_linkvalidator_link.record_uid')
240
            ->addOrderBy('tx_linkvalidator_link.uid')
241
            ->execute()
242
            ->fetchAll();
243
        foreach ($records as &$record) {
244
            $response = json_decode($record['url_response'], true);
245
            // Fallback mechanism to still support the old serialized data, could be removed in TYPO3 v12 or later
246
            if ($response === null) {
247
                $response = unserialize($record['url_response'], ['allowed_classes' => false]);
248
            }
249
            $record['url_response'] = $response;
250
        }
251
        return $records;
252
    }
253
254
    public function addBrokenLink($record, bool $isValid, array $errorParams = null): void
255
    {
256
        $response = ['valid' => $isValid];
257
        if ($errorParams) {
258
            $response['errorParams'] = $errorParams;
259
        }
260
        $record['url_response'] = json_encode($response);
261
        GeneralUtility::makeInstance(ConnectionPool::class)
262
            ->getConnectionForTable(self::TABLE)
263
            ->insert(self::TABLE, $record);
264
    }
265
}
266