Completed
Push — master ( a8b8b7...ae10a2 )
by
unknown
21:25
created

EditableRestriction::buildExpression()   B

Complexity

Conditions 9
Paths 18

Size

Total Lines 93
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 56
nc 18
nop 2
dl 0
loc 93
rs 7.4044
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types = 1);
3
4
namespace TYPO3\CMS\Linkvalidator\QueryRestrictions;
5
6
/*
7
 * This file is part of the TYPO3 CMS project.
8
 *
9
 * It is free software; you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License, either version 2
11
 * of the License, or any later version.
12
 *
13
 * For the full copyright and license information, please read the
14
 * LICENSE.txt file that was distributed with this source code.
15
 *
16
 * The TYPO3 project - inspiring people to share!
17
 */
18
19
use TYPO3\CMS\Core\Database\Connection;
20
use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
21
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
22
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
23
use TYPO3\CMS\Core\Database\Query\QueryHelper;
24
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface;
25
use TYPO3\CMS\Core\Type\Bitmask\Permission;
26
27
class EditableRestriction implements QueryRestrictionInterface
28
{
29
    /**
30
     * Specify which database fields the current user is allowed to edit
31
     *
32
     * @var array
33
     */
34
    protected $allowedFields = [];
35
36
    /**
37
     * Specify which languages the current user is allowed to edit
38
     *
39
     * @var array
40
     */
41
    protected $allowedLanguages = [];
42
43
    /**
44
     * Explicit allow fields
45
     *
46
     * @var array
47
     */
48
    protected $explicitAllowFields = [];
49
50
    /**
51
     * @var QueryBuilder
52
     */
53
    protected $queryBuilder;
54
55
    /**
56
     * @param array $searchFields array of 'table' => 'field1, field2'
57
     *   in which linkvalidator searches for broken links.
58
     * @param QueryBuilder $queryBuilder
59
     */
60
    public function __construct(array $searchFields, QueryBuilder $queryBuilder)
61
    {
62
        $this->allowedFields = $this->getAllowedFieldsForCurrentUser($searchFields);
63
        $this->allowedLanguages = $this->getAllowedLanguagesForCurrentUser();
64
        foreach ($searchFields as $table => $fields) {
65
            if ($table !== 'pages' && ($GLOBALS['TCA'][$table]['ctrl']['type'] ?? false)) {
66
                $type = $GLOBALS['TCA'][$table]['ctrl']['type'];
67
                $this->explicitAllowFields[$table][$type] = $this->getExplicitAllowFieldsForCurrentUser($table, $type);
68
            }
69
        }
70
        $this->queryBuilder = $queryBuilder;
71
    }
72
73
    /**
74
     * Gets all allowed language ids for current backend user
75
     *
76
     * @return array
77
     */
78
    protected function getAllowedLanguagesForCurrentUser(): array
79
    {
80
        if (!(is_string($GLOBALS['BE_USER']->groupData['allowed_languages'] ?? false))) {
81
            return [];
82
        }
83
84
        return array_map('intval', explode(',', $GLOBALS['BE_USER']->groupData['allowed_languages']));
85
    }
86
87
    protected function getExplicitAllowFieldsForCurrentUser(string $table, string $field): array
88
    {
89
        $allowDenyOptions = [];
90
        $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
91
        // Check for items
92
        if ($fieldConfig['type'] === 'select' && is_array($fieldConfig['items'] ?? false)) {
93
            foreach ($fieldConfig['items'] as $iVal) {
94
                $itemIdentifier = (string)$iVal[1];
95
                if ($GLOBALS['BE_USER']->checkAuthMode($table, $field, $itemIdentifier, $GLOBALS['TYPO3_CONF_VARS']['BE']['explicitADmode'])) {
96
                    $allowDenyOptions[] = $itemIdentifier;
97
                }
98
            }
99
        }
100
        return $allowDenyOptions;
101
    }
102
103
    /**
104
     * Get allowed table / fieldnames for current backend user.
105
     * Only consider table / fields in $searchFields
106
     *
107
     * @param array $searchFields array of 'table' => ['field1, field2', ....]
108
     *   in which linkvalidator searches for broken links
109
     * @return array
110
     */
111
    protected function getAllowedFieldsForCurrentUser(array $searchFields = []): array
112
    {
113
        if (!$searchFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $searchFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
114
            return [];
115
        }
116
117
        $allowedFields = [];
118
119
        foreach ($searchFields as $table => $fieldList) {
120
            if (!$GLOBALS['BE_USER']->isAdmin() && !$GLOBALS['BE_USER']->check('tables_modify', $table)) {
121
                // table not allowed
122
                continue;
123
            }
124
            foreach ($fieldList as $field) {
125
                $isExcludeField = $GLOBALS['TCA'][$table]['columns'][$field]['exclude'] ?? false;
126
                if (!$GLOBALS['BE_USER']->isAdmin()
127
                    && $isExcludeField
128
                    && !$GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $field)) {
129
                    continue;
130
                }
131
                $allowedFields[$table][$field] = true;
132
            }
133
        }
134
        return $allowedFields;
135
    }
