MediaWiki  master
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 );
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 
168  $linkRenderer = $this->getLinkRenderer();
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  ) {
181  $edit = $linkRenderer->makeKnownLink(
182  $titleA,
183  $this->msg( 'parentheses', $this->msg( 'editlink' )->text() )->text(),
184  [],
185  [ 'action' => 'edit' ]
186  );
187  } else {
188  $edit = '';
189  }
190 
191  $linkA = $linkRenderer->makeKnownLink(
192  $titleA,
193  null,
194  [],
195  [ 'redirect' => 'no' ]
196  );
197 
198  $titleB = Title::makeTitle( $deep->b_namespace, $deep->b_title );
199  $linkB = $linkRenderer->makeKnownLink(
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 }
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:42
setDBLoadBalancer(ILoadBalancer $loadBalancer)
Definition: QueryPage.php:895
ILoadBalancer null $loadBalancer
Definition: QueryPage.php:72
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)
LinkBatchFactory $linkBatchFactory
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()
Sometimes we don't want to build rss / atom feeds.
LinkRenderer null $linkRenderer
Definition: SpecialPage.php:81
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.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:637
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
Database cluster connection, tracking, load balancing, and transaction manager interface.
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Result wrapper for grabbing data queried from an IDatabase object.
const DB_REPLICA
Definition: defines.php:25
if(!isset( $args[0])) $lang