MediaWiki  master
SpecialRecentChangesLinked.php
Go to the documentation of this file.
1 <?php
31  protected $rclTargetTitle;
32 
33  public function __construct() {
34  parent::__construct( 'Recentchangeslinked' );
35  }
36 
37  public function getDefaultOptions() {
38  $opts = parent::getDefaultOptions();
39  $opts->add( 'target', '' );
40  $opts->add( 'showlinkedto', false );
41 
42  return $opts;
43  }
44 
45  public function parseParameters( $par, FormOptions $opts ) {
46  $opts['target'] = $par;
47  }
48 
52  protected function doMainQuery( $tables, $select, $conds, $query_options,
53  $join_conds, FormOptions $opts
54  ) {
55  $target = $opts['target'];
56  $showlinkedto = $opts['showlinkedto'];
57  $limit = $opts['limit'];
58 
59  if ( $target === '' ) {
60  return false;
61  }
62  $outputPage = $this->getOutput();
63  $title = Title::newFromText( $target );
64  if ( !$title || $title->isExternal() ) {
65  $outputPage->addHTML(
66  Html::errorBox( $this->msg( 'allpagesbadtitle' )->parse() )
67  );
68  return false;
69  }
70 
71  $outputPage->setPageTitle( $this->msg( 'recentchangeslinked-title', $title->getPrefixedText() ) );
72 
73  /*
74  * Ordinary links are in the pagelinks table, while transclusions are
75  * in the templatelinks table, categorizations in categorylinks and
76  * image use in imagelinks. We need to somehow combine all these.
77  * Special:Whatlinkshere does this by firing multiple queries and
78  * merging the results, but the code we inherit from our parent class
79  * expects only one result set so we use UNION instead.
80  */
81 
82  $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
83  $id = $title->getArticleID();
84  $ns = $title->getNamespace();
85  $dbkey = $title->getDBkey();
86 
87  $rcQuery = RecentChange::getQueryInfo();
88  $tables = array_merge( $tables, $rcQuery['tables'] );
89  $select = array_merge( $rcQuery['fields'], $select );
90  $join_conds = array_merge( $join_conds, $rcQuery['joins'] );
91 
92  // Join with watchlist and watchlist_expiry tables to highlight watched rows.
93  $this->addWatchlistJoins( $dbr, $tables, $select, $join_conds, $conds );
94 
95  // JOIN on page, used for 'last revision' filter highlight
96  $tables[] = 'page';
97  $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
98  $select[] = 'page_latest';
99 
100  $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
102  $tables,
103  $select,
104  $conds,
105  $join_conds,
106  $query_options,
107  $tagFilter
108  );
109 
110  if ( $dbr->unionSupportsOrderAndLimit() ) {
111  if ( count( $tagFilter ) > 1 ) {
112  // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
113  // To prevent this from causing query performance problems, we need to add
114  // a GROUP BY, and add rc_id to the ORDER BY.
115  $order = [
116  'GROUP BY' => [ 'rc_timestamp', 'rc_id' ],
117  'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ]
118  ];
119  } else {
120  $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
121  }
122  } else {
123  $order = [];
124  }
125 
126  if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
127  $opts )
128  ) {
129  return false;
130  }
131 
132  if ( $ns === NS_CATEGORY && !$showlinkedto ) {
133  // special handling for categories
134  // XXX: should try to make this less kludgy
135  $link_tables = [ 'categorylinks' ];
136  $showlinkedto = true;
137  } else {
138  // for now, always join on these tables; really should be configurable as in whatlinkshere
139  $link_tables = [ 'pagelinks', 'templatelinks' ];
140  // imagelinks only contains links to pages in NS_FILE
141  if ( $ns === NS_FILE || !$showlinkedto ) {
142  $link_tables[] = 'imagelinks';
143  }
144  }
145 
146  if ( $id == 0 && !$showlinkedto ) {
147  return false; // nonexistent pages can't link to any pages
148  }
149 
150  // field name prefixes for all the various tables we might want to join with
151  $prefix = [
152  'pagelinks' => 'pl',
153  'templatelinks' => 'tl',
154  'categorylinks' => 'cl',
155  'imagelinks' => 'il'
156  ];
157 
158  $subsql = []; // SELECT statements to combine with UNION
159 
160  foreach ( $link_tables as $link_table ) {
161  $pfx = $prefix[$link_table];
162 
163  // imagelinks and categorylinks tables have no xx_namespace field,
164  // and have xx_to instead of xx_title
165  if ( $link_table == 'imagelinks' ) {
166  $link_ns = NS_FILE;
167  } elseif ( $link_table == 'categorylinks' ) {
168  $link_ns = NS_CATEGORY;
169  } else {
170  $link_ns = 0;
171  }
172 
173  if ( $showlinkedto ) {
174  // find changes to pages linking to this page
175  if ( $link_ns ) {
176  if ( $ns != $link_ns ) {
177  continue;
178  } // should never happen, but check anyway
179  $subconds = [ "{$pfx}_to" => $dbkey ];
180  } else {
181  $subconds = [ "{$pfx}_namespace" => $ns, "{$pfx}_title" => $dbkey ];
182  }
183  $subjoin = "rc_cur_id = {$pfx}_from";
184  } else {
185  // find changes to pages linked from this page
186  $subconds = [ "{$pfx}_from" => $id ];
187  if ( $link_table == 'imagelinks' || $link_table == 'categorylinks' ) {
188  $subconds["rc_namespace"] = $link_ns;
189  $subjoin = "rc_title = {$pfx}_to";
190  } else {
191  $subjoin = [ "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" ];
192  }
193  }
194 
195  $query = $dbr->selectSQLText(
196  array_merge( $tables, [ $link_table ] ),
197  $select,
198  $conds + $subconds,
199  __METHOD__,
200  $order + $query_options,
201  $join_conds + [ $link_table => [ 'JOIN', $subjoin ] ]
202  );
203 
204  if ( $dbr->unionSupportsOrderAndLimit() ) {
205  $query = $dbr->limitResult( $query, $limit );
206  }
207 
208  $subsql[] = $query;
209  }
210 
211  if ( count( $subsql ) == 0 ) {
212  return false; // should never happen
213  }
214  if ( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) {
215  $sql = $subsql[0];
216  } else {
217  // need to resort and relimit after union
218  $sql = $dbr->unionQueries( $subsql, $dbr::UNION_DISTINCT ) .
219  ' ORDER BY rc_timestamp DESC';
220  $sql = $dbr->limitResult( $sql, $limit, false );
221  }
222 
223  return $dbr->query( $sql, __METHOD__ );
224  }
225 
226  public function setTopText( FormOptions $opts ) {
227  $target = $this->getTargetTitle();
228  if ( $target ) {
229  $this->getOutput()->addBacklinkSubtitle( $target );
230  $this->getSkin()->setRelevantTitle( $target );
231  }
232  }
233 
240  public function getExtraOptions( $opts ) {
241  $extraOpts = parent::getExtraOptions( $opts );
242 
243  $opts->consumeValues( [ 'showlinkedto', 'target' ] );
244 
245  $extraOpts['target'] = [ $this->msg( 'recentchangeslinked-page' )->escaped(),
246  Xml::input( 'target', 40, str_replace( '_', ' ', $opts['target'] ) ) .
247  Xml::check( 'showlinkedto', $opts['showlinkedto'], [ 'id' => 'showlinkedto' ] ) . ' ' .
248  Xml::label( $this->msg( 'recentchangeslinked-to' )->text(), 'showlinkedto' ) ];
249 
250  $this->addHelpLink( 'Help:Related changes' );
251  return $extraOpts;
252  }
253 
257  private function getTargetTitle() {
258  if ( $this->rclTargetTitle === null ) {
259  $opts = $this->getOptions();
260  if ( isset( $opts['target'] ) && $opts['target'] !== '' ) {
261  $this->rclTargetTitle = Title::newFromText( $opts['target'] );
262  } else {
263  $this->rclTargetTitle = false;
264  }
265  }
266 
267  return $this->rclTargetTitle;
268  }
269 
278  public function prefixSearchSubpages( $search, $limit, $offset ) {
279  return $this->prefixSearchString( $search, $limit, $offset );
280  }
281 
282  protected function outputNoResults() {
283  $targetTitle = $this->getTargetTitle();
284  if ( $targetTitle === false ) {
285  $this->getOutput()->addHTML(
287  'div',
288  [ 'class' => 'mw-changeslist-empty mw-changeslist-notargetpage' ],
289  $this->msg( 'recentchanges-notargetpage' )->parse()
290  )
291  );
292  } elseif ( !$targetTitle || $targetTitle->isExternal() ) {
293  $this->getOutput()->addHTML(
295  'div',
296  [ 'class' => 'mw-changeslist-empty mw-changeslist-invalidtargetpage' ],
297  $this->msg( 'allpagesbadtitle' )->parse()
298  )
299  );
300  } else {
301  parent::outputNoResults();
302  }
303  }
304 }
SpecialRecentChangesLinked\getExtraOptions
getExtraOptions( $opts)
Get options to be displayed in a form.
Definition: SpecialRecentChangesLinked.php:240
RecentChange\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new recentchanges object.
Definition: RecentChange.php:246
SpecialRecentChangesLinked\outputNoResults
outputNoResults()
Add the "no results" message to the output.
Definition: SpecialRecentChangesLinked.php:282
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:827
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:328
SpecialRecentChangesLinked\parseParameters
parseParameters( $par, FormOptions $opts)
Process $par and put options found in $opts.
Definition: SpecialRecentChangesLinked.php:45
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:743
SpecialRecentChangesLinked
This is to display changes made to all articles linked in an article.
Definition: SpecialRecentChangesLinked.php:29
Xml\label
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
Definition: Xml.php:362
SpecialRecentChangesLinked\getTargetTitle
getTargetTitle()
Definition: SpecialRecentChangesLinked.php:257
SpecialRecentChangesLinked\setTopText
setTopText(FormOptions $opts)
Send the text to be displayed above the options.
Definition: SpecialRecentChangesLinked.php:226
NS_FILE
const NS_FILE
Definition: Defines.php:75
SpecialRecentChanges\addWatchlistJoins
addWatchlistJoins(IDatabase $dbr, &$tables, &$fields, &$joinConds, &$conds)
Add required values to a query's $tables, $fields, $joinConds, and $conds arrays to join to the watch...
Definition: SpecialRecentChanges.php:294
SpecialPage\getSkin
getSkin()
Shortcut to get the skin being used for this instance.
Definition: SpecialPage.php:763
$dbr
$dbr
Definition: testCompression.php:54
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
Definition: ChangeTags.php:773
SpecialPage\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: SpecialPage.php:863
SpecialPage\prefixSearchString
prefixSearchString( $search, $limit, $offset)
Perform a regular substring search for prefixSearchSubpages.
Definition: SpecialPage.php:534
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2475
ChangesListSpecialPage\runMainQueryHook
runMainQueryHook(&$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts)
Definition: ChangesListSpecialPage.php:1641
Xml\check
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
Definition: Xml.php:327
$title
$title
Definition: testCompression.php:38
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:83
Html\errorBox
static errorBox( $html, $heading='', $className='')
Return an error box.
Definition: Html.php:739
SpecialRecentChangesLinked\prefixSearchSubpages
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Definition: SpecialRecentChangesLinked.php:278
SpecialRecentChangesLinked\__construct
__construct()
Definition: SpecialRecentChangesLinked.php:33
SpecialRecentChangesLinked\doMainQuery
doMainQuery( $tables, $select, $conds, $query_options, $join_conds, FormOptions $opts)
Process the query.Array of tables; see IDatabase::select $table Array of fields; see IDatabase::selec...
Definition: SpecialRecentChangesLinked.php:52
SpecialRecentChanges
A special page that lists last changes made to the wiki.
Definition: SpecialRecentChanges.php:33
Title
Represents a title within MediaWiki.
Definition: Title.php:41
SpecialRecentChangesLinked\getDefaultOptions
getDefaultOptions()
Get a FormOptions object containing the default options.
Definition: SpecialRecentChangesLinked.php:37
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
Xml\input
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
Definition: Xml.php:278
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
SpecialRecentChangesLinked\$rclTargetTitle
bool Title $rclTargetTitle
Definition: SpecialRecentChangesLinked.php:31
ChangesListSpecialPage\getOptions
getOptions()
Get the current FormOptions for this request.
Definition: ChangesListSpecialPage.php:1040