Merge "Add watchlist expiry support to applicable APIs"
This commit is contained in:
commit
e9ad97eead
22 changed files with 579 additions and 156 deletions
|
|
@ -156,6 +156,7 @@ $wgAutoloadLocalClasses = [
|
|||
'ApiUserrights' => __DIR__ . '/includes/api/ApiUserrights.php',
|
||||
'ApiValidatePassword' => __DIR__ . '/includes/api/ApiValidatePassword.php',
|
||||
'ApiWatch' => __DIR__ . '/includes/api/ApiWatch.php',
|
||||
'ApiWatchlistTrait' => __DIR__ . '/includes/api/ApiWatchlistTrait.php',
|
||||
'ArchivedFile' => __DIR__ . '/includes/filerepo/file/ArchivedFile.php',
|
||||
'Argon2Password' => __DIR__ . '/includes/password/Argon2Password.php',
|
||||
'ArrayDiffFormatter' => __DIR__ . '/includes/diff/ArrayDiffFormatter.php',
|
||||
|
|
|
|||
|
|
@ -1021,18 +1021,13 @@ class EditPage implements IEditObject {
|
|||
$this->minoredit = $request->getCheck( 'wpMinoredit' );
|
||||
$this->watchthis = $request->getCheck( 'wpWatchthis' );
|
||||
if ( $this->watchlistExpiryEnabled ) {
|
||||
// Set the watchlistExpiry value: this will either be what's posted by the user,
|
||||
// if we're saving the page; or what's already in the DB, if we're previewing.
|
||||
// The posted value for previewing will be retrieved
|
||||
// separately in $this->getCheckboxesDefinitionForWatchlist().
|
||||
if ( $this->preview ) {
|
||||
$watchedItem = $this->watchedItemStore->getWatchedItem( $user, $this->getTitle() );
|
||||
$this->watchlistExpiry = $watchedItem ? $watchedItem->getExpiry() : null;
|
||||
} else {
|
||||
$expiry = ExpiryDef::normalizeExpiry( $request->getText( 'wpWatchlistExpiry' ) );
|
||||
if ( $expiry !== false ) {
|
||||
$this->watchlistExpiry = $expiry;
|
||||
}
|
||||
// This parsing of the user-posted expiry is done for both preview and saving. This
|
||||
// is necessary because ApiEditPage uses preview when it saves (yuck!). Note that it
|
||||
// only works because the unnormalized value is retrieved again below in
|
||||
// getCheckboxesDefinitionForWatchlist().
|
||||
$expiry = ExpiryDef::normalizeExpiry( $request->getText( 'wpWatchlistExpiry' ) );
|
||||
if ( $expiry !== false ) {
|
||||
$this->watchlistExpiry = $expiry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ use Wikimedia\Rdbms\IDatabase;
|
|||
abstract class ApiBase extends ContextSource {
|
||||
|
||||
use ApiBlockInfoTrait;
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
/** @var HookContainer */
|
||||
private $hookContainer;
|
||||
|
|
@ -1039,46 +1040,6 @@ abstract class ApiBase extends ContextSource {
|
|||
return $titleObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we're to watch the page, false if not, null if no change.
|
||||
* @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
|
||||
* @param Title $titleObj The page under consideration
|
||||
* @param string|null $userOption The user option to consider when $watchlist=preferences.
|
||||
* If not set will use watchdefault always and watchcreations if $titleObj doesn't exist.
|
||||
* @return bool
|
||||
*/
|
||||
protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
|
||||
$userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS );
|
||||
|
||||
switch ( $watchlist ) {
|
||||
case 'watch':
|
||||
return true;
|
||||
|
||||
case 'unwatch':
|
||||
return false;
|
||||
|
||||
case 'preferences':
|
||||
# If the user is already watching, don't bother checking
|
||||
if ( $userWatching ) {
|
||||
return true;
|
||||
}
|
||||
# If no user option was passed, use watchdefault and watchcreations
|
||||
if ( $userOption === null ) {
|
||||
return $this->getUser()->getBoolOption( 'watchdefault' ) ||
|
||||
$this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists();
|
||||
}
|
||||
|
||||
# Watch the article based on the user preference
|
||||
return $this->getUser()->getBoolOption( $userOption );
|
||||
|
||||
case 'nochange':
|
||||
return $userWatching;
|
||||
|
||||
default:
|
||||
return $userWatching;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the settings determine the value for the given parameter
|
||||
*
|
||||
|
|
@ -1163,21 +1124,6 @@ abstract class ApiBase extends ContextSource {
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set a watch (or unwatch) based the based on a watchlist parameter.
|
||||
* @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
|
||||
* @param Title $titleObj The article's title to change
|
||||
* @param string|null $userOption The user option to consider when $watch=preferences
|
||||
*/
|
||||
protected function setWatch( $watch, $titleObj, $userOption = null ) {
|
||||
$value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
|
||||
if ( $value === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user for whom to get the watchlist
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,6 +29,16 @@ use MediaWiki\MediaWikiServices;
|
|||
* @ingroup API
|
||||
*/
|
||||
class ApiDelete extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the title and reason from the request parameters and invokes
|
||||
* the local delete() function with these as arguments. It does not make use of
|
||||
|
|
@ -91,12 +101,15 @@ class ApiDelete extends ApiBase {
|
|||
} else {
|
||||
$watch = $params['watchlist'];
|
||||
}
|
||||
$this->setWatch( $watch, $titleObj, 'watchdeletion' );
|
||||
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $params );
|
||||
$this->setWatch( $watch, $titleObj, 'watchdeletion', $watchlistExpiry );
|
||||
|
||||
$r = [
|
||||
'title' => $titleObj->getPrefixedText(),
|
||||
'reason' => $reason,
|
||||
];
|
||||
|
||||
if ( $status->hasMessage( 'delete-scheduled' ) ) {
|
||||
$r['scheduled'] = true;
|
||||
}
|
||||
|
|
@ -200,7 +213,7 @@ class ApiDelete extends ApiBase {
|
|||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
$params = [
|
||||
'title' => null,
|
||||
'pageid' => [
|
||||
ApiBase::PARAM_TYPE => 'integer'
|
||||
|
|
@ -214,15 +227,13 @@ class ApiDelete extends ApiBase {
|
|||
ApiBase::PARAM_DFLT => false,
|
||||
ApiBase::PARAM_DEPRECATED => true,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Params appear in the docs in the order they are defined,
|
||||
// which is why this is here and not at the bottom.
|
||||
$params += $this->getWatchlistParams();
|
||||
|
||||
return $params + [
|
||||
'unwatch' => [
|
||||
ApiBase::PARAM_DFLT => false,
|
||||
ApiBase::PARAM_DEPRECATED => true,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@ use MediaWiki\Revision\SlotRecord;
|
|||
* @ingroup API
|
||||
*/
|
||||
class ApiEditPage extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$this->useTransactionalTimeLimit();
|
||||
|
||||
|
|
@ -357,6 +367,7 @@ class ApiEditPage extends ApiBase {
|
|||
}
|
||||
|
||||
$watch = $this->getWatchlistValue( $params['watchlist'], $titleObj );
|
||||
$watchlistExpiry = $params['watchlistexpiry'] ?? null;
|
||||
|
||||
// Deprecated parameters
|
||||
if ( $params['watch'] ) {
|
||||
|
|
@ -367,6 +378,10 @@ class ApiEditPage extends ApiBase {
|
|||
|
||||
if ( $watch ) {
|
||||
$requestArray['wpWatchthis'] = '';
|
||||
|
||||
if ( $this->watchlistExpiryEnabled && $watchlistExpiry ) {
|
||||
$requestArray['wpWatchlistExpiry'] = $watchlistExpiry;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply change tags
|
||||
|
|
@ -463,6 +478,14 @@ class ApiEditPage extends ApiBase {
|
|||
$r['newtimestamp'] = wfTimestamp( TS_ISO_8601,
|
||||
$pageObj->getTimestamp() );
|
||||
}
|
||||
|
||||
if ( $watch ) {
|
||||
$r['watched'] = $status->isOK();
|
||||
|
||||
if ( $this->watchlistExpiryEnabled ) {
|
||||
$r['watchlistexpiry'] = ApiResult::formatExpiry( $watchlistExpiry );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -540,7 +563,7 @@ class ApiEditPage extends ApiBase {
|
|||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
$params = [
|
||||
'title' => [
|
||||
ApiBase::PARAM_TYPE => 'string',
|
||||
],
|
||||
|
|
@ -582,15 +605,13 @@ class ApiEditPage extends ApiBase {
|
|||
ApiBase::PARAM_DFLT => false,
|
||||
ApiBase::PARAM_DEPRECATED => true,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Params appear in the docs in the order they are defined,
|
||||
// which is why this is here and not at the bottom.
|
||||
$params += $this->getWatchlistParams();
|
||||
|
||||
return $params + [
|
||||
'md5' => null,
|
||||
'prependtext' => [
|
||||
ApiBase::PARAM_TYPE => 'text',
|
||||
|
|
|
|||
|
|
@ -28,6 +28,15 @@ use MediaWiki\MediaWikiServices;
|
|||
*/
|
||||
class ApiMove extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$this->useTransactionalTimeLimit();
|
||||
|
||||
|
|
@ -157,10 +166,11 @@ class ApiMove extends ApiBase {
|
|||
if ( isset( $params['watchlist'] ) ) {
|
||||
$watch = $params['watchlist'];
|
||||
}
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $params );
|
||||
|
||||
// Watch pages
|
||||
$this->setWatch( $watch, $fromTitle, 'watchmoves' );
|
||||
$this->setWatch( $watch, $toTitle, 'watchmoves' );
|
||||
$this->setWatch( $watch, $fromTitle, 'watchmoves', $watchlistExpiry );
|
||||
$this->setWatch( $watch, $toTitle, 'watchmoves', $watchlistExpiry );
|
||||
|
||||
$result->addValue( null, $this->getModuleName(), $r );
|
||||
}
|
||||
|
|
@ -238,7 +248,7 @@ class ApiMove extends ApiBase {
|
|||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
$params = [
|
||||
'from' => null,
|
||||
'fromid' => [
|
||||
ApiBase::PARAM_TYPE => 'integer'
|
||||
|
|
@ -251,15 +261,13 @@ class ApiMove extends ApiBase {
|
|||
'movetalk' => false,
|
||||
'movesubpages' => false,
|
||||
'noredirect' => false,
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Params appear in the docs in the order they are defined,
|
||||
// which is why this is here and not at the bottom.
|
||||
$params += $this->getWatchlistParams();
|
||||
|
||||
return $params + [
|
||||
'ignorewarnings' => false,
|
||||
'tags' => [
|
||||
ApiBase::PARAM_TYPE => 'tags',
|
||||
|
|
|
|||
|
|
@ -24,6 +24,16 @@
|
|||
* @ingroup API
|
||||
*/
|
||||
class ApiProtect extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$params = $this->extractRequestParams();
|
||||
|
||||
|
|
@ -102,7 +112,8 @@ class ApiProtect extends ApiBase {
|
|||
$cascade = $params['cascade'];
|
||||
|
||||
$watch = $params['watch'] ? 'watch' : $params['watchlist'];
|
||||
$this->setWatch( $watch, $titleObj, 'watchdefault' );
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $params );
|
||||
$this->setWatch( $watch, $titleObj, 'watchdefault', $watchlistExpiry );
|
||||
|
||||
$status = $pageObj->doUpdateRestrictions(
|
||||
$protections,
|
||||
|
|
@ -164,16 +175,7 @@ class ApiProtect extends ApiBase {
|
|||
ApiBase::PARAM_DFLT => false,
|
||||
ApiBase::PARAM_DEPRECATED => true,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
] + $this->getWatchlistParams();
|
||||
}
|
||||
|
||||
public function needsToken() {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,15 @@ use MediaWiki\User\UserIdentity;
|
|||
*/
|
||||
class ApiRollback extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Title
|
||||
*/
|
||||
|
|
@ -82,9 +91,10 @@ class ApiRollback extends ApiBase {
|
|||
}
|
||||
|
||||
$watch = $params['watchlist'] ?? 'preferences';
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $params );
|
||||
|
||||
// Watch pages
|
||||
$this->setWatch( $watch, $titleObj, 'watchrollback' );
|
||||
$this->setWatch( $watch, $titleObj, 'watchrollback', $watchlistExpiry );
|
||||
|
||||
$currentRevisionRecord = $details['current-revision-record'];
|
||||
$targetRevisionRecord = $details['target-revision-record'];
|
||||
|
|
@ -112,7 +122,7 @@ class ApiRollback extends ApiBase {
|
|||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
$params = [
|
||||
'title' => null,
|
||||
'pageid' => [
|
||||
ApiBase::PARAM_TYPE => 'integer'
|
||||
|
|
@ -129,15 +139,13 @@ class ApiRollback extends ApiBase {
|
|||
],
|
||||
'summary' => '',
|
||||
'markbot' => false,
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Params appear in the docs in the order they are defined,
|
||||
// which is why this is here (we want it above the token param).
|
||||
$params += $this->getWatchlistParams();
|
||||
|
||||
return $params + [
|
||||
'token' => [
|
||||
// Standard definition automatically inserted
|
||||
ApiBase::PARAM_HELP_MSG_APPEND => [ 'api-help-param-token-webui' ],
|
||||
|
|
|
|||
|
|
@ -25,6 +25,15 @@
|
|||
*/
|
||||
class ApiUndelete extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$this->useTransactionalTimeLimit();
|
||||
|
||||
|
|
@ -83,7 +92,8 @@ class ApiUndelete extends ApiBase {
|
|||
$this->getUser(), $params['reason'] );
|
||||
}
|
||||
|
||||
$this->setWatch( $params['watchlist'], $titleObj );
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $params );
|
||||
$this->setWatch( $params['watchlist'], $titleObj, null, $watchlistExpiry );
|
||||
|
||||
$info = [
|
||||
'title' => $titleObj->getPrefixedText(),
|
||||
|
|
@ -121,16 +131,7 @@ class ApiUndelete extends ApiBase {
|
|||
ApiBase::PARAM_TYPE => 'integer',
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
] + $this->getWatchlistParams();
|
||||
}
|
||||
|
||||
public function needsToken() {
|
||||
|
|
|
|||
|
|
@ -24,11 +24,21 @@
|
|||
* @ingroup API
|
||||
*/
|
||||
class ApiUpload extends ApiBase {
|
||||
|
||||
use ApiWatchlistTrait;
|
||||
|
||||
/** @var UploadBase|UploadFromChunks */
|
||||
protected $mUpload = null;
|
||||
|
||||
protected $mParams;
|
||||
|
||||
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
|
||||
parent::__construct( $mainModule, $moduleName, $modulePrefix );
|
||||
|
||||
$this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
|
||||
$this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
// Check whether upload is enabled
|
||||
if ( !UploadBase::isEnabled() ) {
|
||||
|
|
@ -825,6 +835,7 @@ class ApiUpload extends ApiBase {
|
|||
$this->getWatchlistValue( 'preferences', $file->getTitle(), 'watchcreations' )
|
||||
);
|
||||
}
|
||||
$watchlistExpiry = $this->getExpiryFromParams( $this->mParams );
|
||||
|
||||
// Deprecated parameters
|
||||
if ( $this->mParams['watch'] ) {
|
||||
|
|
@ -859,6 +870,7 @@ class ApiUpload extends ApiBase {
|
|||
'tags' => $this->mParams['tags'],
|
||||
'text' => $this->mParams['text'],
|
||||
'watch' => $watch,
|
||||
'watchlistexpiry' => $watchlistExpiry,
|
||||
'session' => $this->getContext()->exportSession()
|
||||
]
|
||||
) );
|
||||
|
|
@ -866,8 +878,14 @@ class ApiUpload extends ApiBase {
|
|||
$result['stage'] = 'queued';
|
||||
} else {
|
||||
/** @var Status $status */
|
||||
$status = $this->mUpload->performUpload( $this->mParams['comment'],
|
||||
$this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags'] );
|
||||
$status = $this->mUpload->performUpload(
|
||||
$this->mParams['comment'],
|
||||
$this->mParams['text'],
|
||||
$watch,
|
||||
$this->getUser(),
|
||||
$this->mParams['tags'],
|
||||
$watchlistExpiry
|
||||
);
|
||||
|
||||
if ( !$status->isGood() ) {
|
||||
$this->dieRecoverableError( $status->getErrors() );
|
||||
|
|
@ -910,14 +928,17 @@ class ApiUpload extends ApiBase {
|
|||
ApiBase::PARAM_DFLT => false,
|
||||
ApiBase::PARAM_DEPRECATED => true,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_DFLT => 'preferences',
|
||||
ApiBase::PARAM_TYPE => [
|
||||
'watch',
|
||||
'preferences',
|
||||
'nochange'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Params appear in the docs in the order they are defined,
|
||||
// which is why this is here and not at the bottom.
|
||||
$params += $this->getWatchlistParams( [
|
||||
'watch',
|
||||
'preferences',
|
||||
'nochange',
|
||||
] );
|
||||
|
||||
$params += [
|
||||
'ignorewarnings' => false,
|
||||
'file' => [
|
||||
ApiBase::PARAM_TYPE => 'upload',
|
||||
|
|
|
|||
148
includes/api/ApiWatchlistTrait.php
Normal file
148
includes/api/ApiWatchlistTrait.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
use Wikimedia\ParamValidator\TypeDef\ExpiryDef;
|
||||
|
||||
/**
|
||||
* An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to
|
||||
* watch a page. This should ONLY be used in API modules that extend ApiBase.
|
||||
* Also, it should not be used in ApiWatch, which has its own special handling.
|
||||
*
|
||||
* Note the class-level properties watchlistExpiryEnabled and watchlistMaxDuration must still be
|
||||
* set in the API module's constructor.
|
||||
*
|
||||
* @ingroup API
|
||||
* @since 1.35
|
||||
*/
|
||||
trait ApiWatchlistTrait {
|
||||
|
||||
/** @var bool Whether watchlist expiries are enabled. */
|
||||
private $watchlistExpiryEnabled;
|
||||
|
||||
/** @var string Relative maximum expiry. */
|
||||
private $watchlistMaxDuration;
|
||||
|
||||
/**
|
||||
* Get additional allow params specific to watchlisting.
|
||||
* This should be merged in with the result of self::getAllowedParams().
|
||||
*
|
||||
* This purposefully does not include the deprecated 'watch' and 'unwatch'
|
||||
* parameters that some APIs still accept.
|
||||
*
|
||||
* @param string[] $watchOptions
|
||||
* @return array
|
||||
*/
|
||||
protected function getWatchlistParams( array $watchOptions = [] ): array {
|
||||
if ( !$watchOptions ) {
|
||||
$watchOptions = [
|
||||
'watch',
|
||||
'unwatch',
|
||||
'preferences',
|
||||
'nochange',
|
||||
];
|
||||
}
|
||||
|
||||
$result = [
|
||||
'watchlist' => [
|
||||
ParamValidator::PARAM_DEFAULT => 'preferences',
|
||||
ParamValidator::PARAM_TYPE => $watchOptions,
|
||||
],
|
||||
];
|
||||
|
||||
if ( $this->watchlistExpiryEnabled ) {
|
||||
$result['watchlistexpiry'] = [
|
||||
ParamValidator::PARAM_TYPE => 'expiry',
|
||||
ExpiryDef::PARAM_MAX => $this->watchlistMaxDuration,
|
||||
ExpiryDef::PARAM_USE_MAX => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a watch (or unwatch) based the based on a watchlist parameter.
|
||||
* @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
|
||||
* @param Title $titleObj The article's title to change
|
||||
* @param string|null $userOption The user option to consider when $watch=preferences
|
||||
* @param string|null $expiry Optional expiry timestamp in any format acceptable to wfTimestamp(),
|
||||
* null will not create expiries, or leave them unchanged should they already exist.
|
||||
*/
|
||||
protected function setWatch(
|
||||
string $watch,
|
||||
Title $titleObj,
|
||||
?string $userOption = null,
|
||||
?string $expiry = null
|
||||
): void {
|
||||
$value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
|
||||
WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser(), $expiry );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we're to watch the page, false if not.
|
||||
* @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
|
||||
* @param Title $titleObj The page under consideration
|
||||
* @param string|null $userOption The user option to consider when $watchlist=preferences.
|
||||
* If not set will use watchdefault always and watchcreations if $titleObj doesn't exist.
|
||||
* @return bool
|
||||
*/
|
||||
protected function getWatchlistValue(
|
||||
string $watchlist,
|
||||
Title $titleObj,
|
||||
?string $userOption = null
|
||||
): bool {
|
||||
$user = $this->getUser();
|
||||
$userWatching = $user->isWatched( $titleObj, User::IGNORE_USER_RIGHTS );
|
||||
|
||||
switch ( $watchlist ) {
|
||||
case 'watch':
|
||||
return true;
|
||||
|
||||
case 'unwatch':
|
||||
return false;
|
||||
|
||||
case 'preferences':
|
||||
// If the user is already watching, don't bother checking
|
||||
if ( $userWatching ) {
|
||||
return true;
|
||||
}
|
||||
// If no user option was passed, use watchdefault and watchcreations
|
||||
if ( $userOption === null ) {
|
||||
return $user->getBoolOption( 'watchdefault' ) ||
|
||||
$user->getBoolOption( 'watchcreations' ) &&
|
||||
!$titleObj->exists();
|
||||
}
|
||||
|
||||
// Watch the article based on the user preference
|
||||
return $user->getBoolOption( $userOption );
|
||||
|
||||
case 'nochange':
|
||||
return $userWatching;
|
||||
|
||||
default:
|
||||
return $userWatching;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted expiry from the given parameters, or null if no expiry was provided.
|
||||
* @param array $params Request parameters passed to the API.
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getExpiryFromParams( array $params ): ?string {
|
||||
$watchlistExpiry = null;
|
||||
if ( $this->watchlistExpiryEnabled && isset( $params['watchlistexpiry'] ) ) {
|
||||
$watchlistExpiry = ApiResult::formatExpiry( $params['watchlistexpiry'] );
|
||||
}
|
||||
|
||||
return $watchlistExpiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented by ApiBase. We do this because otherwise Phan would complain
|
||||
* about calls to $this->getUser() in this trait, since it doesn't infer that
|
||||
* classes using the trait are also extending ApiBase.
|
||||
* @return User
|
||||
*/
|
||||
abstract protected function getUser();
|
||||
}
|
||||
|
|
@ -128,6 +128,7 @@
|
|||
"apihelp-delete-param-tags": "Change tags to apply to the entry in the deletion log.",
|
||||
"apihelp-delete-param-watch": "Add the page to the current user's watchlist.",
|
||||
"apihelp-delete-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-delete-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-delete-param-unwatch": "Remove the page from the current user's watchlist.",
|
||||
"apihelp-delete-param-oldimage": "The name of the old image to delete as provided by [[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]].",
|
||||
"apihelp-delete-example-simple": "Delete the <kbd>Main Page</kbd>.",
|
||||
|
|
@ -155,6 +156,7 @@
|
|||
"apihelp-edit-param-watch": "Add the page to the current user's watchlist.",
|
||||
"apihelp-edit-param-unwatch": "Remove the page from the current user's watchlist.",
|
||||
"apihelp-edit-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-edit-param-watchlistexpiry": "Watchlst expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-edit-param-md5": "The MD5 hash of the $1text parameter, or the $1prependtext and $1appendtext parameters concatenated. If set, the edit won't be done unless the hash is correct.",
|
||||
"apihelp-edit-param-prependtext": "Add this text to the beginning of the page. Overrides $1text.",
|
||||
"apihelp-edit-param-appendtext": "Add this text to the end of the page. Overrides $1text.\n\nUse $1section=new to append a new section, rather than this parameter.",
|
||||
|
|
@ -322,6 +324,7 @@
|
|||
"apihelp-move-param-watch": "Add the page and the redirect to the current user's watchlist.",
|
||||
"apihelp-move-param-unwatch": "Remove the page and the redirect from the current user's watchlist.",
|
||||
"apihelp-move-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-move-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-move-param-ignorewarnings": "Ignore any warnings.",
|
||||
"apihelp-move-param-tags": "Change tags to apply to the entry in the move log and to the null revision on the destination page.",
|
||||
"apihelp-move-example-move": "Move <kbd>Badtitle</kbd> to <kbd>Goodtitle</kbd> without leaving a redirect.",
|
||||
|
|
@ -431,6 +434,7 @@
|
|||
"apihelp-protect-param-cascade": "Enable cascading protection (i.e. protect transcluded templates and images used in this page). Ignored if none of the given protection levels support cascading.",
|
||||
"apihelp-protect-param-watch": "If set, add the page being (un)protected to the current user's watchlist.",
|
||||
"apihelp-protect-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-protect-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-protect-example-protect": "Protect a page.",
|
||||
"apihelp-protect-example-unprotect": "Unprotect a page by setting restrictions to <kbd>all</kbd> (i.e. everyone is allowed to take the action).",
|
||||
"apihelp-protect-example-unprotect2": "Unprotect a page by setting no restrictions.",
|
||||
|
|
@ -1452,6 +1456,7 @@
|
|||
"apihelp-rollback-param-summary": "Custom edit summary. If empty, default summary will be used.",
|
||||
"apihelp-rollback-param-markbot": "Mark the reverted edits and the revert as bot edits.",
|
||||
"apihelp-rollback-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-rollback-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-rollback-example-simple": "Roll back the last edits to page <kbd>Main Page</kbd> by user <kbd>Example</kbd>.",
|
||||
"apihelp-rollback-example-summary": "Roll back the last edits to page <kbd>Main Page</kbd> by IP user <kbd>192.0.2.5</kbd> with summary <kbd>Reverting vandalism</kbd>, and mark those edits and the revert as bot edits.",
|
||||
|
||||
|
|
@ -1525,6 +1530,7 @@
|
|||
"apihelp-undelete-param-timestamps": "Timestamps of the revisions to restore. If both <var>$1timestamps</var> and <var>$1fileids</var> are empty, all will be restored.",
|
||||
"apihelp-undelete-param-fileids": "IDs of the file revisions to restore. If both <var>$1timestamps</var> and <var>$1fileids</var> are empty, all will be restored.",
|
||||
"apihelp-undelete-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-undelete-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-undelete-example-page": "Undelete page <kbd>Main Page</kbd>.",
|
||||
"apihelp-undelete-example-revisions": "Undelete two revisions of page <kbd>Main Page</kbd>.",
|
||||
|
||||
|
|
@ -1539,6 +1545,7 @@
|
|||
"apihelp-upload-param-text": "Initial page text for new files.",
|
||||
"apihelp-upload-param-watch": "Watch the page.",
|
||||
"apihelp-upload-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
|
||||
"apihelp-upload-param-watchlistexpiry": "Watchlist expiry timestamp. Omit this parameter entirely to leave the current expiry unchanged.",
|
||||
"apihelp-upload-param-ignorewarnings": "Ignore any warnings.",
|
||||
"apihelp-upload-param-file": "File contents.",
|
||||
"apihelp-upload-param-url": "URL to fetch the file from.",
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@
|
|||
"apihelp-delete-param-tags": "{{doc-apihelp-param|delete|tags}}",
|
||||
"apihelp-delete-param-watch": "{{doc-apihelp-param|delete|watch}}",
|
||||
"apihelp-delete-param-watchlist": "{{doc-apihelp-param|delete|watchlist}}",
|
||||
"apihelp-delete-param-watchlistexpiry": "{{doc-apihelp-param|delete|watchlistexpiry}}",
|
||||
"apihelp-delete-param-unwatch": "{{doc-apihelp-param|delete|unwatch}}",
|
||||
"apihelp-delete-param-oldimage": "{{doc-apihelp-param|delete|oldimage}}",
|
||||
"apihelp-delete-example-simple": "{{doc-apihelp-example|delete}}",
|
||||
|
|
@ -154,6 +155,7 @@
|
|||
"apihelp-edit-param-watch": "{{doc-apihelp-param|edit|watch}}",
|
||||
"apihelp-edit-param-unwatch": "{{doc-apihelp-param|edit|unwatch}}",
|
||||
"apihelp-edit-param-watchlist": "{{doc-apihelp-param|edit|watchlist}}",
|
||||
"apihelp-edit-param-watchlistexpiry": "{{doc-apihelp-param|edit|watchlistexpiry}}",
|
||||
"apihelp-edit-param-md5": "{{doc-apihelp-param|edit|md5}}",
|
||||
"apihelp-edit-param-prependtext": "{{doc-apihelp-param|edit|prependtext}}",
|
||||
"apihelp-edit-param-appendtext": "{{doc-apihelp-param|edit|appendtext}}",
|
||||
|
|
@ -306,6 +308,7 @@
|
|||
"apihelp-move-param-watch": "{{doc-apihelp-param|move|watch}}",
|
||||
"apihelp-move-param-unwatch": "{{doc-apihelp-param|move|unwatch}}",
|
||||
"apihelp-move-param-watchlist": "{{doc-apihelp-param|move|watchlist}}",
|
||||
"apihelp-move-param-watchlistexpiry": "{{doc-apihelp-param|move|watchlistexpiry}}",
|
||||
"apihelp-move-param-ignorewarnings": "{{doc-apihelp-param|move|ignorewarnings}}",
|
||||
"apihelp-move-param-tags": "{{doc-apihelp-param|move|tags}}",
|
||||
"apihelp-move-example-move": "{{doc-apihelp-example|move}}",
|
||||
|
|
@ -409,6 +412,7 @@
|
|||
"apihelp-protect-param-cascade": "{{doc-apihelp-param|protect|cascade}}",
|
||||
"apihelp-protect-param-watch": "{{doc-apihelp-param|protect|watch}}",
|
||||
"apihelp-protect-param-watchlist": "{{doc-apihelp-param|protect|watchlist}}",
|
||||
"apihelp-protect-param-watchlistexpiry": "{{doc-apihelp-param|protect|watchlistexpiry}}",
|
||||
"apihelp-protect-example-protect": "{{doc-apihelp-example|protect}}",
|
||||
"apihelp-protect-example-unprotect": "{{doc-apihelp-example|protect}}",
|
||||
"apihelp-protect-example-unprotect2": "{{doc-apihelp-example|protect}}",
|
||||
|
|
@ -1360,6 +1364,7 @@
|
|||
"apihelp-rollback-param-summary": "{{doc-apihelp-param|rollback|summary}}",
|
||||
"apihelp-rollback-param-markbot": "{{doc-apihelp-param|rollback|markbot}}",
|
||||
"apihelp-rollback-param-watchlist": "{{doc-apihelp-param|rollback|watchlist}}",
|
||||
"apihelp-rollback-param-watchlistexpiry": "{{doc-apihelp-param|rollback|watchlistexpiry}}",
|
||||
"apihelp-rollback-example-simple": "{{doc-apihelp-example|rollback}}",
|
||||
"apihelp-rollback-example-summary": "{{doc-apihelp-example|rollback}}",
|
||||
"apihelp-rsd-summary": "{{doc-apihelp-summary|rsd}}",
|
||||
|
|
@ -1425,6 +1430,7 @@
|
|||
"apihelp-undelete-param-timestamps": "{{doc-apihelp-param|undelete|timestamps}}",
|
||||
"apihelp-undelete-param-fileids": "{{doc-apihelp-param|undelete|fileids}}",
|
||||
"apihelp-undelete-param-watchlist": "{{doc-apihelp-param|undelete|watchlist}}",
|
||||
"apihelp-undelete-param-watchlistexpiry": "{{doc-apihelp-param|undelete|watchlistexpiry}}",
|
||||
"apihelp-undelete-example-page": "{{doc-apihelp-example|undelete}}",
|
||||
"apihelp-undelete-example-revisions": "{{doc-apihelp-example|undelete}}",
|
||||
"apihelp-unlinkaccount-summary": "{{doc-apihelp-summary|unlinkaccount}}",
|
||||
|
|
@ -1437,6 +1443,7 @@
|
|||
"apihelp-upload-param-text": "{{doc-apihelp-param|upload|text}}",
|
||||
"apihelp-upload-param-watch": "{{doc-apihelp-param|upload|watch}}",
|
||||
"apihelp-upload-param-watchlist": "{{doc-apihelp-param|upload|watchlist}}",
|
||||
"apihelp-upload-param-watchlistexpiry": "{{doc-apihelp-param|upload|watchlistexpiry}}",
|
||||
"apihelp-upload-param-ignorewarnings": "{{doc-apihelp-param|upload|ignorewarnings}}",
|
||||
"apihelp-upload-param-file": "{{doc-apihelp-param|upload|file}}",
|
||||
"apihelp-upload-param-url": "{{doc-apihelp-param|upload|url}}",
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ class PublishStashedFileJob extends Job {
|
|||
$this->params['text'],
|
||||
$this->params['watch'],
|
||||
$user,
|
||||
$this->params['tags'] ?? []
|
||||
$this->params['tags'] ?? [],
|
||||
$this->params['watchlistexpiry'] ?? null
|
||||
);
|
||||
if ( !$status->isGood() ) {
|
||||
UploadBase::setSessionStatus(
|
||||
|
|
|
|||
|
|
@ -919,9 +919,15 @@ abstract class UploadBase {
|
|||
* @param User $user
|
||||
* @param string[] $tags Change tags to add to the log entry and page revision.
|
||||
* (This doesn't check $user's permissions.)
|
||||
* @param string|null $watchlistExpiry Optional watchlist expiry timestamp in any format
|
||||
* acceptable to wfTimestamp().
|
||||
* @return Status Indicating the whether the upload succeeded.
|
||||
*
|
||||
* @since 1.35 Accepts $watchlistExpiry parameter.
|
||||
*/
|
||||
public function performUpload( $comment, $pageText, $watch, $user, $tags = [] ) {
|
||||
public function performUpload(
|
||||
$comment, $pageText, $watch, $user, $tags = [], ?string $watchlistExpiry = null
|
||||
) {
|
||||
$this->getLocalFile()->load( File::READ_LATEST );
|
||||
$props = $this->mFileProps;
|
||||
|
||||
|
|
@ -950,7 +956,8 @@ abstract class UploadBase {
|
|||
WatchAction::doWatch(
|
||||
$this->getLocalFile()->getTitle(),
|
||||
$user,
|
||||
User::IGNORE_USER_RIGHTS
|
||||
User::IGNORE_USER_RIGHTS,
|
||||
$watchlistExpiry
|
||||
);
|
||||
}
|
||||
$this->getHookRunner()->onUploadComplete( $this );
|
||||
|
|
|
|||
|
|
@ -17,8 +17,12 @@ class ApiDeleteTest extends ApiTestCase {
|
|||
parent::setUp();
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'change_tag', 'change_tag_def', 'logging' ]
|
||||
[ 'change_tag', 'change_tag_def', 'logging', 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
|
|
@ -190,10 +194,17 @@ class ApiDeleteTest extends ApiTestCase {
|
|||
$this->assertTrue( Title::newFromText( $name )->exists() );
|
||||
$this->assertFalse( $user->isWatched( Title::newFromText( $name ) ) );
|
||||
|
||||
$this->doApiRequestWithToken( [ 'action' => 'delete', 'title' => $name, 'watch' => '' ] );
|
||||
$this->doApiRequestWithToken( [
|
||||
'action' => 'delete',
|
||||
'title' => $name,
|
||||
'watch' => '',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] );
|
||||
|
||||
$this->assertFalse( Title::newFromText( $name )->exists() );
|
||||
$this->assertTrue( $user->isWatched( Title::newFromText( $name ) ) );
|
||||
$title = Title::newFromText( $name );
|
||||
$this->assertFalse( $title->exists() );
|
||||
$this->assertTrue( $user->isWatched( $title ) );
|
||||
$this->assertTrue( $user->isTempWatched( $title ) );
|
||||
}
|
||||
|
||||
public function testDeleteUnwatch() {
|
||||
|
|
@ -205,7 +216,11 @@ class ApiDeleteTest extends ApiTestCase {
|
|||
$user->addWatch( Title::newFromText( $name ) );
|
||||
$this->assertTrue( $user->isWatched( Title::newFromText( $name ) ) );
|
||||
|
||||
$this->doApiRequestWithToken( [ 'action' => 'delete', 'title' => $name, 'unwatch' => '' ] );
|
||||
$this->doApiRequestWithToken( [
|
||||
'action' => 'delete',
|
||||
'title' => $name,
|
||||
'watchlist' => 'unwatch',
|
||||
] );
|
||||
|
||||
$this->assertFalse( Title::newFromText( $name )->exists() );
|
||||
$this->assertFalse( $user->isWatched( Title::newFromText( $name ) ) );
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ class ApiEditPageTest extends ApiTestCase {
|
|||
12312 => 'testing',
|
||||
12314 => 'testing-nontext',
|
||||
],
|
||||
'wgWatchlistExpiry' => true,
|
||||
'wgWatchlistExpiryMaxDuration' => '6 months',
|
||||
] );
|
||||
$this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
|
||||
'testing' => 'DummyContentHandlerForTesting',
|
||||
|
|
@ -38,7 +40,7 @@ class ApiEditPageTest extends ApiTestCase {
|
|||
] );
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'change_tag', 'change_tag_def', 'logging' ]
|
||||
[ 'change_tag', 'change_tag_def', 'logging', 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1354,10 +1356,13 @@ class ApiEditPageTest extends ApiTestCase {
|
|||
'title' => $name,
|
||||
'text' => 'Some text',
|
||||
'watch' => '',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] );
|
||||
|
||||
$this->assertTrue( Title::newFromText( $name )->exists() );
|
||||
$this->assertTrue( $user->isWatched( Title::newFromText( $name ) ) );
|
||||
$title = Title::newFromText( $name );
|
||||
$this->assertTrue( $title->exists() );
|
||||
$this->assertTrue( $user->isWatched( $title ) );
|
||||
$this->assertTrue( $user->isTempWatched( $title ) );
|
||||
}
|
||||
|
||||
public function testEditUnwatch() {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,20 @@ use MediaWiki\Revision\SlotRecord;
|
|||
* @covers ApiMove
|
||||
*/
|
||||
class ApiMoveTest extends ApiTestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $from Prefixed name of source
|
||||
* @param string $to Prefixed name of destination
|
||||
|
|
@ -100,6 +114,24 @@ class ApiMoveTest extends ApiTestCase {
|
|||
$this->assertArrayNotHasKey( 'warnings', $res[0] );
|
||||
}
|
||||
|
||||
public function testMoveAndWatch(): void {
|
||||
$name = ucfirst( __FUNCTION__ );
|
||||
$this->createPage( $name );
|
||||
|
||||
$this->doApiRequestWithToken( [
|
||||
'action' => 'move',
|
||||
'from' => $name,
|
||||
'to' => "$name 2",
|
||||
'watchlist' => 'watch',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] );
|
||||
|
||||
$title = Title::newFromText( $name );
|
||||
$title2 = Title::newFromText( "$name 2" );
|
||||
$this->assertTrue( $this->getTestSysop()->getUser()->isTempWatched( $title ) );
|
||||
$this->assertTrue( $this->getTestSysop()->getUser()->isTempWatched( $title2 ) );
|
||||
}
|
||||
|
||||
public function testMoveNonexistent() {
|
||||
$this->expectException( ApiUsageException::class );
|
||||
$this->expectExceptionMessage( "The page you specified doesn't exist." );
|
||||
|
|
|
|||
49
tests/phpunit/includes/api/ApiProtectTest.php
Normal file
49
tests/phpunit/includes/api/ApiProtectTest.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Tests for protect API.
|
||||
*
|
||||
* @group API
|
||||
* @group Database
|
||||
* @group medium
|
||||
*
|
||||
* @covers ApiProtect
|
||||
*/
|
||||
class ApiProtectTest extends ApiTestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'page_restrictions', 'logging', 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ApiProtect::execute()
|
||||
*/
|
||||
public function testProtectWithWatch(): void {
|
||||
$name = ucfirst( __FUNCTION__ );
|
||||
$title = Title::newFromText( $name );
|
||||
|
||||
$this->editPage( $name, 'Some text' );
|
||||
|
||||
$apiResult = $this->doApiRequestWithToken( [
|
||||
'action' => 'protect',
|
||||
'title' => $name,
|
||||
'protections' => 'edit=sysop',
|
||||
'expiry' => '55550123000000',
|
||||
'watchlist' => 'watch',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] )[0];
|
||||
|
||||
$this->assertArrayHasKey( 'protect', $apiResult );
|
||||
$this->assertSame( $name, $apiResult['protect']['title'] );
|
||||
$this->assertTrue( $title->isProtected( 'edit' ) );
|
||||
$this->assertTrue( $this->getTestSysop()->getUser()->isTempWatched( $title ) );
|
||||
}
|
||||
}
|
||||
70
tests/phpunit/includes/api/ApiRollbackTest.php
Normal file
70
tests/phpunit/includes/api/ApiRollbackTest.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
use MediaWiki\MediaWikiServices;
|
||||
|
||||
/**
|
||||
* Tests for Rollback API.
|
||||
*
|
||||
* @group API
|
||||
* @group Database
|
||||
* @group medium
|
||||
*
|
||||
* @covers ApiRollback
|
||||
*/
|
||||
class ApiRollbackTest extends ApiTestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ApiRollback::execute()
|
||||
*/
|
||||
public function testProtectWithWatch(): void {
|
||||
$name = ucfirst( __FUNCTION__ );
|
||||
$title = Title::newFromText( $name );
|
||||
$revLookup = MediaWikiServices::getInstance()
|
||||
->getRevisionLookup();
|
||||
|
||||
$user = $this->getTestUser()->getUser();
|
||||
$sysop = $this->getTestSysop()->getUser();
|
||||
|
||||
// Create page as sysop.
|
||||
$this->editPage( $name, 'Some text', '', NS_MAIN, $sysop );
|
||||
|
||||
// Edit as non-sysop.
|
||||
$this->editPage( $name, 'Vandalism', '', NS_MAIN, $user );
|
||||
|
||||
// Rollback as sysop.
|
||||
$apiResult = $this->doApiRequestWithToken( [
|
||||
'action' => 'rollback',
|
||||
'title' => $name,
|
||||
'user' => $user->getName(),
|
||||
'watchlist' => 'watch',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] )[0];
|
||||
|
||||
// Content of latest revision should match the initial.
|
||||
$latestRev = $revLookup->getRevisionByTitle( $title );
|
||||
$initialRev = $revLookup->getFirstRevision( $title );
|
||||
$this->assertTrue( $latestRev->hasSameContent( $initialRev ) );
|
||||
// ...but have different rev IDs.
|
||||
$this->assertNotSame( $latestRev->getId(), $initialRev->getId() );
|
||||
|
||||
// Make sure the API response looks good.
|
||||
$this->assertArrayHasKey( 'rollback', $apiResult );
|
||||
$this->assertSame( $name, $apiResult['rollback']['title'] );
|
||||
|
||||
// And that the page was temporarily watched.
|
||||
$this->assertTrue( $sysop->isTempWatched( $title ) );
|
||||
}
|
||||
}
|
||||
58
tests/phpunit/includes/api/ApiUndeleteTest.php
Normal file
58
tests/phpunit/includes/api/ApiUndeleteTest.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Tests for Undelete API.
|
||||
*
|
||||
* @group API
|
||||
* @group Database
|
||||
* @group medium
|
||||
*
|
||||
* @covers ApiUndelete
|
||||
*/
|
||||
class ApiUndeleteTest extends ApiTestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->tablesUsed = array_merge(
|
||||
$this->tablesUsed,
|
||||
[ 'logging', 'watchlist', 'watchlist_expiry' ]
|
||||
);
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ApiUndelete::execute()
|
||||
*/
|
||||
public function testUndeleteWithWatch(): void {
|
||||
$name = ucfirst( __FUNCTION__ );
|
||||
$title = Title::newFromText( $name );
|
||||
$sysop = $this->getTestSysop()->getUser();
|
||||
|
||||
// Create page.
|
||||
$this->editPage( $name, 'Test' );
|
||||
|
||||
// Delete page.
|
||||
$this->doApiRequestWithToken( [
|
||||
'action' => 'delete',
|
||||
'title' => $name,
|
||||
] );
|
||||
|
||||
// For good measure.
|
||||
$this->assertFalse( $title->exists() );
|
||||
$this->assertFalse( $sysop->isWatched( $title ) );
|
||||
|
||||
// Restore page, and watch with expiry.
|
||||
$this->doApiRequestWithToken( [
|
||||
'action' => 'undelete',
|
||||
'title' => $name,
|
||||
'watchlist' => 'watch',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
] );
|
||||
|
||||
$this->assertTrue( $title->exists() );
|
||||
$this->assertTrue( $sysop->isTempWatched( $title ) );
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ class ApiUploadTest extends ApiUploadTestCase {
|
|||
protected function setUp() : void {
|
||||
parent::setUp();
|
||||
$this->tablesUsed[] = 'watchlist'; // This test might interfere with watchlists test.
|
||||
$this->tablesUsed[] = 'watchlist_expiry';
|
||||
$this->tablesUsed = array_merge( $this->tablesUsed, LocalFile::getQueryInfo()['tables'] );
|
||||
$this->setService( 'RepoGroup', new RepoGroup(
|
||||
[
|
||||
|
|
@ -30,6 +31,10 @@ class ApiUploadTest extends ApiUploadTestCase {
|
|||
null
|
||||
) );
|
||||
$this->resetServices();
|
||||
|
||||
$this->setMwGlobals( [
|
||||
'wgWatchlistExpiry' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
public function testUploadRequiresToken() {
|
||||
|
|
@ -48,10 +53,12 @@ class ApiUploadTest extends ApiUploadTestCase {
|
|||
], null, self::$users['uploader']->getUser() );
|
||||
}
|
||||
|
||||
public function testUpload() {
|
||||
public function testUploadWithWatch() {
|
||||
$fileName = 'TestUpload.jpg';
|
||||
$mimeType = 'image/jpeg';
|
||||
$filePath = $this->filePath( 'yuv420.jpg' );
|
||||
$title = Title::newFromText( $fileName, NS_FILE );
|
||||
$user = self::$users['uploader']->getUser();
|
||||
|
||||
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
|
||||
list( $result ) = $this->doApiRequestWithToken( [
|
||||
|
|
@ -60,12 +67,15 @@ class ApiUploadTest extends ApiUploadTestCase {
|
|||
'file' => 'dummy content',
|
||||
'comment' => 'dummy comment',
|
||||
'text' => "This is the page text for $fileName",
|
||||
], null, self::$users['uploader']->getUser() );
|
||||
'watchlist' => 'watch',
|
||||
'watchlistexpiry' => '99990123000000',
|
||||
], null, $user );
|
||||
|
||||
$this->assertArrayHasKey( 'upload', $result );
|
||||
$this->assertEquals( 'Success', $result['upload']['result'] );
|
||||
$this->assertSame( filesize( $filePath ), (int)$result['upload']['imageinfo']['size'] );
|
||||
$this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
|
||||
$this->assertTrue( $user->isTempWatched( $title ) );
|
||||
}
|
||||
|
||||
public function testUploadZeroLength() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue