2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2012-05-09 17:55:56 +00:00
|
|
|
* 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
|
|
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @file
|
|
|
|
|
* @ingroup Watchlist
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2017-12-08 03:09:15 +00:00
|
|
|
|
2024-05-17 09:37:08 +00:00
|
|
|
namespace MediaWiki\Watchlist;
|
|
|
|
|
|
2016-04-20 08:29:21 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
2021-03-25 03:08:22 +00:00
|
|
|
use MediaWiki\Page\PageIdentity;
|
2023-09-18 13:56:39 +00:00
|
|
|
use MediaWiki\Title\TitleValue;
|
2019-04-28 11:07:18 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
2023-08-19 03:35:06 +00:00
|
|
|
use MediaWiki\Utils\MWTimestamp;
|
2024-05-17 09:37:08 +00:00
|
|
|
use MessageLocalizer;
|
|
|
|
|
use RecentChange;
|
2020-08-28 19:49:08 +00:00
|
|
|
use Wikimedia\ParamValidator\TypeDef\ExpiryDef;
|
|
|
|
|
use Wikimedia\Timestamp\ConvertibleTimestamp;
|
2003-11-09 11:45:12 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2012-05-09 17:55:56 +00:00
|
|
|
* Representation of a pair of user and title for watchlist entries.
|
|
|
|
|
*
|
2016-02-01 11:53:01 +00:00
|
|
|
* @author Tim Starling
|
|
|
|
|
* @author Addshore
|
|
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Watchlist
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2003-11-09 11:45:12 +00:00
|
|
|
class WatchedItem {
|
2014-03-15 20:13:54 +00:00
|
|
|
/**
|
2021-03-25 03:08:22 +00:00
|
|
|
* @var LinkTarget|PageIdentity deprecated LinkTarget since 1.36
|
2014-03-15 20:13:54 +00:00
|
|
|
*/
|
2021-03-25 03:08:22 +00:00
|
|
|
private $target;
|
2014-03-15 20:13:54 +00:00
|
|
|
|
|
|
|
|
/**
|
2019-04-28 11:07:18 +00:00
|
|
|
* @var UserIdentity
|
2014-03-15 20:13:54 +00:00
|
|
|
*/
|
2016-02-01 11:53:01 +00:00
|
|
|
private $user;
|
2012-11-17 14:26:01 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2022-03-08 22:57:00 +00:00
|
|
|
* @var bool|null|string the value of the wl_notificationtimestamp field
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2016-02-01 11:53:01 +00:00
|
|
|
private $notificationTimestamp;
|
2012-02-10 19:35:14 +00:00
|
|
|
|
2020-02-24 19:55:37 +00:00
|
|
|
/**
|
2020-08-28 19:49:08 +00:00
|
|
|
* @var ConvertibleTimestamp|null value that determines when a watched item will expire.
|
|
|
|
|
* 'null' means that there is no expiration.
|
2020-02-24 19:55:37 +00:00
|
|
|
*/
|
|
|
|
|
private $expiry;
|
|
|
|
|
|
2020-06-10 00:35:09 +00:00
|
|
|
/**
|
|
|
|
|
* Used to calculate how many days are remaining until a watched item will expire.
|
|
|
|
|
* Uses a different algorithm from Language::getDurationIntervals for calculating
|
|
|
|
|
* days remaining in an interval of time
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
*/
|
|
|
|
|
private const SECONDS_IN_A_DAY = 86400;
|
|
|
|
|
|
2012-02-10 19:35:14 +00:00
|
|
|
/**
|
2019-04-28 11:07:18 +00:00
|
|
|
* @param UserIdentity $user
|
2021-03-25 03:08:22 +00:00
|
|
|
* @param LinkTarget|PageIdentity $target deprecated passing LinkTarget since 1.36
|
2022-03-08 22:57:00 +00:00
|
|
|
* @param bool|null|string $notificationTimestamp the value of the wl_notificationtimestamp field
|
2020-02-24 19:55:37 +00:00
|
|
|
* @param null|string $expiry Optional expiry timestamp in any format acceptable to wfTimestamp()
|
2012-02-10 19:35:14 +00:00
|
|
|
*/
|
2016-02-01 11:53:01 +00:00
|
|
|
public function __construct(
|
2019-04-28 11:07:18 +00:00
|
|
|
UserIdentity $user,
|
2021-03-25 03:08:22 +00:00
|
|
|
$target,
|
2020-02-24 19:55:37 +00:00
|
|
|
$notificationTimestamp,
|
|
|
|
|
?string $expiry = null
|
2016-02-01 11:53:01 +00:00
|
|
|
) {
|
|
|
|
|
$this->user = $user;
|
2021-03-25 03:08:22 +00:00
|
|
|
$this->target = $target;
|
2016-02-01 11:53:01 +00:00
|
|
|
$this->notificationTimestamp = $notificationTimestamp;
|
2020-08-28 19:49:08 +00:00
|
|
|
|
|
|
|
|
// Expiry will be saved in ConvertibleTimestamp
|
|
|
|
|
$this->expiry = ExpiryDef::normalizeExpiry( $expiry );
|
|
|
|
|
|
|
|
|
|
// If the normalization returned 'infinity' then set it as null since they are synonymous
|
|
|
|
|
if ( $this->expiry === 'infinity' ) {
|
|
|
|
|
$this->expiry = null;
|
|
|
|
|
}
|
2012-02-10 19:35:14 +00:00
|
|
|
}
|
|
|
|
|
|
2020-05-15 05:56:36 +00:00
|
|
|
/**
|
|
|
|
|
* @since 1.35
|
|
|
|
|
* @param RecentChange $recentChange
|
|
|
|
|
* @param UserIdentity $user
|
|
|
|
|
* @return WatchedItem
|
|
|
|
|
*/
|
|
|
|
|
public static function newFromRecentChange( RecentChange $recentChange, UserIdentity $user ) {
|
|
|
|
|
return new self(
|
|
|
|
|
$user,
|
|
|
|
|
$recentChange->getTitle(),
|
|
|
|
|
$recentChange->notificationtimestamp,
|
|
|
|
|
$recentChange->watchlistExpiry
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-28 11:07:18 +00:00
|
|
|
/**
|
|
|
|
|
* @return UserIdentity
|
|
|
|
|
*/
|
|
|
|
|
public function getUserIdentity() {
|
2016-02-01 11:53:01 +00:00
|
|
|
return $this->user;
|
2013-06-13 18:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-10 19:35:14 +00:00
|
|
|
/**
|
2016-02-01 11:53:01 +00:00
|
|
|
* @return LinkTarget
|
2021-03-25 03:08:22 +00:00
|
|
|
* @deprecated since 1.36, use getTarget() instead
|
2012-02-10 19:35:14 +00:00
|
|
|
*/
|
2016-02-01 11:53:01 +00:00
|
|
|
public function getLinkTarget() {
|
2021-03-25 03:08:22 +00:00
|
|
|
if ( !$this->target instanceof LinkTarget ) {
|
|
|
|
|
return TitleValue::newFromPage( $this->target );
|
|
|
|
|
}
|
|
|
|
|
return $this->getTarget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return LinkTarget|PageIdentity deprecated returning LinkTarget since 1.36
|
|
|
|
|
* @since 1.36
|
|
|
|
|
*/
|
|
|
|
|
public function getTarget() {
|
|
|
|
|
return $this->target;
|
2012-02-10 19:35:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the notification timestamp of this entry.
|
|
|
|
|
*
|
2016-02-01 11:53:01 +00:00
|
|
|
* @return bool|null|string
|
2012-02-10 19:35:14 +00:00
|
|
|
*/
|
|
|
|
|
public function getNotificationTimestamp() {
|
2016-02-01 11:53:01 +00:00
|
|
|
return $this->notificationTimestamp;
|
2012-02-10 19:35:14 +00:00
|
|
|
}
|
2020-02-24 19:55:37 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When the watched item will expire.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
2020-08-28 19:49:08 +00:00
|
|
|
* @param int|null $style Given timestamp format to style the ConvertibleTimestamp
|
|
|
|
|
* @return string|null null or in a format acceptable to ConvertibleTimestamp (TS_* constants).
|
|
|
|
|
* Default is TS_MW format.
|
2020-02-24 19:55:37 +00:00
|
|
|
*/
|
2020-08-28 19:49:08 +00:00
|
|
|
public function getExpiry( ?int $style = TS_MW ) {
|
|
|
|
|
return $this->expiry instanceof ConvertibleTimestamp
|
|
|
|
|
? $this->expiry->getTimestamp( $style )
|
|
|
|
|
: $this->expiry;
|
2020-02-24 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Has the watched item expired?
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function isExpired(): bool {
|
2020-08-28 19:49:08 +00:00
|
|
|
$expiry = $this->getExpiry();
|
|
|
|
|
if ( $expiry === null ) {
|
2020-02-24 19:55:37 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-28 19:49:08 +00:00
|
|
|
return $expiry < ConvertibleTimestamp::now();
|
2020-02-24 19:55:37 +00:00
|
|
|
}
|
2020-06-10 00:35:09 +00:00
|
|
|
|
|
|
|
|
/**
|
2020-07-28 03:06:11 +00:00
|
|
|
* Get days remaining until a watched item expires.
|
2020-06-10 00:35:09 +00:00
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
*
|
2020-07-28 03:06:11 +00:00
|
|
|
* @return int|null days remaining or null if no expiration is present
|
2020-06-10 00:35:09 +00:00
|
|
|
*/
|
2020-07-28 03:06:11 +00:00
|
|
|
public function getExpiryInDays(): ?int {
|
2020-08-10 01:27:38 +00:00
|
|
|
return self::calculateExpiryInDays( $this->getExpiry() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the number of days remaining until the given expiry time.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
*
|
|
|
|
|
* @param string|null $expiry The expiry to calculate from, in any format
|
|
|
|
|
* supported by MWTimestamp::convert().
|
|
|
|
|
*
|
|
|
|
|
* @return int|null The remaining number of days or null if $expiry is null.
|
|
|
|
|
*/
|
|
|
|
|
public static function calculateExpiryInDays( ?string $expiry ): ?int {
|
|
|
|
|
if ( $expiry === null ) {
|
2020-07-28 03:06:11 +00:00
|
|
|
return null;
|
2020-07-25 19:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
2021-10-16 21:47:01 +00:00
|
|
|
$unixTimeExpiry = (int)MWTimestamp::convert( TS_UNIX, $expiry );
|
|
|
|
|
$diffInSeconds = $unixTimeExpiry - (int)wfTimestamp( TS_UNIX );
|
2020-06-10 00:35:09 +00:00
|
|
|
$diffInDays = $diffInSeconds / self::SECONDS_IN_A_DAY;
|
|
|
|
|
|
|
|
|
|
if ( $diffInDays < 1 ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (int)ceil( $diffInDays );
|
|
|
|
|
}
|
2020-07-23 01:24:42 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get days remaining until a watched item expires as a text.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
* @param MessageLocalizer $msgLocalizer
|
|
|
|
|
* @param bool $isDropdownOption Whether the text is being displayed as a dropdown option.
|
|
|
|
|
* The text is different as a dropdown option from when it is used in other
|
|
|
|
|
* places as a watchlist indicator.
|
|
|
|
|
* @return string days remaining text and '' if no expiration is present
|
|
|
|
|
*/
|
|
|
|
|
public function getExpiryInDaysText( MessageLocalizer $msgLocalizer, $isDropdownOption = false ): string {
|
|
|
|
|
$expiryInDays = $this->getExpiryInDays();
|
|
|
|
|
if ( $expiryInDays === null ) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $expiryInDays < 1 ) {
|
|
|
|
|
if ( $isDropdownOption ) {
|
|
|
|
|
return $msgLocalizer->msg( 'watchlist-expiry-hours-left' )->text();
|
|
|
|
|
}
|
|
|
|
|
return $msgLocalizer->msg( 'watchlist-expiring-hours-full-text' )->text();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $isDropdownOption ) {
|
|
|
|
|
return $msgLocalizer->msg( 'watchlist-expiry-days-left', [ $expiryInDays ] )->text();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $msgLocalizer->msg( 'watchlist-expiring-days-full-text', [ $expiryInDays ] )->text();
|
|
|
|
|
}
|
2003-11-09 11:45:12 +00:00
|
|
|
}
|
2024-05-17 09:37:08 +00:00
|
|
|
/** @deprecated class alias since 1.43 */
|
|
|
|
|
class_alias( WatchedItem::class, 'WatchedItem' );
|