2013-10-17 00:51:49 +00:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Base class for all changes lists.
|
|
|
|
|
*
|
|
|
|
|
* The class is used for formatting recent changes, related changes and watchlist.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
*/
|
2016-05-13 07:24:29 +00:00
|
|
|
use MediaWiki\Linker\LinkRenderer;
|
|
|
|
|
use MediaWiki\MediaWikiServices;
|
2017-02-19 05:03:13 +00:00
|
|
|
use Wikimedia\Rdbms\ResultWrapper;
|
2013-10-17 00:51:49 +00:00
|
|
|
|
|
|
|
|
class ChangesList extends ContextSource {
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
const CSS_CLASS_PREFIX = 'mw-changeslist-';
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* @var Skin
|
|
|
|
|
*/
|
|
|
|
|
public $skin;
|
|
|
|
|
|
|
|
|
|
protected $watchlist = false;
|
2013-11-19 12:23:46 +00:00
|
|
|
protected $lastdate;
|
2013-10-17 00:51:49 +00:00
|
|
|
protected $message;
|
2013-11-19 12:23:46 +00:00
|
|
|
protected $rc_cache;
|
|
|
|
|
protected $rcCacheIndex;
|
|
|
|
|
protected $rclistOpen;
|
|
|
|
|
protected $rcMoveIndex;
|
2013-10-17 00:51:49 +00:00
|
|
|
|
2016-06-11 00:59:58 +00:00
|
|
|
/** @var callable */
|
|
|
|
|
protected $changeLinePrefixer;
|
|
|
|
|
|
2015-11-03 06:39:09 +00:00
|
|
|
/** @var BagOStuff */
|
|
|
|
|
protected $watchMsgCache;
|
2014-08-20 23:58:13 +00:00
|
|
|
|
2016-05-13 07:24:29 +00:00
|
|
|
/**
|
|
|
|
|
* @var LinkRenderer
|
|
|
|
|
*/
|
|
|
|
|
protected $linkRenderer;
|
|
|
|
|
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
protected $filterGroups;
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* Changeslist constructor
|
|
|
|
|
*
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param Skin|IContextSource $obj
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
* @param array $filterGroups Array of ChangesListFilterGroup objects (currently optional)
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
public function __construct( $obj, array $filterGroups = [] ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
if ( $obj instanceof IContextSource ) {
|
|
|
|
|
$this->setContext( $obj );
|
|
|
|
|
$this->skin = $obj->getSkin();
|
|
|
|
|
} else {
|
|
|
|
|
$this->setContext( $obj->getContext() );
|
|
|
|
|
$this->skin = $obj;
|
|
|
|
|
}
|
|
|
|
|
$this->preCacheMessages();
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->watchMsgCache = new HashBagOStuff( [ 'maxKeys' => 50 ] );
|
2016-05-13 07:24:29 +00:00
|
|
|
$this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
$this->filterGroups = $filterGroups;
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetch an appropriate changes list class for the specified context
|
|
|
|
|
* Some users might want to use an enhanced list format, for instance
|
|
|
|
|
*
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param IContextSource $context
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
* @param array $groups Array of ChangesListFilterGroup objects (currently optional)
|
2014-04-14 19:43:18 +00:00
|
|
|
* @return ChangesList
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
public static function newFromContext( IContextSource $context, array $groups = [] ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$user = $context->getUser();
|
|
|
|
|
$sk = $context->getSkin();
|
|
|
|
|
$list = null;
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( Hooks::run( 'FetchChangesList', [ $user, &$sk, &$list ] ) ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
|
2013-11-18 22:07:49 +00:00
|
|
|
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
return $new ?
|
|
|
|
|
new EnhancedChangesList( $context, $groups ) :
|
|
|
|
|
new OldChangesList( $context, $groups );
|
2013-10-17 00:51:49 +00:00
|
|
|
} else {
|
|
|
|
|
return $list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-13 09:56:04 +00:00
|
|
|
/**
|
|
|
|
|
* Format a line
|
|
|
|
|
*
|
|
|
|
|
* @since 1.27
|
|
|
|
|
*
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param RecentChange &$rc Passed by reference
|
2015-10-13 09:56:04 +00:00
|
|
|
* @param bool $watched (default false)
|
|
|
|
|
* @param int $linenumber (default null)
|
|
|
|
|
*
|
|
|
|
|
* @return string|bool
|
|
|
|
|
*/
|
|
|
|
|
public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
|
|
|
|
|
throw new RuntimeException( 'recentChangesLine should be implemented' );
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param bool $value
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function setWatchlistDivs( $value = true ) {
|
|
|
|
|
$this->watchlist = $value;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 20:32:27 +00:00
|
|
|
/**
|
2014-04-14 19:43:18 +00:00
|
|
|
* @return bool True when setWatchlistDivs has been called
|
2014-03-07 20:32:27 +00:00
|
|
|
* @since 1.23
|
|
|
|
|
*/
|
|
|
|
|
public function isWatchlist() {
|
|
|
|
|
return (bool)$this->watchlist;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* As we use the same small set of messages in various methods and that
|
|
|
|
|
* they are called often, we call them once and save them in $this->message
|
|
|
|
|
*/
|
|
|
|
|
private function preCacheMessages() {
|
|
|
|
|
if ( !isset( $this->message ) ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
foreach ( [
|
2013-10-17 00:51:49 +00:00
|
|
|
'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
|
2016-02-17 09:09:32 +00:00
|
|
|
'semicolon-separator', 'pipe-separator' ] as $msg
|
2013-10-17 00:51:49 +00:00
|
|
|
) {
|
|
|
|
|
$this->message[$msg] = $this->msg( $msg )->escaped();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the appropriate flags for new page, minor change and patrolling
|
|
|
|
|
* @param array $flags Associative array of 'flag' => Bool
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param string $nothing To use for empty space
|
2013-11-19 12:23:46 +00:00
|
|
|
* @return string
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function recentChangesFlags( $flags, $nothing = ' ' ) {
|
|
|
|
|
$f = '';
|
2014-08-22 20:01:08 +00:00
|
|
|
foreach ( array_keys( $this->getConfig()->get( 'RecentChangesFlags' ) ) as $flag ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$f .= isset( $flags[$flag] ) && $flags[$flag]
|
2016-03-08 17:12:38 +00:00
|
|
|
? self::flag( $flag, $this->getContext() )
|
2013-10-17 00:51:49 +00:00
|
|
|
: $nothing;
|
|
|
|
|
}
|
2013-11-18 22:07:49 +00:00
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
return $f;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-28 16:01:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get an array of default HTML class attributes for the change.
|
|
|
|
|
*
|
|
|
|
|
* @param RecentChange|RCCacheEntry $rc
|
|
|
|
|
* @param string|bool $watched Optionally timestamp for adding watched class
|
|
|
|
|
*
|
|
|
|
|
* @return array of classes
|
|
|
|
|
*/
|
|
|
|
|
protected function getHTMLClasses( $rc, $watched ) {
|
2016-06-11 00:59:58 +00:00
|
|
|
$classes = [ self::CSS_CLASS_PREFIX . 'line' ];
|
2015-09-28 16:01:10 +00:00
|
|
|
$logType = $rc->mAttribs['rc_log_type'];
|
|
|
|
|
|
|
|
|
|
if ( $logType ) {
|
2016-06-11 00:59:58 +00:00
|
|
|
$classes[] = self::CSS_CLASS_PREFIX . 'log';
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
$classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'log-' . $logType );
|
2015-09-28 16:01:10 +00:00
|
|
|
} else {
|
2016-06-11 00:59:58 +00:00
|
|
|
$classes[] = self::CSS_CLASS_PREFIX . 'edit';
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
$classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'ns' .
|
2015-09-28 16:01:10 +00:00
|
|
|
$rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
|
|
|
|
|
}
|
2017-09-14 00:18:11 +00:00
|
|
|
$classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'ns-' .
|
|
|
|
|
$rc->mAttribs['rc_namespace'] );
|
2015-09-28 16:01:10 +00:00
|
|
|
|
|
|
|
|
// Indicate watched status on the line to allow for more
|
|
|
|
|
// comprehensive styling.
|
|
|
|
|
$classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
? self::CSS_CLASS_PREFIX . 'line-watched'
|
|
|
|
|
: self::CSS_CLASS_PREFIX . 'line-not-watched';
|
2017-02-10 14:18:02 +00:00
|
|
|
|
|
|
|
|
$classes = array_merge( $classes, $this->getHTMLClassesForFilters( $rc ) );
|
|
|
|
|
|
|
|
|
|
return $classes;
|
|
|
|
|
}
|
|
|
|
|
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
/**
|
|
|
|
|
* Get an array of CSS classes attributed to filters for this row
|
|
|
|
|
*
|
|
|
|
|
* @param RecentChange $rc
|
|
|
|
|
* @return array Array of CSS classes
|
|
|
|
|
*/
|
2017-02-10 14:18:02 +00:00
|
|
|
protected function getHTMLClassesForFilters( $rc ) {
|
|
|
|
|
$classes = [];
|
Back-end of new RecentChanges page, refactoring
Generate old RC, Related changes (it was already displayed and working
on 'Related changes' before this change), and Watchlist/etc. and data
for new UI from back-end.
This moves everything used for defining the old (unstructured) and new
(structured) filters into unified objects, ChangesListFilter and
ChangesListFilterGroup (and sub-classes).
This includes the query logic (see below) and logic for adding
CSS attribution classes.
This is a breaking change (for subclasses of ChangesListSpecialpage)
due to the signature (and name) change of buildMainQueryConds and
doMainQuery. An alternative that I don't think is a breaking change
would be to put the filter->DB logic in runMainQueryHook, but then it's
doing more than just running a hook.
This is because it used to only build $conds here, but it's clear from
filterOnUserExperienceLevel filters need more than this. I added all
the DB parameters from the hook, but this could be debated.
I have an checked and fixed the WMF-deployed extensions affected by
this.
Other than that, there should be full back-compat (including legacy
filters not using the new system).
* add hidepatrolled/hideunpatrolled to new UI.
* Move userExpLevel from RC to ChangesListSpecialPage. Although for
now the structured UI only displays on RC anyway, when it displays on
watchlist, it seems we'll want userExpLevel there.
Change this to make 'all' exclude unregistered users.
* Don't have front-end convert none-selected to 'all' on string_options.
* Needs the hideanons/hideliu special redirect to be done before this
is merged (T151873)
Bug: T152754
Bug: T152797
Change-Id: Iec2d82f6a830403d1c948a280efa58992e0cdee7
2017-02-14 07:55:37 +00:00
|
|
|
|
|
|
|
|
if ( $this->filterGroups !== null ) {
|
|
|
|
|
foreach ( $this->filterGroups as $filterGroup ) {
|
|
|
|
|
foreach ( $filterGroup->getFilters() as $filter ) {
|
|
|
|
|
$filter->applyCssClassIfNeeded( $this, $rc, $classes );
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-10 14:18:02 +00:00
|
|
|
}
|
2015-09-28 16:01:10 +00:00
|
|
|
|
|
|
|
|
return $classes;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
2016-03-08 17:12:38 +00:00
|
|
|
* Make an "<abbr>" element for a given change flag. The flag indicating a new page, minor edit,
|
|
|
|
|
* bot edit, or unpatrolled edit. In English it typically contains "N", "m", "b", or "!".
|
2013-10-17 00:51:49 +00:00
|
|
|
*
|
|
|
|
|
* @param string $flag One key of $wgRecentChangesFlags
|
2016-03-08 17:12:38 +00:00
|
|
|
* @param IContextSource $context
|
|
|
|
|
* @return string HTML
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
2016-03-08 17:12:38 +00:00
|
|
|
public static function flag( $flag, IContextSource $context = null ) {
|
|
|
|
|
static $map = [ 'minoredit' => 'minor', 'botedit' => 'bot' ];
|
2013-10-17 00:51:49 +00:00
|
|
|
static $flagInfos = null;
|
2016-03-08 17:12:38 +00:00
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
if ( is_null( $flagInfos ) ) {
|
|
|
|
|
global $wgRecentChangesFlags;
|
2016-02-17 09:09:32 +00:00
|
|
|
$flagInfos = [];
|
2013-10-17 00:51:49 +00:00
|
|
|
foreach ( $wgRecentChangesFlags as $key => $value ) {
|
2016-03-08 17:12:38 +00:00
|
|
|
$flagInfos[$key]['letter'] = $value['letter'];
|
|
|
|
|
$flagInfos[$key]['title'] = $value['title'];
|
2013-10-17 00:51:49 +00:00
|
|
|
// Allow customized class name, fall back to flag name
|
2016-03-08 17:12:38 +00:00
|
|
|
$flagInfos[$key]['class'] = isset( $value['class'] ) ? $value['class'] : $key;
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-08 17:12:38 +00:00
|
|
|
$context = $context ?: RequestContext::getMain();
|
|
|
|
|
|
|
|
|
|
// Inconsistent naming, kepted for b/c
|
2013-10-17 00:51:49 +00:00
|
|
|
if ( isset( $map[$flag] ) ) {
|
|
|
|
|
$flag = $map[$flag];
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-08 17:12:38 +00:00
|
|
|
$info = $flagInfos[$flag];
|
|
|
|
|
return Html::element( 'abbr', [
|
|
|
|
|
'class' => $info['class'],
|
|
|
|
|
'title' => wfMessage( $info['title'] )->setContext( $context )->text(),
|
|
|
|
|
], wfMessage( $info['letter'] )->setContext( $context )->text() );
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns text for the start of the tabular part of RC
|
2013-11-19 12:23:46 +00:00
|
|
|
* @return string
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
2014-03-26 14:07:44 +00:00
|
|
|
public function beginRecentChangesList() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->rc_cache = [];
|
2013-10-17 00:51:49 +00:00
|
|
|
$this->rcMoveIndex = 0;
|
|
|
|
|
$this->rcCacheIndex = 0;
|
|
|
|
|
$this->lastdate = '';
|
|
|
|
|
$this->rclistOpen = false;
|
|
|
|
|
$this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
|
2013-11-18 22:07:49 +00:00
|
|
|
|
2013-11-26 22:13:51 +00:00
|
|
|
return '<div class="mw-changeslist">';
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
2014-03-03 21:26:54 +00:00
|
|
|
/**
|
|
|
|
|
* @param ResultWrapper|array $rows
|
|
|
|
|
*/
|
|
|
|
|
public function initChangesListRows( $rows ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
Hooks::run( 'ChangesListInitRows', [ $this, $rows ] );
|
2014-03-03 21:26:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* Show formatted char difference
|
2016-07-15 12:16:38 +00:00
|
|
|
*
|
|
|
|
|
* Needs the css module 'mediawiki.special.changeslist' to style output
|
|
|
|
|
*
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param int $old Number of bytes
|
|
|
|
|
* @param int $new Number of bytes
|
|
|
|
|
* @param IContextSource $context
|
|
|
|
|
* @return string
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
|
|
|
|
|
if ( !$context ) {
|
|
|
|
|
$context = RequestContext::getMain();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$new = (int)$new;
|
|
|
|
|
$old = (int)$old;
|
|
|
|
|
$szdiff = $new - $old;
|
|
|
|
|
|
|
|
|
|
$lang = $context->getLanguage();
|
2014-08-22 20:01:08 +00:00
|
|
|
$config = $context->getConfig();
|
2013-10-17 00:51:49 +00:00
|
|
|
$code = $lang->getCode();
|
2016-02-17 09:09:32 +00:00
|
|
|
static $fastCharDiff = [];
|
2013-10-17 00:51:49 +00:00
|
|
|
if ( !isset( $fastCharDiff[$code] ) ) {
|
2015-06-25 19:13:23 +00:00
|
|
|
$fastCharDiff[$code] = $config->get( 'MiserMode' )
|
|
|
|
|
|| $context->msg( 'rc-change-size' )->plain() === '$1';
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$formattedSize = $lang->formatNum( $szdiff );
|
|
|
|
|
|
|
|
|
|
if ( !$fastCharDiff[$code] ) {
|
|
|
|
|
$formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-22 20:01:08 +00:00
|
|
|
if ( abs( $szdiff ) > abs( $config->get( 'RCChangedSizeThreshold' ) ) ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$tag = 'strong';
|
|
|
|
|
} else {
|
|
|
|
|
$tag = 'span';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $szdiff === 0 ) {
|
|
|
|
|
$formattedSizeClass = 'mw-plusminus-null';
|
2013-11-19 12:23:46 +00:00
|
|
|
} elseif ( $szdiff > 0 ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$formattedSize = '+' . $formattedSize;
|
|
|
|
|
$formattedSizeClass = 'mw-plusminus-pos';
|
2013-11-19 12:23:46 +00:00
|
|
|
} else {
|
2013-10-17 00:51:49 +00:00
|
|
|
$formattedSizeClass = 'mw-plusminus-neg';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
|
|
|
|
|
|
|
|
|
|
return Html::element( $tag,
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ],
|
2013-10-17 00:51:49 +00:00
|
|
|
$context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Format the character difference of one or several changes.
|
|
|
|
|
*
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param RecentChange $old
|
|
|
|
|
* @param RecentChange $new Last change to use, if not provided, $old will be used
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string HTML fragment
|
|
|
|
|
*/
|
|
|
|
|
public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
|
|
|
|
|
$oldlen = $old->mAttribs['rc_old_len'];
|
|
|
|
|
|
|
|
|
|
if ( $new ) {
|
|
|
|
|
$newlen = $new->mAttribs['rc_new_len'];
|
|
|
|
|
} else {
|
|
|
|
|
$newlen = $old->mAttribs['rc_new_len'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $oldlen === null || $newlen === null ) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns text for the end of RC
|
2014-04-14 19:43:18 +00:00
|
|
|
* @return string
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function endRecentChangesList() {
|
2013-11-26 22:13:51 +00:00
|
|
|
$out = $this->rclistOpen ? "</ul>\n" : '';
|
|
|
|
|
$out .= '</div>';
|
2014-02-05 11:02:29 +00:00
|
|
|
|
2013-11-26 22:13:51 +00:00
|
|
|
return $out;
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s HTML to update
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param mixed $rc_timestamp
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertDateHeader( &$s, $rc_timestamp ) {
|
|
|
|
|
# Make date header if necessary
|
|
|
|
|
$date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
|
|
|
|
|
if ( $date != $this->lastdate ) {
|
|
|
|
|
if ( $this->lastdate != '' ) {
|
|
|
|
|
$s .= "</ul>\n";
|
|
|
|
|
}
|
|
|
|
|
$s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
|
|
|
|
|
$this->lastdate = $date;
|
|
|
|
|
$this->rclistOpen = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s HTML to update
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param Title $title
|
|
|
|
|
* @param string $logtype
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertLog( &$s, $title, $logtype ) {
|
|
|
|
|
$page = new LogPage( $logtype );
|
2016-05-13 07:24:29 +00:00
|
|
|
$logname = $page->getName()->setContext( $this->getContext() )->text();
|
|
|
|
|
$s .= $this->msg( 'parentheses' )->rawParams(
|
|
|
|
|
$this->linkRenderer->makeKnownLink( $title, $logname )
|
|
|
|
|
)->escaped();
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s HTML to update
|
|
|
|
|
* @param RecentChange &$rc
|
2016-02-11 14:40:42 +00:00
|
|
|
* @param bool|null $unpatrolled Unused variable, since 1.27.
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
2016-02-11 14:40:42 +00:00
|
|
|
public function insertDiffHist( &$s, &$rc, $unpatrolled = null ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
# Diff link
|
2015-08-24 17:40:06 +00:00
|
|
|
if (
|
|
|
|
|
$rc->mAttribs['rc_type'] == RC_NEW ||
|
|
|
|
|
$rc->mAttribs['rc_type'] == RC_LOG ||
|
|
|
|
|
$rc->mAttribs['rc_type'] == RC_CATEGORIZE
|
|
|
|
|
) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$diffLink = $this->message['diff'];
|
|
|
|
|
} elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
|
|
|
|
|
$diffLink = $this->message['diff'];
|
|
|
|
|
} else {
|
2016-02-17 09:09:32 +00:00
|
|
|
$query = [
|
2013-10-17 00:51:49 +00:00
|
|
|
'curid' => $rc->mAttribs['rc_cur_id'],
|
|
|
|
|
'diff' => $rc->mAttribs['rc_this_oldid'],
|
|
|
|
|
'oldid' => $rc->mAttribs['rc_last_oldid']
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2013-10-17 00:51:49 +00:00
|
|
|
|
2016-05-13 07:24:29 +00:00
|
|
|
$diffLink = $this->linkRenderer->makeKnownLink(
|
2013-10-17 00:51:49 +00:00
|
|
|
$rc->getTitle(),
|
2016-05-13 07:24:29 +00:00
|
|
|
new HtmlArmor( $this->message['diff'] ),
|
2017-02-04 01:38:42 +00:00
|
|
|
[ 'class' => 'mw-changeslist-diff' ],
|
2013-10-17 00:51:49 +00:00
|
|
|
$query
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-08-24 17:40:06 +00:00
|
|
|
if ( $rc->mAttribs['rc_type'] == RC_CATEGORIZE ) {
|
|
|
|
|
$diffhist = $diffLink . $this->message['pipe-separator'] . $this->message['hist'];
|
|
|
|
|
} else {
|
|
|
|
|
$diffhist = $diffLink . $this->message['pipe-separator'];
|
|
|
|
|
# History link
|
2016-05-13 07:24:29 +00:00
|
|
|
$diffhist .= $this->linkRenderer->makeKnownLink(
|
2015-08-24 17:40:06 +00:00
|
|
|
$rc->getTitle(),
|
2016-05-13 07:24:29 +00:00
|
|
|
new HtmlArmor( $this->message['hist'] ),
|
2017-02-04 01:38:42 +00:00
|
|
|
[ 'class' => 'mw-changeslist-history' ],
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2015-08-24 17:40:06 +00:00
|
|
|
'curid' => $rc->mAttribs['rc_cur_id'],
|
|
|
|
|
'action' => 'history'
|
2016-02-17 09:09:32 +00:00
|
|
|
]
|
2015-08-24 17:40:06 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-18 22:03:29 +00:00
|
|
|
// @todo FIXME: Hard coded ". .". Is there a message for this? Should there be?
|
|
|
|
|
$s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() .
|
|
|
|
|
' <span class="mw-changeslist-separator">. .</span> ';
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s Article link will be appended to this string, in place.
|
2015-10-19 08:33:54 +00:00
|
|
|
* @param RecentChange $rc
|
|
|
|
|
* @param bool $unpatrolled
|
|
|
|
|
* @param bool $watched
|
|
|
|
|
* @deprecated since 1.27, use getArticleLink instead.
|
|
|
|
|
*/
|
|
|
|
|
public function insertArticleLink( &$s, RecentChange $rc, $unpatrolled, $watched ) {
|
|
|
|
|
$s .= $this->getArticleLink( $rc, $unpatrolled, $watched );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param RecentChange &$rc
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param bool $unpatrolled
|
|
|
|
|
* @param bool $watched
|
2015-10-19 08:33:54 +00:00
|
|
|
* @return string HTML
|
|
|
|
|
* @since 1.26
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
2015-10-19 08:33:54 +00:00
|
|
|
public function getArticleLink( &$rc, $unpatrolled, $watched ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$params = [];
|
2014-04-26 00:50:18 +00:00
|
|
|
if ( $rc->getTitle()->isRedirect() ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$params = [ 'redirect' => 'no' ];
|
2014-04-26 00:50:18 +00:00
|
|
|
}
|
2013-10-17 00:51:49 +00:00
|
|
|
|
2016-05-13 07:24:29 +00:00
|
|
|
$articlelink = $this->linkRenderer->makeLink(
|
2013-10-17 00:51:49 +00:00
|
|
|
$rc->getTitle(),
|
|
|
|
|
null,
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'class' => 'mw-changeslist-title' ],
|
2013-10-17 00:51:49 +00:00
|
|
|
$params
|
|
|
|
|
);
|
|
|
|
|
if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
|
|
|
|
|
$articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
|
|
|
|
|
}
|
|
|
|
|
# To allow for boldening pages watched by this user
|
|
|
|
|
$articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
|
|
|
|
|
# RTL/LTR marker
|
|
|
|
|
$articlelink .= $this->getLanguage()->getDirMark();
|
|
|
|
|
|
2015-10-19 08:33:54 +00:00
|
|
|
# TODO: Deprecate the $s argument, it seems happily unused.
|
|
|
|
|
$s = '';
|
2017-02-01 04:01:54 +00:00
|
|
|
# Avoid PHP 7.1 warning from passing $this by reference
|
|
|
|
|
$changesList = $this;
|
2014-12-09 07:23:30 +00:00
|
|
|
Hooks::run( 'ChangesListInsertArticleLink',
|
2017-02-01 04:01:54 +00:00
|
|
|
[ &$changesList, &$articlelink, &$s, &$rc, $unpatrolled, $watched ] );
|
2013-10-17 00:51:49 +00:00
|
|
|
|
2015-10-19 08:33:54 +00:00
|
|
|
return "{$s} {$articlelink}";
|
2015-07-02 12:09:41 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
|
|
|
|
* Get the timestamp from $rc formatted with current user's settings
|
|
|
|
|
* and a separator
|
|
|
|
|
*
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param RecentChange $rc
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string HTML fragment
|
|
|
|
|
*/
|
|
|
|
|
public function getTimestamp( $rc ) {
|
2013-11-18 22:03:29 +00:00
|
|
|
// @todo FIXME: Hard coded ". .". Is there a message for this? Should there be?
|
2013-10-17 00:51:49 +00:00
|
|
|
return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
|
2013-11-18 22:03:29 +00:00
|
|
|
$this->getLanguage()->userTime(
|
|
|
|
|
$rc->mAttribs['rc_timestamp'],
|
|
|
|
|
$this->getUser()
|
|
|
|
|
) . '</span> <span class="mw-changeslist-separator">. .</span> ';
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert time timestamp string from $rc into $s
|
|
|
|
|
*
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s HTML to update
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param RecentChange $rc
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertTimestamp( &$s, $rc ) {
|
|
|
|
|
$s .= $this->getTimestamp( $rc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert links to user page, user talk page and eventually a blocking link
|
|
|
|
|
*
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param string &$s HTML to update
|
|
|
|
|
* @param RecentChange &$rc
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertUserRelatedLinks( &$s, &$rc ) {
|
|
|
|
|
if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
|
2013-11-18 22:03:29 +00:00
|
|
|
$s .= ' <span class="history-deleted">' .
|
|
|
|
|
$this->msg( 'rev-deleted-user' )->escaped() . '</span>';
|
2013-10-17 00:51:49 +00:00
|
|
|
} else {
|
|
|
|
|
$s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
|
|
|
|
|
$rc->mAttribs['rc_user_text'] );
|
|
|
|
|
$s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert a formatted action
|
|
|
|
|
*
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param RecentChange $rc
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function insertLogEntry( $rc ) {
|
|
|
|
|
$formatter = LogFormatter::newFromRow( $rc->mAttribs );
|
|
|
|
|
$formatter->setContext( $this->getContext() );
|
|
|
|
|
$formatter->setShowUserToolLinks( true );
|
|
|
|
|
$mark = $this->getLanguage()->getDirMark();
|
2013-11-18 22:07:49 +00:00
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
return $formatter->getActionText() . " $mark" . $formatter->getComment();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert a formatted comment
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param RecentChange $rc
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function insertComment( $rc ) {
|
2014-06-05 00:54:02 +00:00
|
|
|
if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
|
|
|
|
|
return ' <span class="history-deleted">' .
|
|
|
|
|
$this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
|
|
|
|
|
} else {
|
|
|
|
|
return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the string which indicates the number of watching users
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param int $count Number of user watching a page
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
protected function numberofWatchingusers( $count ) {
|
2015-11-03 06:39:09 +00:00
|
|
|
if ( $count <= 0 ) {
|
2013-10-17 00:51:49 +00:00
|
|
|
return '';
|
|
|
|
|
}
|
2015-11-03 06:39:09 +00:00
|
|
|
$cache = $this->watchMsgCache;
|
2017-11-20 05:30:45 +00:00
|
|
|
return $cache->getWithSetCallback(
|
|
|
|
|
$cache->makeKey( 'watching-users-msg', $count ),
|
|
|
|
|
$cache::TTL_INDEFINITE,
|
2016-02-10 23:25:29 +00:00
|
|
|
function () use ( $count ) {
|
|
|
|
|
return $this->msg( 'number_of_watching_users_RCview' )
|
2015-11-03 06:39:09 +00:00
|
|
|
->numParams( $count )->escaped();
|
|
|
|
|
}
|
|
|
|
|
);
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determine if said field of a revision is hidden
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param RCCacheEntry|RecentChange $rc
|
|
|
|
|
* @param int $field One of DELETED_* bitfield constants
|
|
|
|
|
* @return bool
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public static function isDeleted( $rc, $field ) {
|
|
|
|
|
return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determine if the current user is allowed to view a particular
|
|
|
|
|
* field of this revision, if it's marked as deleted.
|
2013-11-19 12:23:46 +00:00
|
|
|
* @param RCCacheEntry|RecentChange $rc
|
|
|
|
|
* @param int $field
|
|
|
|
|
* @param User $user User object to check, or null to use $wgUser
|
|
|
|
|
* @return bool
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public static function userCan( $rc, $field, User $user = null ) {
|
|
|
|
|
if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
|
|
|
|
|
return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
|
|
|
|
|
} else {
|
|
|
|
|
return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-04-14 19:43:18 +00:00
|
|
|
* @param string $link
|
|
|
|
|
* @param bool $watched
|
2013-10-17 00:51:49 +00:00
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
protected function maybeWatchedLink( $link, $watched = false ) {
|
|
|
|
|
if ( $watched ) {
|
|
|
|
|
return '<strong class="mw-watched">' . $link . '</strong>';
|
|
|
|
|
} else {
|
|
|
|
|
return '<span class="mw-rc-unwatched">' . $link . '</span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Inserts a rollback link
|
|
|
|
|
*
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s
|
|
|
|
|
* @param RecentChange &$rc
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertRollback( &$s, &$rc ) {
|
2013-11-18 22:03:29 +00:00
|
|
|
if ( $rc->mAttribs['rc_type'] == RC_EDIT
|
|
|
|
|
&& $rc->mAttribs['rc_this_oldid']
|
|
|
|
|
&& $rc->mAttribs['rc_cur_id']
|
|
|
|
|
) {
|
2013-10-17 00:51:49 +00:00
|
|
|
$page = $rc->getTitle();
|
|
|
|
|
/** Check for rollback and edit permissions, disallow special pages, and only
|
2013-11-18 22:07:49 +00:00
|
|
|
* show a link on the top-most revision */
|
2013-11-18 22:03:29 +00:00
|
|
|
if ( $this->getUser()->isAllowed( 'rollback' )
|
|
|
|
|
&& $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid']
|
|
|
|
|
) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$rev = new Revision( [
|
2013-10-17 00:51:49 +00:00
|
|
|
'title' => $page,
|
|
|
|
|
'id' => $rc->mAttribs['rc_this_oldid'],
|
|
|
|
|
'user' => $rc->mAttribs['rc_user'],
|
|
|
|
|
'user_text' => $rc->mAttribs['rc_user_text'],
|
|
|
|
|
'deleted' => $rc->mAttribs['rc_deleted']
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2013-10-17 00:51:49 +00:00
|
|
|
$s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-25 19:13:23 +00:00
|
|
|
/**
|
|
|
|
|
* @param RecentChange $rc
|
|
|
|
|
* @return string
|
|
|
|
|
* @since 1.26
|
|
|
|
|
*/
|
|
|
|
|
public function getRollback( RecentChange $rc ) {
|
|
|
|
|
$s = '';
|
|
|
|
|
$this->insertRollback( $s, $rc );
|
|
|
|
|
return $s;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
/**
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param string &$s
|
|
|
|
|
* @param RecentChange &$rc
|
|
|
|
|
* @param array &$classes
|
2013-10-17 00:51:49 +00:00
|
|
|
*/
|
|
|
|
|
public function insertTags( &$s, &$rc, &$classes ) {
|
|
|
|
|
if ( empty( $rc->mAttribs['ts_tags'] ) ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-18 22:03:29 +00:00
|
|
|
list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
|
|
|
|
|
$rc->mAttribs['ts_tags'],
|
2016-01-29 13:03:41 +00:00
|
|
|
'changeslist',
|
|
|
|
|
$this->getContext()
|
2013-11-18 22:03:29 +00:00
|
|
|
);
|
2013-10-17 00:51:49 +00:00
|
|
|
$classes = array_merge( $classes, $newClasses );
|
|
|
|
|
$s .= ' ' . $tagSummary;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-25 19:13:23 +00:00
|
|
|
/**
|
|
|
|
|
* @param RecentChange $rc
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param array &$classes
|
2015-06-25 19:13:23 +00:00
|
|
|
* @return string
|
|
|
|
|
* @since 1.26
|
|
|
|
|
*/
|
|
|
|
|
public function getTags( RecentChange $rc, array &$classes ) {
|
|
|
|
|
$s = '';
|
|
|
|
|
$this->insertTags( $s, $rc, $classes );
|
|
|
|
|
return $s;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-17 00:51:49 +00:00
|
|
|
public function insertExtra( &$s, &$rc, &$classes ) {
|
|
|
|
|
// Empty, used for subclasses to add anything special.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function showAsUnpatrolled( RecentChange $rc ) {
|
2013-10-25 08:25:48 +00:00
|
|
|
return self::isUnpatrolled( $rc, $this->getUser() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param object|RecentChange $rc Database row from recentchanges or a RecentChange object
|
|
|
|
|
* @param User $user
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public static function isUnpatrolled( $rc, User $user ) {
|
|
|
|
|
if ( $rc instanceof RecentChange ) {
|
|
|
|
|
$isPatrolled = $rc->mAttribs['rc_patrolled'];
|
|
|
|
|
$rcType = $rc->mAttribs['rc_type'];
|
2015-05-17 15:36:29 +00:00
|
|
|
$rcLogType = $rc->mAttribs['rc_log_type'];
|
2013-10-25 08:25:48 +00:00
|
|
|
} else {
|
|
|
|
|
$isPatrolled = $rc->rc_patrolled;
|
|
|
|
|
$rcType = $rc->rc_type;
|
2015-05-17 15:36:29 +00:00
|
|
|
$rcLogType = $rc->rc_log_type;
|
2013-10-25 08:25:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !$isPatrolled ) {
|
|
|
|
|
if ( $user->useRCPatrol() ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if ( $user->useNPPatrol() && $rcType == RC_NEW ) {
|
|
|
|
|
return true;
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
2015-05-17 15:36:29 +00:00
|
|
|
if ( $user->useFilePatrol() && $rcLogType == 'upload' ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
2013-10-25 08:25:48 +00:00
|
|
|
|
|
|
|
|
return false;
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|
2015-08-24 17:40:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines whether a revision is linked to this change; this may not be the case
|
|
|
|
|
* when the categorization wasn't done by an edit but a conditional parser function
|
|
|
|
|
*
|
|
|
|
|
* @since 1.27
|
|
|
|
|
*
|
|
|
|
|
* @param RecentChange|RCCacheEntry $rcObj
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected function isCategorizationWithoutRevision( $rcObj ) {
|
|
|
|
|
return intval( $rcObj->getAttribute( 'rc_type' ) ) === RC_CATEGORIZE
|
|
|
|
|
&& intval( $rcObj->getAttribute( 'rc_this_oldid' ) ) === 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-10 05:31:32 +00:00
|
|
|
/**
|
|
|
|
|
* Get recommended data attributes for a change line.
|
|
|
|
|
* @param RecentChange $rc
|
|
|
|
|
* @return string[] attribute name => value
|
|
|
|
|
*/
|
|
|
|
|
protected function getDataAttributes( RecentChange $rc ) {
|
2017-07-21 15:41:36 +00:00
|
|
|
$attrs = [];
|
|
|
|
|
|
2017-02-10 05:31:32 +00:00
|
|
|
$type = $rc->getAttribute( 'rc_source' );
|
|
|
|
|
switch ( $type ) {
|
|
|
|
|
case RecentChange::SRC_EDIT:
|
|
|
|
|
case RecentChange::SRC_NEW:
|
2017-08-11 14:22:42 +00:00
|
|
|
$attrs['data-mw-revid'] = $rc->mAttribs['rc_this_oldid'];
|
2017-07-21 15:41:36 +00:00
|
|
|
break;
|
2017-02-10 05:31:32 +00:00
|
|
|
case RecentChange::SRC_LOG:
|
2017-08-11 14:22:42 +00:00
|
|
|
$attrs['data-mw-logid'] = $rc->mAttribs['rc_logid'];
|
|
|
|
|
$attrs['data-mw-logaction'] =
|
|
|
|
|
$rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'];
|
2017-07-21 15:41:36 +00:00
|
|
|
break;
|
2017-02-10 05:31:32 +00:00
|
|
|
}
|
2017-07-21 15:41:36 +00:00
|
|
|
|
|
|
|
|
$attrs[ 'data-mw-ts' ] = $rc->getAttribute( 'rc_timestamp' );
|
|
|
|
|
|
|
|
|
|
return $attrs;
|
2017-02-10 05:31:32 +00:00
|
|
|
}
|
2016-06-11 00:59:58 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the callable that generates a change line prefix added to the beginning of each line.
|
|
|
|
|
*
|
|
|
|
|
* @param callable $prefixer Callable to run that generates the change line prefix.
|
|
|
|
|
* Takes three parameters: a RecentChange object, a ChangesList object,
|
|
|
|
|
* and whether the current entry is a grouped entry.
|
|
|
|
|
*/
|
|
|
|
|
public function setChangeLinePrefixer( callable $prefixer ) {
|
|
|
|
|
$this->changeLinePrefixer = $prefixer;
|
|
|
|
|
}
|
2013-10-17 00:51:49 +00:00
|
|
|
}
|