Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
68.89% |
31 / 45 |
|
45.45% |
5 / 11 |
CRAP | |
0.00% |
0 / 1 |
WatchedItem | |
70.45% |
31 / 44 |
|
45.45% |
5 / 11 |
32.37 | |
0.00% |
0 / 1 |
__construct | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
newFromRecentChange | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getUserIdentity | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLinkTarget | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNotificationTimestamp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getExpiry | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isExpired | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getExpiryInDays | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
calculateExpiryInDays | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
getExpiryInDaysText | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
5 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | * @ingroup Watchlist |
20 | */ |
21 | |
22 | namespace MediaWiki\Watchlist; |
23 | |
24 | use MediaWiki\Linker\LinkTarget; |
25 | use MediaWiki\Page\PageIdentity; |
26 | use MediaWiki\Title\TitleValue; |
27 | use MediaWiki\User\UserIdentity; |
28 | use MediaWiki\Utils\MWTimestamp; |
29 | use MessageLocalizer; |
30 | use RecentChange; |
31 | use Wikimedia\ParamValidator\TypeDef\ExpiryDef; |
32 | use Wikimedia\Timestamp\ConvertibleTimestamp; |
33 | |
34 | /** |
35 | * Representation of a pair of user and title for watchlist entries. |
36 | * |
37 | * @author Tim Starling |
38 | * @author Addshore |
39 | * |
40 | * @ingroup Watchlist |
41 | */ |
42 | class WatchedItem { |
43 | /** |
44 | * @var LinkTarget|PageIdentity deprecated LinkTarget since 1.36 |
45 | */ |
46 | private $target; |
47 | |
48 | /** |
49 | * @var UserIdentity |
50 | */ |
51 | private $user; |
52 | |
53 | /** |
54 | * @var bool|null|string the value of the wl_notificationtimestamp field |
55 | */ |
56 | private $notificationTimestamp; |
57 | |
58 | /** |
59 | * @var ConvertibleTimestamp|null value that determines when a watched item will expire. |
60 | * 'null' means that there is no expiration. |
61 | */ |
62 | private $expiry; |
63 | |
64 | /** |
65 | * Used to calculate how many days are remaining until a watched item will expire. |
66 | * Uses a different algorithm from Language::getDurationIntervals for calculating |
67 | * days remaining in an interval of time |
68 | * |
69 | * @since 1.35 |
70 | */ |
71 | private const SECONDS_IN_A_DAY = 86400; |
72 | |
73 | /** |
74 | * @param UserIdentity $user |
75 | * @param LinkTarget|PageIdentity $target deprecated passing LinkTarget since 1.36 |
76 | * @param bool|null|string $notificationTimestamp the value of the wl_notificationtimestamp field |
77 | * @param null|string $expiry Optional expiry timestamp in any format acceptable to wfTimestamp() |
78 | */ |
79 | public function __construct( |
80 | UserIdentity $user, |
81 | $target, |
82 | $notificationTimestamp, |
83 | ?string $expiry = null |
84 | ) { |
85 | $this->user = $user; |
86 | $this->target = $target; |
87 | $this->notificationTimestamp = $notificationTimestamp; |
88 | |
89 | // Expiry will be saved in ConvertibleTimestamp |
90 | $this->expiry = ExpiryDef::normalizeExpiry( $expiry ); |
91 | |
92 | // If the normalization returned 'infinity' then set it as null since they are synonymous |
93 | if ( $this->expiry === 'infinity' ) { |
94 | $this->expiry = null; |
95 | } |
96 | } |
97 | |
98 | /** |
99 | * @since 1.35 |
100 | * @param RecentChange $recentChange |
101 | * @param UserIdentity $user |
102 | * @return WatchedItem |
103 | */ |
104 | public static function newFromRecentChange( RecentChange $recentChange, UserIdentity $user ) { |
105 | return new self( |
106 | $user, |
107 | $recentChange->getTitle(), |
108 | $recentChange->notificationtimestamp, |
109 | $recentChange->watchlistExpiry |
110 | ); |
111 | } |
112 | |
113 | /** |
114 | * @return UserIdentity |
115 | */ |
116 | public function getUserIdentity() { |
117 | return $this->user; |
118 | } |
119 | |
120 | /** |
121 | * @return LinkTarget |
122 | * @deprecated since 1.36, use getTarget() instead |
123 | */ |
124 | public function getLinkTarget() { |
125 | if ( !$this->target instanceof LinkTarget ) { |
126 | return TitleValue::newFromPage( $this->target ); |
127 | } |
128 | return $this->getTarget(); |
129 | } |
130 | |
131 | /** |
132 | * @return LinkTarget|PageIdentity deprecated returning LinkTarget since 1.36 |
133 | * @since 1.36 |
134 | */ |
135 | public function getTarget() { |
136 | return $this->target; |
137 | } |
138 | |
139 | /** |
140 | * Get the notification timestamp of this entry. |
141 | * |
142 | * @return bool|null|string |
143 | */ |
144 | public function getNotificationTimestamp() { |
145 | return $this->notificationTimestamp; |
146 | } |
147 | |
148 | /** |
149 | * When the watched item will expire. |
150 | * |
151 | * @since 1.35 |
152 | * @param int|null $style Given timestamp format to style the ConvertibleTimestamp |
153 | * @return string|null null or in a format acceptable to ConvertibleTimestamp (TS_* constants). |
154 | * Default is TS_MW format. |
155 | */ |
156 | public function getExpiry( ?int $style = TS_MW ) { |
157 | return $this->expiry instanceof ConvertibleTimestamp |
158 | ? $this->expiry->getTimestamp( $style ) |
159 | : $this->expiry; |
160 | } |
161 | |
162 | /** |
163 | * Has the watched item expired? |
164 | * |
165 | * @since 1.35 |
166 | * |
167 | * @return bool |
168 | */ |
169 | public function isExpired(): bool { |
170 | $expiry = $this->getExpiry(); |
171 | if ( $expiry === null ) { |
172 | return false; |
173 | } |
174 | return $expiry < ConvertibleTimestamp::now(); |
175 | } |
176 | |
177 | /** |
178 | * Get days remaining until a watched item expires. |
179 | * |
180 | * @since 1.35 |
181 | * |
182 | * @return int|null days remaining or null if no expiration is present |
183 | */ |
184 | public function getExpiryInDays(): ?int { |
185 | return self::calculateExpiryInDays( $this->getExpiry() ); |
186 | } |
187 | |
188 | /** |
189 | * Get the number of days remaining until the given expiry time. |
190 | * |
191 | * @since 1.35 |
192 | * |
193 | * @param string|null $expiry The expiry to calculate from, in any format |
194 | * supported by MWTimestamp::convert(). |
195 | * |
196 | * @return int|null The remaining number of days or null if $expiry is null. |
197 | */ |
198 | public static function calculateExpiryInDays( ?string $expiry ): ?int { |
199 | if ( $expiry === null ) { |
200 | return null; |
201 | } |
202 | |
203 | $unixTimeExpiry = (int)MWTimestamp::convert( TS_UNIX, $expiry ); |
204 | $diffInSeconds = $unixTimeExpiry - (int)wfTimestamp( TS_UNIX ); |
205 | $diffInDays = $diffInSeconds / self::SECONDS_IN_A_DAY; |
206 | |
207 | if ( $diffInDays < 1 ) { |
208 | return 0; |
209 | } |
210 | |
211 | return (int)ceil( $diffInDays ); |
212 | } |
213 | |
214 | /** |
215 | * Get days remaining until a watched item expires as a text. |
216 | * |
217 | * @since 1.35 |
218 | * @param MessageLocalizer $msgLocalizer |
219 | * @param bool $isDropdownOption Whether the text is being displayed as a dropdown option. |
220 | * The text is different as a dropdown option from when it is used in other |
221 | * places as a watchlist indicator. |
222 | * @return string days remaining text and '' if no expiration is present |
223 | */ |
224 | public function getExpiryInDaysText( MessageLocalizer $msgLocalizer, $isDropdownOption = false ): string { |
225 | $expiryInDays = $this->getExpiryInDays(); |
226 | if ( $expiryInDays === null ) { |
227 | return ''; |
228 | } |
229 | |
230 | if ( $expiryInDays < 1 ) { |
231 | if ( $isDropdownOption ) { |
232 | return $msgLocalizer->msg( 'watchlist-expiry-hours-left' )->text(); |
233 | } |
234 | return $msgLocalizer->msg( 'watchlist-expiring-hours-full-text' )->text(); |
235 | } |
236 | |
237 | if ( $isDropdownOption ) { |
238 | return $msgLocalizer->msg( 'watchlist-expiry-days-left', [ $expiryInDays ] )->text(); |
239 | } |
240 | |
241 | return $msgLocalizer->msg( 'watchlist-expiring-days-full-text', [ $expiryInDays ] )->text(); |
242 | } |
243 | } |
244 | /** @deprecated class alias since 1.43 */ |
245 | class_alias( WatchedItem::class, 'WatchedItem' ); |