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