Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
70.45% |
31 / 44 |
|
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 | use MediaWiki\Linker\LinkTarget; |
23 | use MediaWiki\Page\PageIdentity; |
24 | use MediaWiki\Title\TitleValue; |
25 | use MediaWiki\User\UserIdentity; |
26 | use MediaWiki\Utils\MWTimestamp; |
27 | use Wikimedia\ParamValidator\TypeDef\ExpiryDef; |
28 | use Wikimedia\Timestamp\ConvertibleTimestamp; |
29 | |
30 | /** |
31 | * Representation of a pair of user and title for watchlist entries. |
32 | * |
33 | * @author Tim Starling |
34 | * @author Addshore |
35 | * |
36 | * @ingroup Watchlist |
37 | */ |
38 | class WatchedItem { |
39 | /** |
40 | * @var LinkTarget|PageIdentity deprecated LinkTarget since 1.36 |
41 | */ |
42 | private $target; |
43 | |
44 | /** |
45 | * @var UserIdentity |
46 | */ |
47 | private $user; |
48 | |
49 | /** |
50 | * @var bool|null|string the value of the wl_notificationtimestamp field |
51 | */ |
52 | private $notificationTimestamp; |
53 | |
54 | /** |
55 | * @var ConvertibleTimestamp|null value that determines when a watched item will expire. |
56 | * 'null' means that there is no expiration. |
57 | */ |
58 | private $expiry; |
59 | |
60 | /** |
61 | * Used to calculate how many days are remaining until a watched item will expire. |
62 | * Uses a different algorithm from Language::getDurationIntervals for calculating |
63 | * days remaining in an interval of time |
64 | * |
65 | * @since 1.35 |
66 | */ |
67 | private const SECONDS_IN_A_DAY = 86400; |
68 | |
69 | /** |
70 | * @param UserIdentity $user |
71 | * @param LinkTarget|PageIdentity $target deprecated passing LinkTarget since 1.36 |
72 | * @param bool|null|string $notificationTimestamp the value of the wl_notificationtimestamp field |
73 | * @param null|string $expiry Optional expiry timestamp in any format acceptable to wfTimestamp() |
74 | */ |
75 | public function __construct( |
76 | UserIdentity $user, |
77 | $target, |
78 | $notificationTimestamp, |
79 | ?string $expiry = null |
80 | ) { |
81 | $this->user = $user; |
82 | $this->target = $target; |
83 | $this->notificationTimestamp = $notificationTimestamp; |
84 | |
85 | // Expiry will be saved in ConvertibleTimestamp |
86 | $this->expiry = ExpiryDef::normalizeExpiry( $expiry ); |
87 | |
88 | // If the normalization returned 'infinity' then set it as null since they are synonymous |
89 | if ( $this->expiry === 'infinity' ) { |
90 | $this->expiry = null; |
91 | } |
92 | } |
93 | |
94 | /** |
95 | * @since 1.35 |
96 | * @param RecentChange $recentChange |
97 | * @param UserIdentity $user |
98 | * @return WatchedItem |
99 | */ |
100 | public static function newFromRecentChange( RecentChange $recentChange, UserIdentity $user ) { |
101 | return new self( |
102 | $user, |
103 | $recentChange->getTitle(), |
104 | $recentChange->notificationtimestamp, |
105 | $recentChange->watchlistExpiry |
106 | ); |
107 | } |
108 | |
109 | /** |
110 | * @return UserIdentity |
111 | */ |
112 | public function getUserIdentity() { |
113 | return $this->user; |
114 | } |
115 | |
116 | /** |
117 | * @return LinkTarget |
118 | * @deprecated since 1.36, use getTarget() instead |
119 | */ |
120 | public function getLinkTarget() { |
121 | if ( !$this->target instanceof LinkTarget ) { |
122 | return TitleValue::newFromPage( $this->target ); |
123 | } |
124 | return $this->getTarget(); |
125 | } |
126 | |
127 | /** |
128 | * @return LinkTarget|PageIdentity deprecated returning LinkTarget since 1.36 |
129 | * @since 1.36 |
130 | */ |
131 | public function getTarget() { |
132 | return $this->target; |
133 | } |
134 | |
135 | /** |
136 | * Get the notification timestamp of this entry. |
137 | * |
138 | * @return bool|null|string |
139 | */ |
140 | public function getNotificationTimestamp() { |
141 | return $this->notificationTimestamp; |
142 | } |
143 | |
144 | /** |
145 | * When the watched item will expire. |
146 | * |
147 | * @since 1.35 |
148 | * @param int|null $style Given timestamp format to style the ConvertibleTimestamp |
149 | * @return string|null null or in a format acceptable to ConvertibleTimestamp (TS_* constants). |
150 | * Default is TS_MW format. |
151 | */ |
152 | public function getExpiry( ?int $style = TS_MW ) { |
153 | return $this->expiry instanceof ConvertibleTimestamp |
154 | ? $this->expiry->getTimestamp( $style ) |
155 | : $this->expiry; |
156 | } |
157 | |
158 | /** |
159 | * Has the watched item expired? |
160 | * |
161 | * @since 1.35 |
162 | * |
163 | * @return bool |
164 | */ |
165 | public function isExpired(): bool { |
166 | $expiry = $this->getExpiry(); |
167 | if ( $expiry === null ) { |
168 | return false; |
169 | } |
170 | return $expiry < ConvertibleTimestamp::now(); |
171 | } |
172 | |
173 | /** |
174 | * Get days remaining until a watched item expires. |
175 | * |
176 | * @since 1.35 |
177 | * |
178 | * @return int|null days remaining or null if no expiration is present |
179 | */ |
180 | public function getExpiryInDays(): ?int { |
181 | return self::calculateExpiryInDays( $this->getExpiry() ); |
182 | } |
183 | |
184 | /** |
185 | * Get the number of days remaining until the given expiry time. |
186 | * |
187 | * @since 1.35 |
188 | * |
189 | * @param string|null $expiry The expiry to calculate from, in any format |
190 | * supported by MWTimestamp::convert(). |
191 | * |
192 | * @return int|null The remaining number of days or null if $expiry is null. |
193 | */ |
194 | public static function calculateExpiryInDays( ?string $expiry ): ?int { |
195 | if ( $expiry === null ) { |
196 | return null; |
197 | } |
198 | |
199 | $unixTimeExpiry = (int)MWTimestamp::convert( TS_UNIX, $expiry ); |
200 | $diffInSeconds = $unixTimeExpiry - (int)wfTimestamp( TS_UNIX ); |
201 | $diffInDays = $diffInSeconds / self::SECONDS_IN_A_DAY; |
202 | |
203 | if ( $diffInDays < 1 ) { |
204 | return 0; |
205 | } |
206 | |
207 | return (int)ceil( $diffInDays ); |
208 | } |
209 | |
210 | /** |
211 | * Get days remaining until a watched item expires as a text. |
212 | * |
213 | * @since 1.35 |
214 | * @param MessageLocalizer $msgLocalizer |
215 | * @param bool $isDropdownOption Whether the text is being displayed as a dropdown option. |
216 | * The text is different as a dropdown option from when it is used in other |
217 | * places as a watchlist indicator. |
218 | * @return string days remaining text and '' if no expiration is present |
219 | */ |
220 | public function getExpiryInDaysText( MessageLocalizer $msgLocalizer, $isDropdownOption = false ): string { |
221 | $expiryInDays = $this->getExpiryInDays(); |
222 | if ( $expiryInDays === null ) { |
223 | return ''; |
224 | } |
225 | |
226 | if ( $expiryInDays < 1 ) { |
227 | if ( $isDropdownOption ) { |
228 | return $msgLocalizer->msg( 'watchlist-expiry-hours-left' )->text(); |
229 | } |
230 | return $msgLocalizer->msg( 'watchlist-expiring-hours-full-text' )->text(); |
231 | } |
232 | |
233 | if ( $isDropdownOption ) { |
234 | return $msgLocalizer->msg( 'watchlist-expiry-days-left', [ $expiryInDays ] )->text(); |
235 | } |
236 | |
237 | return $msgLocalizer->msg( 'watchlist-expiring-days-full-text', [ $expiryInDays ] )->text(); |
238 | } |
239 | } |