136
137
    public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
138
    {
139
        $constraints = [];
140
141
        if ($this->allowedFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->allowedFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
142
            $constraints = [
143
                $expressionBuilder->orX(
144
                // broken link is in page and page is editable
145
                    $expressionBuilder->andX(
146
                        $expressionBuilder->eq(
147
                            'tx_linkvalidator_link.table_name',
148
                            $this->queryBuilder->createNamedParameter('pages')
149
                        ),
150
                        QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_EDIT))
151
                    ),
152
                    // OR broken link is in content and content is editable
153
                    $expressionBuilder->andX(
154
                        $expressionBuilder->neq(
155
                            'tx_linkvalidator_link.table_name',
156
                            $this->queryBuilder->createNamedParameter('pages')
157
                        ),
158
                        QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(Permission::CONTENT_EDIT))
159
                    )
160
                )
161
            ];
162
163
            // check if fields are editable
164
            $additionalWhere = [];
165
            foreach ($this->allowedFields as $table => $fields) {
166
                foreach ($fields as $field => $value) {
167
                    $additionalWhere[] = $expressionBuilder->andX(
168
                        $expressionBuilder->eq(
169
                            'tx_linkvalidator_link.table_name',
170
                            $this->queryBuilder->createNamedParameter($table)
171
                        ),
172
                        $expressionBuilder->eq(
173
                            'tx_linkvalidator_link.field',
174
                            $this->queryBuilder->createNamedParameter($field)
175
                        )
176
                    );
177
                }
178
            }
179
            if ($additionalWhere) {
180
                $constraints[] = $expressionBuilder->orX(...$additionalWhere);
181
            }
182
        } else {
183
            // add a constraint that will always return zero records because there are NO allowed fields
184
            $constraints[] = $expressionBuilder->isNull('tx_linkvalidator_link.table_name');
185
        }
186
187
        foreach ($this->explicitAllowFields as $table => $field) {
188
            $additionalWhere = [];
189
            $additionalWhere[] = $expressionBuilder->andX(
190
                $expressionBuilder->eq(
191
                    'tx_linkvalidator_link.table_name',
192
                    $this->queryBuilder->createNamedParameter($table)
193
                ),
194
                $expressionBuilder->in(
195
                    'tx_linkvalidator_link.element_type',
196
                    $this->queryBuilder->createNamedParameter(
197
                        array_unique(current($field)),
198
                        Connection::PARAM_STR_ARRAY
199
                    )
200
                )
201
            );
202
            $additionalWhere[] = $expressionBuilder->neq(
203
                'tx_linkvalidator_link.table_name',
204
                $this->queryBuilder->createNamedParameter($table)
205
            );
206
            if ($additionalWhere) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $additionalWhere of type array<mixed,TYPO3\CMS\Co...ositeExpression|string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
207
                $constraints[] = $expressionBuilder->orX(...$additionalWhere);
208
            }
209
        }
210
211
        if ($this->allowedLanguages) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->allowedLanguages of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
212
            $additionalWhere = [];
213
            foreach ($this->allowedLanguages as $langId) {
214
                $additionalWhere[] = $expressionBuilder->orX(
215
                    $expressionBuilder->eq(
216
                        'tx_linkvalidator_link.language',
217
                        $this->queryBuilder->createNamedParameter($langId, \PDO::PARAM_INT)
218
                    ),
219
                    $expressionBuilder->eq(
220
                        'tx_linkvalidator_link.language',
221
                        $this->queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
222
                    )
223
                );
224
            }
225
            $constraints[] = $expressionBuilder->orX(...$additionalWhere);
226
        }
227
        // If allowed languages is empty: all languages are allowed, so no constraint in this case
228
229
        return $expressionBuilder->andX(...$constraints);
230
    }
231
}
232