Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialBrokenRedirects
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 11
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 isExpensive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isSyndicated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sortDescending
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPageHeader
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
2
 getOrderFields
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatResult
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
56
 execute
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 preprocessResults
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Implements Special:Brokenredirects
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24namespace MediaWiki\Specials;
25
26use MediaWiki\Cache\LinkBatchFactory;
27use MediaWiki\Content\IContentHandlerFactory;
28use MediaWiki\SpecialPage\QueryPage;
29use MediaWiki\Title\Title;
30use Skin;
31use Wikimedia\Rdbms\IConnectionProvider;
32use Wikimedia\Rdbms\IDatabase;
33use Wikimedia\Rdbms\IResultWrapper;
34
35/**
36 * A special page listing redirects to non existent page. Those should be
37 * fixed to point to an existing page.
38 *
39 * @ingroup SpecialPage
40 */
41class SpecialBrokenRedirects extends QueryPage {
42
43    private IContentHandlerFactory $contentHandlerFactory;
44
45    /**
46     * @param IContentHandlerFactory $contentHandlerFactory
47     * @param IConnectionProvider $dbProvider
48     * @param LinkBatchFactory $linkBatchFactory
49     */
50    public function __construct(
51        IContentHandlerFactory $contentHandlerFactory,
52        IConnectionProvider $dbProvider,
53        LinkBatchFactory $linkBatchFactory
54    ) {
55        parent::__construct( 'BrokenRedirects' );
56        $this->contentHandlerFactory = $contentHandlerFactory;
57        $this->setDatabaseProvider( $dbProvider );
58        $this->setLinkBatchFactory( $linkBatchFactory );
59    }
60
61    public function isExpensive() {
62        return true;
63    }
64
65    public function isSyndicated() {
66        return false;
67    }
68
69    protected function sortDescending() {
70        return false;
71    }
72
73    protected function getPageHeader() {
74        return $this->msg( 'brokenredirectstext' )->parseAsBlock();
75    }
76
77    public function getQueryInfo() {
78        $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
79
80        return [
81            'tables' => [
82                'redirect',
83                'p1' => 'page',
84                'p2' => 'page',
85            ],
86            'fields' => [
87                'namespace' => 'p1.page_namespace',
88                'title' => 'p1.page_title',
89                'rd_namespace',
90                'rd_title',
91                'rd_fragment',
92            ],
93            'conds' => [
94                // Exclude pages that don't exist locally as wiki pages, but aren't "broken" either: special
95                // pages and interwiki links.
96                'rd_namespace >= 0',
97                'rd_interwiki' => '',
98                'p2.page_namespace' => null,
99            ],
100            'join_conds' => [
101                'p1' => [ 'JOIN', [
102                    'rd_from=p1.page_id',
103                ] ],
104                'p2' => [ 'LEFT JOIN', [
105                    'rd_namespace=p2.page_namespace',
106                    'rd_title=p2.page_title'
107                ] ],
108            ],
109        ];
110    }
111
112    /**
113     * @return array
114     */
115    protected function getOrderFields() {
116        return [ 'rd_namespace', 'rd_title', 'rd_from' ];
117    }
118
119    /**
120     * @param Skin $skin
121     * @param \stdClass $result Result row
122     * @return string
123     */
124    public function formatResult( $skin, $result ) {
125        $fromObj = Title::makeTitle( $result->namespace, $result->title );
126        if ( isset( $result->rd_title ) ) {
127            $toObj = Title::makeTitle(
128                $result->rd_namespace,
129                $result->rd_title,
130                $result->rd_fragment
131            );
132        } else {
133            $toObj = false;
134        }
135
136        $linkRenderer = $this->getLinkRenderer();
137
138        // $toObj may very easily be false if the $result list is cached
139        if ( !is_object( $toObj ) ) {
140            return '<del>' . $linkRenderer->makeLink( $fromObj ) . '</del>';
141        }
142
143        $from = $linkRenderer->makeKnownLink(
144            $fromObj,
145            null,
146            [],
147            [ 'redirect' => 'no' ]
148        );
149        $links = [];
150        // if the page is editable, add an edit link
151        if (
152            // check user permissions
153            $this->getAuthority()->isAllowed( 'edit' ) &&
154            // check, if the content model is editable through action=edit
155            $this->contentHandlerFactory->getContentHandler( $fromObj->getContentModel() )
156                ->supportsDirectEditing()
157        ) {
158            $links[] = $linkRenderer->makeKnownLink(
159                $fromObj,
160                $this->msg( 'brokenredirects-edit' )->text(),
161                [],
162                [ 'action' => 'edit' ]
163            );
164        }
165        $to = $linkRenderer->makeBrokenLink( $toObj, $toObj->getFullText() );
166        $arr = $this->getLanguage()->getArrow();
167
168        $out = $from . $this->msg( 'word-separator' )->escaped();
169
170        if ( $this->getAuthority()->isAllowed( 'delete' ) ) {
171            $links[] = $linkRenderer->makeKnownLink(
172                $fromObj,
173                $this->msg( 'brokenredirects-delete' )->text(),
174                [],
175                [
176                    'action' => 'delete',
177                    'wpReason' => $this->msg( 'brokenredirects-delete-reason' )
178                        ->inContentLanguage()
179                        ->text()
180                ]
181            );
182        }
183
184        if ( $links ) {
185            $out .= $this->msg( 'parentheses' )->rawParams( $this->getLanguage()
186                ->pipeList( $links ) )->escaped();
187        }
188        $out .= " {$arr} {$to}";
189
190        return $out;
191    }
192
193    public function execute( $par ) {
194        $this->addHelpLink( 'Help:Redirects' );
195        parent::execute( $par );
196    }
197
198    /**
199     * Cache page content model for performance
200     *
201     * @param IDatabase $db
202     * @param IResultWrapper $res
203     */
204    public function preprocessResults( $db, $res ) {
205        $this->executeLBFromResultWrapper( $res );
206    }
207
208    protected function getGroupName() {
209        return 'maintenance';
210    }
211}
212
213/** @deprecated class alias since 1.41 */
214class_alias( SpecialBrokenRedirects::class, 'SpecialBrokenRedirects' );