42 private $searchEngineFactory;
61 $this->mName =
'Recentchangeslinked';
62 $this->searchEngineFactory = $searchEngineFactory;
66 $opts = parent::getDefaultOptions();
67 $opts->add(
'target',
'' );
68 $opts->add(
'showlinkedto',
false );
74 $opts[
'target'] = $par;
82 protected function doMainQuery( $tables, $select, $conds, $query_options,
85 $target = $opts[
'target'];
86 $showlinkedto = $opts[
'showlinkedto'];
87 $limit = $opts[
'limit'];
89 if ( $target ===
'' ) {
93 $title = Title::newFromText( $target );
96 Html::errorBox( $this->
msg(
'allpagesbadtitle' )->parse(),
'',
'mw-recentchangeslinked-errorbox' )
101 $outputPage->setPageTitle( $this->
msg(
'recentchangeslinked-title',
$title->getPrefixedText() ) );
112 $id =
$title->getArticleID();
113 $ns =
$title->getNamespace();
114 $dbkey =
$title->getDBkey();
117 $tables = array_unique( array_merge( $rcQuery[
'tables'], $tables ) );
118 $select = array_unique( array_merge( $rcQuery[
'fields'], $select ) );
119 $join_conds = array_merge( $rcQuery[
'joins'], $join_conds );
126 $join_conds[
'page'] = [
'LEFT JOIN',
'rc_cur_id=page_id' ];
127 $select[] =
'page_latest';
129 $tagFilter = $opts[
'tagfilter'] !==
'' ? explode(
'|', $opts[
'tagfilter'] ) : [];
140 if (
$dbr->unionSupportsOrderAndLimit() ) {
141 if ( in_array(
'DISTINCT', $query_options ) ) {
146 'GROUP BY' => [
'rc_timestamp',
'rc_id' ],
147 'ORDER BY' => [
'rc_timestamp DESC',
'rc_id DESC' ]
150 $order = [
'ORDER BY' =>
'rc_timestamp DESC' ];
156 if ( !$this->
runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
165 $link_tables = [
'categorylinks' ];
166 $showlinkedto =
true;
169 $link_tables = [
'pagelinks',
'templatelinks' ];
171 if ( $ns ===
NS_FILE || !$showlinkedto ) {
172 $link_tables[] =
'imagelinks';
176 if ( $id == 0 && !$showlinkedto ) {
183 'templatelinks' =>
'tl',
184 'categorylinks' =>
'cl',
190 foreach ( $link_tables as $link_table ) {
191 $queryBuilder =
$dbr->newSelectQueryBuilder();
193 $queryBuilder = $queryBuilder
197 ->caller( __METHOD__ )
198 ->options( $order + $query_options )
199 ->joinConds( $join_conds );
200 $pfx = $prefix[$link_table];
204 if ( $link_table ==
'imagelinks' ) {
206 } elseif ( $link_table ==
'categorylinks' ) {
212 if ( $showlinkedto ) {
215 if ( $ns != $link_ns ) {
218 $queryBuilder->where( [
"{$pfx}_to" => $dbkey ] );
220 if ( isset( $linksMigration::$mapping[$link_table] ) ) {
221 $queryBuilder->where( $linksMigration->getLinksConditions( $link_table,
$title ) );
223 $queryBuilder->where( [
"{$pfx}_namespace" => $ns,
"{$pfx}_title" => $dbkey ] );
226 $queryBuilder->join( $link_table,
null,
"rc_cur_id = {$pfx}_from" );
229 $queryBuilder->where( [
"{$pfx}_from" => $id ] );
230 if ( $link_table ==
'imagelinks' || $link_table ==
'categorylinks' ) {
231 $queryBuilder->where( [
"rc_namespace" => $link_ns ] );
232 $queryBuilder->join( $link_table,
null,
"rc_title = {$pfx}_to" );
235 if ( isset( $linksMigration::$mapping[$link_table] ) ) {
236 $queryInfo = $linksMigration->getQueryInfo( $link_table, $link_table );
237 [ $nsField, $titleField ] = $linksMigration->getTitleFields( $link_table );
238 if ( in_array(
'linktarget', $queryInfo[
'tables'] ) ) {
239 $joinTable =
'linktarget';
241 $joinTable = $link_table;
246 [
"rc_namespace = {$nsField}",
"rc_title = {$titleField}" ]
248 if ( in_array(
'linktarget', $queryInfo[
'tables'] ) ) {
249 $queryBuilder->joinConds( $queryInfo[
'joins'] );
250 $queryBuilder->table( $link_table );
256 [
"rc_namespace = {$pfx}_namespace",
"rc_title = {$pfx}_title" ]
262 if (
$dbr->unionSupportsOrderAndLimit() ) {
263 $queryBuilder->limit( $limit );
266 $subsql[] = $queryBuilder;
269 if ( count( $subsql ) == 0 ) {
272 if ( count( $subsql ) == 1 &&
$dbr->unionSupportsOrderAndLimit() ) {
274 ->setMaxExecutionTime( $this->
getConfig()->
get( MainConfigNames::MaxExecutionTimeForExpensiveQueries ) )
275 ->caller( __METHOD__ )->fetchResultSet();
277 $sqls = array_map(
static function ( $queryBuilder ) {
278 return $queryBuilder->getSQL();
280 return $dbr->newSelectQueryBuilder()
283 new Subquery(
$dbr->unionQueries( $sqls, $dbr::UNION_DISTINCT ) ),
286 ->orderBy(
'rc_timestamp', SelectQueryBuilder::SORT_DESC )
287 ->setMaxExecutionTime( $this->
getConfig()->
get( MainConfigNames::MaxExecutionTimeForExpensiveQueries ) )
289 ->caller( __METHOD__ )->fetchResultSet();
294 $target = $this->getTargetTitle();
296 $this->
getOutput()->addBacklinkSubtitle( $target );
297 $this->
getSkin()->setRelevantTitle( $target );
308 $extraOpts = parent::getExtraOptions( $opts );
310 $opts->consumeValues( [
'showlinkedto',
'target' ] );
312 $extraOpts[
'target'] = [ $this->
msg(
'recentchangeslinked-page' )->escaped(),
313 Xml::input(
'target', 40, str_replace(
'_',
' ', $opts[
'target'] ) ) .
314 Xml::check(
'showlinkedto', $opts[
'showlinkedto'], [
'id' =>
'showlinkedto' ] ) .
' ' .
315 Xml::label( $this->
msg(
'recentchangeslinked-to' )->text(),
'showlinkedto' ) ];
324 private function getTargetTitle() {
325 if ( $this->rclTargetTitle ===
null ) {
327 if ( isset( $opts[
'target'] ) && $opts[
'target'] !==
'' ) {
328 $this->rclTargetTitle = Title::newFromText( $opts[
'target'] );
330 $this->rclTargetTitle =
false;
346 return $this->
prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
350 $targetTitle = $this->getTargetTitle();
351 if ( $targetTitle ===
false ) {
355 [
'class' =>
'mw-changeslist-empty mw-changeslist-notargetpage' ],
356 $this->
msg(
'recentchanges-notargetpage' )->parse()
359 } elseif ( !$targetTitle || $targetTitle->isExternal() ) {
363 [
'class' =>
'mw-changeslist-empty mw-changeslist-invalidtargetpage' ],
364 $this->
msg(
'allpagesbadtitle' )->parse()
368 parent::outputNoResults();
runMainQueryHook(&$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts)
getOptions()
Get the current FormOptions for this request.
getDB()
Which database to use for read queries.
A class containing constants representing the names of configuration variables.
Cache messages that are defined by MediaWiki-namespace pages or by hooks.
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new recentchanges object.
Factory class for SearchEngine.
getOutput()
Get the OutputPage being used for this instance.
getSkin()
Shortcut to get the skin being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
This is to display changes made to all articles linked in an article.
setTopText(FormOptions $opts)
Send the text to be displayed above the options.
doMainQuery( $tables, $select, $conds, $query_options, $join_conds, FormOptions $opts)
FIXME: Port useful changes from SpecialRecentChanges.
parseParameters( $par, FormOptions $opts)
Process $par and put options found in $opts.
getDefaultOptions()
Get a FormOptions object containing the default options.
bool Title $rclTargetTitle
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
outputNoResults()
Add the "no results" message to the output.
__construct(WatchedItemStoreInterface $watchedItemStore, MessageCache $messageCache, UserOptionsLookup $userOptionsLookup, SearchEngineFactory $searchEngineFactory)
getExtraOptions( $opts)
Get options to be displayed in a form.
A special page that lists last changes made to the wiki.
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...
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.