MediaWiki REL1_37
SpecialDoubleRedirects.php
Go to the documentation of this file.
1<?php
28
36
39
42
44 private $dbr;
45
51 public function __construct(
55 ) {
56 parent::__construct( 'DoubleRedirects' );
57 $this->contentHandlerFactory = $contentHandlerFactory;
58 $this->linkBatchFactory = $linkBatchFactory;
59 $this->setDBLoadBalancer( $loadBalancer );
60 $this->dbr = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
61 }
62
63 public function isExpensive() {
64 return true;
65 }
66
67 public function isSyndicated() {
68 return false;
69 }
70
71 protected function sortDescending() {
72 return false;
73 }
74
75 protected function getPageHeader() {
76 return $this->msg( 'doubleredirectstext' )->parseAsBlock();
77 }
78
79 private function reallyGetQueryInfo( $namespace = null, $title = null ) {
80 $limitToTitle = !( $namespace === null && $title === null );
81 $retval = [
82 'tables' => [
83 'ra' => 'redirect',
84 'rb' => 'redirect',
85 'pa' => 'page',
86 'pb' => 'page'
87 ],
88 'fields' => [
89 'namespace' => 'pa.page_namespace',
90 'title' => 'pa.page_title',
91
92 'b_namespace' => 'pb.page_namespace',
93 'b_title' => 'pb.page_title',
94
95 // Select fields from redirect instead of page. Because there may
96 // not actually be a page table row for this target (e.g. for interwiki redirects)
97 'c_namespace' => 'rb.rd_namespace',
98 'c_title' => 'rb.rd_title',
99 'c_fragment' => 'rb.rd_fragment',
100 'c_interwiki' => 'rb.rd_interwiki',
101 ],
102 'conds' => [
103 'ra.rd_from = pa.page_id',
104
105 // Filter out redirects where the target goes interwiki (T42353).
106 // This isn't an optimization, it is required for correct results,
107 // otherwise a non-double redirect like Bar -> w:Foo will show up
108 // like "Bar -> Foo -> w:Foo".
109
110 // Need to check both NULL and "" for some reason,
111 // apparently either can be stored for non-iw entries.
112 'ra.rd_interwiki IS NULL OR ra.rd_interwiki = ' . $this->dbr->addQuotes( '' ),
113
114 'pb.page_namespace = ra.rd_namespace',
115 'pb.page_title = ra.rd_title',
116
117 'rb.rd_from = pb.page_id',
118 ]
119 ];
120
121 if ( $limitToTitle ) {
122 $retval['conds']['pa.page_namespace'] = $namespace;
123 $retval['conds']['pa.page_title'] = $title;
124 }
125
126 return $retval;
127 }
128
129 public function getQueryInfo() {
130 return $this->reallyGetQueryInfo();
131 }
132
133 protected function getOrderFields() {
134 return [ 'ra.rd_namespace', 'ra.rd_title' ];
135 }
136
142 public function formatResult( $skin, $result ) {
143 // If no Title B or C is in the query, it means this came from
144 // querycache (which only saves the 3 columns for title A).
145 // That does save the bulk of the query cost, but now we need to
146 // get a little more detail about each individual entry quickly
147 // using the filter of reallyGetQueryInfo.
148 $deep = false;
149 if ( $result ) {
150 if ( isset( $result->b_namespace ) ) {
151 $deep = $result;
152 } else {
153 $qi = $this->reallyGetQueryInfo(
154 $result->namespace,
155 $result->title
156 );
157 $deep = $this->dbr->selectRow(
158 $qi['tables'],
159 $qi['fields'],
160 $qi['conds'],
161 __METHOD__
162 );
163 }
164 }
165
166 $titleA = Title::makeTitle( $result->namespace, $result->title );
167
169 if ( !$deep ) {
170 return '<del>' . $linkRenderer->makeLink( $titleA, null, [], [ 'redirect' => 'no' ] ) . '</del>';
171 }
172
173 // if the page is editable, add an edit link
174 if (
175 // check user permissions
176 $this->getAuthority()->isAllowed( 'edit' ) &&
177 // check, if the content model is editable through action=edit
178 $this->contentHandlerFactory->getContentHandler( $titleA->getContentModel() )
179 ->supportsDirectEditing()
180 ) {
182 $titleA,
183 $this->msg( 'parentheses', $this->msg( 'editlink' )->text() )->text(),
184 [],
185 [ 'action' => 'edit' ]
186 );
187 } else {
188 $edit = '';
189 }
190
192 $titleA,
193 null,
194 [],
195 [ 'redirect' => 'no' ]
196 );
197
198 $titleB = Title::makeTitle( $deep->b_namespace, $deep->b_title );
200 $titleB,
201 null,
202 [],
203 [ 'redirect' => 'no' ]
204 );
205
206 $titleC = Title::makeTitle(
207 $deep->c_namespace,
208 $deep->c_title,
209 $deep->c_fragment,
210 $deep->c_interwiki
211 );
212 $linkC = $linkRenderer->makeKnownLink( $titleC, $titleC->getFullText() );
213
214 $lang = $this->getLanguage();
215 $arr = $lang->getArrow() . $lang->getDirMark();
216
217 return ( "{$linkA} {$edit} {$arr} {$linkB} {$arr} {$linkC}" );
218 }
219
220 public function execute( $par ) {
221 $this->addHelpLink( 'Help:Redirects' );
222 parent::execute( $par );
223 }
224
231 public function preprocessResults( $db, $res ) {
232 if ( !$res->numRows() ) {
233 return;
234 }
235
236 $batch = $this->linkBatchFactory->newLinkBatch();
237 foreach ( $res as $row ) {
238 $batch->add( $row->namespace, $row->title );
239 if ( isset( $row->b_namespace ) ) {
240 // lazy loaded when using cached results
241 $batch->add( $row->b_namespace, $row->b_title );
242 }
243 if ( isset( $row->c_interwiki ) && !$row->c_interwiki ) {
244 // lazy loaded when using cached result, not added when interwiki link
245 $batch->add( $row->c_namespace, $row->c_title );
246 }
247 }
248 $batch->execute();
249
250 // Back to start for display
251 $res->seek( 0 );
252 }
253
254 protected function getGroupName() {
255 return 'maintenance';
256 }
257}
makeKnownLink( $target, $text=null, array $extraAttribs=[], array $query=[])
makeLink( $target, $text=null, array $extraAttribs=[], array $query=[])
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
Definition QueryPage.php:41
setDBLoadBalancer(ILoadBalancer $loadBalancer)
ILoadBalancer null $loadBalancer
Definition QueryPage.php:71
A special page listing redirects to redirecting page.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
preprocessResults( $db, $res)
Cache page content model and gender distinction for performance.
execute( $par)
This is the actual workhorse.
sortDescending()
Override to sort by increasing values.
isExpensive()
Is this query expensive (for some definition of expensive)? Then we don't let it run in miser mode.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
IContentHandlerFactory $contentHandlerFactory
reallyGetQueryInfo( $namespace=null, $title=null)
__construct(IContentHandlerFactory $contentHandlerFactory, LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer)
getPageHeader()
The content returned by this function will be output before any result.
getOrderFields()
Subclasses return an array of fields to order by here.
isSyndicated()
Sometime we don't want to build rss / atom feeds.
LinkRenderer null $linkRenderer
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
Database cluster connection, tracking, load balancing, and transaction manager interface.
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a live database handle reference for a server index.
Result wrapper for grabbing data queried from an IDatabase object.
if(!isset( $args[0])) $lang