Issues (1783)

src/controllers/TablesController.php (47 issues)

1
<?php
2
/**
3
 * Retour plugin for Craft CMS
4
 *
5
 * Retour allows you to intelligently redirect legacy URLs, so that you don't
6
 * lose SEO value when rebuilding & restructuring a website
7
 *
8
 * @link      https://nystudio107.com/
0 ignored issues
show
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) 2018 nystudio107
0 ignored issues
show
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
PHP version not specified
Loading history...
Missing @category tag in file comment
Loading history...
Missing @package tag in file comment
Loading history...
Missing @author tag in file comment
Loading history...
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\retour\controllers;
13
14
use Craft;
15
use craft\db\Query;
16
use craft\errors\SiteNotFoundException;
17
use craft\helpers\ElementHelper;
18
use craft\web\Controller;
19
use nystudio107\retour\helpers\Permission as PermissionHelper;
20
use nystudio107\retour\helpers\UrlHelper;
21
use yii\web\BadRequestHttpException;
22
use yii\web\ForbiddenHttpException;
23
use yii\web\Response;
24
25
/**
0 ignored issues
show
Missing short description in doc comment
Loading history...
26
 * @author    nystudio107
0 ignored issues
show
The tag in position 1 should be the @package tag
Loading history...
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
27
 * @package   Retour
0 ignored issues
show
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
28
 * @since     3.0.0
0 ignored issues
show
The tag in position 3 should be the @author tag
Loading history...
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
29
 */
0 ignored issues
show
Missing @category tag in class comment
Loading history...
Missing @license tag in class comment
Loading history...
Missing @link tag in class comment
Loading history...
30
class TablesController extends Controller
31
{
32
    // Constants
33
    // =========================================================================
34
35
    protected const HANDLED_MAP = [
36
        'handled' => 1,
37
        'nothandled' => 0,
38
    ];
39
40
    protected const SORT_MAP = [
41
        'DESC' => SORT_DESC,
42
        'ASC' => SORT_ASC,
43
    ];
44
45
    protected const ALLOWED_STATS_SORT_FIELDS = [
46
        'redirectSrcUrl',
47
        'referrerUrl',
48
        'remoteIp',
49
        'hitCount',
50
        'hitLastTime',
51
        'handledByRetour',
52
    ];
53
54
    protected const ALLOWED_REDIRECTS_SORT_FIELDS = [
55
        'redirectSrcUrl',
56
        'redirectDestUrl',
57
        'redirectMatchType',
58
        'siteId',
59
        'redirectHttpCode',
60
        'priority',
61
        'hitCount',
62
        'hitLastTime',
63
    ];
64
65
    // Protected Properties
66
    // =========================================================================
67
68
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
69
     * @inheritdoc
70
     */
71
    protected array|bool|int $allowAnonymous = [
72
    ];
73
74
    // Public Methods
75
    // =========================================================================
76
77
    /**
78
     * Handle requests for the dashboard statistics table
79
     *
80
     * @param string $sort
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 6 spaces after parameter type; 1 found
Loading history...
81
     * @param int $page
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 9 spaces after parameter type; 1 found
Loading history...
82
     * @param int $per_page
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 9 spaces after parameter type; 1 found
Loading history...
83
     * @param string $filter
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 6 spaces after parameter type; 1 found
Loading history...
84
     * @param int $siteId
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 9 spaces after parameter type; 1 found
Loading history...
85
     * @param string|null $handled
0 ignored issues
show
Missing parameter comment
Loading history...
86
     *
87
     * @return Response
88
     * @throws ForbiddenHttpException
89
     * @throws BadRequestHttpException
90
     */
91
    public function actionDashboard(
92
        string $sort = 'hitCount|desc',
93
        int    $page = 1,
94
        int    $per_page = 20,
95
               $filter = '',
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
96
               $siteId = 0,
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
97
               $handled = 'all',
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
98
    ): Response {
99
        PermissionHelper::controllerPermissionCheck('retour:dashboard');
100
        $data = [];
101
        $sortField = 'hitCount';
102
        $sortType = 'DESC';
103
        // Figure out the sorting type
104
        if ($sort !== '') {
105
            if (strpos($sort, '|') === false) {
106
                $sortField = $sort;
107
            } else {
108
                list($sortField, $sortType) = explode('|', $sort);
109
            }
110
        }
111
        $sortType = strtoupper($sortType);
112
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
113
        // Validate untrusted data
114
        if (!in_array($sortField, self::ALLOWED_STATS_SORT_FIELDS, true)) {
115
            throw new BadRequestHttpException(Craft::t('retour', 'Invalid sort field specified.'));
116
        }
117
        // Query the db table
118
        $offset = ($page - 1) * $per_page;
119
        $query = (new Query())
120
            ->from(['{{%retour_stats}}'])
121
            ->offset($offset)
122
            ->limit($per_page)
123
            ->orderBy([$sortField => $sortType])
124
            ->filterWhere(['like', 'redirectSrcUrl', $filter])
125
            ->orFilterWhere(['like', 'referrerUrl', $filter]);
126
        if ((int)$siteId !== 0) {
127
            $query->andWhere(['siteId' => $siteId]);
128
        }
129
        if ($handled !== 'all') {
130
            $query->andWhere(['handledByRetour' => self::HANDLED_MAP[$handled]]);
131
        }
132
        $stats = $query->all();
133
        if ($stats) {
134
            // Add in the `addLink` field
135
            foreach ($stats as &$stat) {
136
                // Normalize the `redirectSrcUrl` to point to a valid frontend site URL
137
                $stat['redirectSrcUrlFull'] = $stat['redirectSrcUrl'];
138
                if (!UrlHelper::isAbsoluteUrl($stat['redirectSrcUrlFull'])) {
139
                    $sites = Craft::$app->getSites();
140
                    $site = $sites->getSiteById($stat['siteId'], true);
141
                    if ($site) {
142
                        $stat['redirectSrcUrlFull'] = UrlHelper::mergeUrlWithPath($site->baseUrl, $stat['redirectSrcUrlFull']);
143
                    }
144
                }
145
                $stat['addLink'] = '';
146
                if (!$stat['handledByRetour']) {
147
                    $encodedUrl = urlencode('/' . ltrim($stat['redirectSrcUrl'], '/'));
148
                    // Add the siteId to the URL, but keep the current behavior of passing in siteId=0 for "all"
149
                    $statSiteId = $stat['siteId'] ?? 0;
150
                    try {
151
                        $primarySite = Craft::$app->getSites()->getPrimarySite();
152
                    } catch (SiteNotFoundException $e) {
153
                        $primarySite = null;
154
                    }
155
                    if ($primarySite !== null && $statSiteId == (int)$primarySite->id) {
156
                        $statSiteId = 0;
157
                    }
158
                    $stat['addLink'] = UrlHelper::cpUrl('retour/add-redirect', [
0 ignored issues
show
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
159
                        'defaultUrl' => $encodedUrl,
160
                        'siteId' => $statSiteId,
161
                    ]);
0 ignored issues
show
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
162
                }
163
            }
164
            // Format the data for the API
165
            $data['data'] = $stats;
166
            $count = $query->count();
167
            $data['links']['pagination'] = [
168
                'total' => $count,
169
                'per_page' => $per_page,
170
                'current_page' => $page,
171
                'last_page' => ceil($count / $per_page),
172
                'next_page_url' => null,
173
                'prev_page_url' => null,
174
                'from' => $offset + 1,
175
                'to' => $offset + ($count > $per_page ? $per_page : $count),
176
            ];
177
        }
178
179
        return $this->asJson($data);
180
    }
181
182
    /**
0 ignored issues
show
Parameter $shortLinks should have a doc-comment as per coding-style.
Loading history...
183
     * Handle requests for the dashboard redirects table
184
     *
185
     * @param string $sort
0 ignored issues
show
Missing parameter comment
Loading history...
186
     * @param int $page
0 ignored issues
show
Expected 4 spaces after parameter type; 1 found
Loading history...
Missing parameter comment
Loading history...
187
     * @param int $per_page
0 ignored issues
show
Expected 4 spaces after parameter type; 1 found
Loading history...
Missing parameter comment
Loading history...
188
     * @param string $filter
0 ignored issues
show
Missing parameter comment
Loading history...
189
     * @param int $siteId
0 ignored issues
show
Missing parameter comment
Loading history...
Expected 4 spaces after parameter type; 1 found
Loading history...
190
     *
191
     * @return Response
192
     * @throws ForbiddenHttpException
193
     * @throws BadRequestHttpException
194
     */
195
    public function actionRedirects(
196
        string $sort = 'hitCount|desc',
197
        int    $page = 1,
198
        int    $per_page = 20,
199
               $filter = '',
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
200
               $siteId = 0,
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
201
               $shortLinks = false,
0 ignored issues
show
Multi-line function declaration not indented correctly; expected 8 spaces but found 15
Loading history...
202
    ): Response {
203
        PermissionHelper::controllerPermissionCheck('retour:redirects');
204
        $data = [];
205
        $sortField = 'hitCount';
206
        $sortType = 'DESC';
207
        // Figure out the sorting type
208
        if ($sort !== '') {
209
            if (strpos($sort, '|') === false) {
210
                $sortField = $sort;
211
            } else {
212
                list($sortField, $sortType) = explode('|', $sort);
213
            }
214
        }
215
        $sortType = strtoupper($sortType);
216
        $sortType = self::SORT_MAP[$sortType] ?? self::SORT_MAP['DESC'];
217
        // Validate untrusted data
218
        if (!in_array($sortField, self::ALLOWED_REDIRECTS_SORT_FIELDS, true)) {
219
            throw new BadRequestHttpException(Craft::t('retour', 'Invalid sort field specified.'));
220
        }
221
        // Query the db table
222
        $offset = ($page - 1) * $per_page;
223
        $query = (new Query())
224
            ->from(['{{%retour_static_redirects}}'])
225
            ->offset($offset)
226
            ->limit($per_page)
227
            ->orderBy([$sortField => $sortType])
228
            ->filterWhere(['like', 'redirectSrcUrl', $filter])
229
            ->orFilterWhere(['like', 'redirectDestUrl', $filter]);
230
        if ((int)$siteId !== 0) {
231
            $query->andWhere(['siteId' => $siteId]);
232
        }
233
        if ($shortLinks) {
234
            $query->andWhere(['not', ['associatedElementId' => 0]]);
235
        } else {
236
            $query->andWhere(['associatedElementId' => 0]);
237
        }
238
        $redirects = $query->all();
239
        // Add in the `deleteLink` field and clean up the redirects
240
        foreach ($redirects as &$redirect) {
241
            // Handle short links by adding the element's title and CP URL
242
            if ($shortLinks) {
243
                $redirect['elementTitle'] = '';
244
                $redirect['elementCpUrl'] = '';
245
                $elementId = $redirect['associatedElementId'] ?? null;
246
                $elementSiteId = $redirect['siteId'] ?? null;
247
                if (!empty($elementId)) {
248
                    $element = Craft::$app->getElements()->getElementById($elementId, null, $elementSiteId);
249
                    if ($element) {
250
                        $element = ElementHelper::rootElement($element);
0 ignored issues
show
Deprecated Code introduced by
The function craft\helpers\ElementHelper::rootElement() has been deprecated: in 5.4.0. Use [[ElementInterface::getRootOwner()]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

250
                        $element = /** @scrutinizer ignore-deprecated */ ElementHelper::rootElement($element);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
251
                        $redirect['elementTitle'] = $element->title;
252
                        $redirect['elementCpUrl'] = $element->getCpEditUrl();
253
                    }
254
                }
255
            }
256
            // Make sure the destination URL is not a regex
257
            if ($redirect['redirectMatchType'] !== 'exactmatch') {
258
                if (preg_match("/\$\d+/", $redirect['redirectDestUrl'])) {
259
                    $redirect['redirectDestUrl'] = '';
260
                }
261
            }
262
            // Handle extracting the site name
263
            $redirect['siteName'] = Craft::t('retour', 'All Sites');
264
            if ($redirect['siteId']) {
265
                $sites = Craft::$app->getSites();
266
                $site = $sites->getSiteById($redirect['siteId']);
267
                if ($site) {
268
                    $redirect['siteName'] = $site->name;
269
                }
270
            }
271
272
            $redirect['editLink'] = UrlHelper::cpUrl('retour/edit-redirect/' . $redirect['id']);
273
        }
274
        // Format the data for the API
275
        if ($redirects) {
276
            $data['data'] = $redirects;
277
            $count = $query->count();
278
            $data['links']['pagination'] = [
279
                'total' => $count,
280
                'per_page' => $per_page,
281
                'current_page' => $page,
282
                'last_page' => ceil($count / $per_page),
283
                'next_page_url' => null,
284
                'prev_page_url' => null,
285
                'from' => $offset + 1,
286
                'to' => $offset + ($count > $per_page ? $per_page : $count),
287
            ];
288
        }
289
290
        return $this->asJson($data);
291
    }
292
293
    // Protected Methods
294
    // =========================================================================
295
}
296