Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
21.05% |
16 / 76 |
|
26.32% |
5 / 19 |
CRAP | |
0.00% |
0 / 1 |
Notification | |
21.33% |
16 / 75 |
|
26.32% |
5 / 19 |
468.14 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
create | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
42 | |||
insert | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
20 | |||
newFromRow | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
3.01 | |||
toDbArray | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getEvent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUser | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTimestamp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getReadTimestamp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isRead | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBundleHash | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTargetPages | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setBundledNotifications | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBundledNotifications | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
canBeBundled | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBundlingKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setBundledElements | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSortingKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
selectFields | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Notifications\Model; |
4 | |
5 | use InvalidArgumentException; |
6 | use MediaWiki\Extension\Notifications\Bundleable; |
7 | use MediaWiki\Extension\Notifications\Hooks\HookRunner; |
8 | use MediaWiki\Extension\Notifications\Mapper\NotificationMapper; |
9 | use MediaWiki\Extension\Notifications\Notifier; |
10 | use MediaWiki\Extension\Notifications\NotifUser; |
11 | use MediaWiki\MediaWikiServices; |
12 | use MediaWiki\User\User; |
13 | use stdClass; |
14 | |
15 | class Notification extends AbstractEntity implements Bundleable { |
16 | |
17 | /** |
18 | * @var User |
19 | */ |
20 | protected $user; |
21 | |
22 | /** |
23 | * @var Event |
24 | */ |
25 | protected $event; |
26 | |
27 | /** |
28 | * The target page object for the notification if there is one. Null means |
29 | * the information has not been loaded. |
30 | * |
31 | * @var TargetPage[]|null |
32 | */ |
33 | protected $targetPages; |
34 | |
35 | /** |
36 | * @var string |
37 | */ |
38 | protected $timestamp; |
39 | |
40 | /** |
41 | * @var string|null |
42 | */ |
43 | protected $readTimestamp; |
44 | |
45 | /** |
46 | * The hash used to determine if a set of event could be bundled |
47 | * @var string |
48 | */ |
49 | protected $bundleHash = ''; |
50 | |
51 | /** |
52 | * @var Notification[] |
53 | */ |
54 | protected $bundledNotifications; |
55 | |
56 | /** |
57 | * Do not use this constructor. |
58 | */ |
59 | protected function __construct() { |
60 | } |
61 | |
62 | /** |
63 | * Creates an Notification object based on event and user |
64 | * @param array $info The following keys are required: |
65 | * - 'event' The Event being notified about. |
66 | * - 'user' The User being notified. |
67 | * @return Notification |
68 | */ |
69 | public static function create( array $info ) { |
70 | $obj = new Notification(); |
71 | static $validFields = [ 'event', 'user' ]; |
72 | |
73 | foreach ( $validFields as $field ) { |
74 | if ( isset( $info[$field] ) ) { |
75 | $obj->$field = $info[$field]; |
76 | } else { |
77 | throw new InvalidArgumentException( "Field $field is required" ); |
78 | } |
79 | } |
80 | |
81 | if ( !$obj->user instanceof User ) { |
82 | throw new InvalidArgumentException( 'Invalid user parameter, expected: User object' ); |
83 | } |
84 | |
85 | if ( !$obj->event instanceof Event ) { |
86 | throw new InvalidArgumentException( 'Invalid event parameter, expected: Event object' ); |
87 | } |
88 | |
89 | // Notification timestamp should be the same as event timestamp |
90 | $obj->timestamp = $obj->event->getTimestamp(); |
91 | // Safe fallback |
92 | if ( !$obj->timestamp ) { |
93 | $obj->timestamp = wfTimestampNow(); |
94 | } |
95 | |
96 | // @Todo - Database insert logic should not be inside the model |
97 | $obj->insert(); |
98 | |
99 | return $obj; |
100 | } |
101 | |
102 | /** |
103 | * Adds this new notification object to the backend storage. |
104 | */ |
105 | protected function insert() { |
106 | global $wgEchoNotifications; |
107 | |
108 | $notifMapper = new NotificationMapper(); |
109 | |
110 | $services = MediaWikiServices::getInstance(); |
111 | $hookRunner = new HookRunner( $services->getHookContainer() ); |
112 | // Get the bundle key for this event if web bundling is enabled |
113 | $bundleKey = ''; |
114 | if ( !empty( $wgEchoNotifications[$this->event->getType()]['bundle']['web'] ) ) { |
115 | Notifier::getBundleRules( $this->event, $bundleKey ); |
116 | } |
117 | |
118 | if ( $bundleKey ) { |
119 | $hash = md5( $bundleKey ); |
120 | $this->bundleHash = $hash; |
121 | } |
122 | |
123 | $notifUser = NotifUser::newFromUser( $this->user ); |
124 | |
125 | // Add listener to refresh notification count upon insert |
126 | $notifMapper->attachListener( 'insert', 'refresh-notif-count', |
127 | static function () use ( $notifUser ) { |
128 | $notifUser->resetNotificationCount(); |
129 | } |
130 | ); |
131 | |
132 | $notifMapper->insert( $this ); |
133 | |
134 | if ( $this->event->getCategory() === 'edit-user-talk' ) { |
135 | $services->getTalkPageNotificationManager() |
136 | ->setUserHasNewMessages( $this->user ); |
137 | } |
138 | $hookRunner->onEchoCreateNotificationComplete( $this ); |
139 | } |
140 | |
141 | /** |
142 | * Load a notification record from std class |
143 | * @param stdClass $row |
144 | * @param TargetPage[]|null $targetPages An array of TargetPage instances, or null if not loaded. |
145 | * @return Notification|false False if failed to load/unserialize |
146 | */ |
147 | public static function newFromRow( $row, array $targetPages = null ) { |
148 | $notification = new Notification(); |
149 | |
150 | if ( property_exists( $row, 'event_type' ) ) { |
151 | $notification->event = Event::newFromRow( $row ); |
152 | } else { |
153 | $notification->event = Event::newFromID( $row->notification_event ); |
154 | } |
155 | |
156 | if ( $notification->event === false ) { |
157 | return false; |
158 | } |
159 | |
160 | $notification->targetPages = $targetPages; |
161 | $notification->user = User::newFromId( $row->notification_user ); |
162 | // Notification timestamp should never be empty |
163 | $notification->timestamp = wfTimestamp( TS_MW, $row->notification_timestamp ); |
164 | $notification->readTimestamp = wfTimestampOrNull( TS_MW, $row->notification_read_timestamp ); |
165 | $notification->bundleHash = $row->notification_bundle_hash; |
166 | |
167 | return $notification; |
168 | } |
169 | |
170 | /** |
171 | * Convert object property to database row array |
172 | * @return array |
173 | */ |
174 | public function toDbArray() { |
175 | return [ |
176 | 'notification_event' => $this->event->getId(), |
177 | 'notification_user' => $this->user->getId(), |
178 | 'notification_timestamp' => $this->timestamp, |
179 | 'notification_read_timestamp' => $this->readTimestamp, |
180 | 'notification_bundle_hash' => $this->bundleHash, |
181 | ]; |
182 | } |
183 | |
184 | /** |
185 | * Getter method |
186 | * @return Event The event for this notification |
187 | */ |
188 | public function getEvent() { |
189 | return $this->event; |
190 | } |
191 | |
192 | /** |
193 | * Getter method |
194 | * @return User The recipient of this notification |
195 | */ |
196 | public function getUser() { |
197 | return $this->user; |
198 | } |
199 | |
200 | /** |
201 | * Getter method |
202 | * @return string Notification creation timestamp |
203 | */ |
204 | public function getTimestamp() { |
205 | return $this->timestamp; |
206 | } |
207 | |
208 | /** |
209 | * Getter method |
210 | * @return string|null Notification read timestamp |
211 | */ |
212 | public function getReadTimestamp() { |
213 | return $this->readTimestamp; |
214 | } |
215 | |
216 | public function isRead() { |
217 | return $this->getReadTimestamp() !== null; |
218 | } |
219 | |
220 | /** |
221 | * Getter method |
222 | * @return string|null Notification bundle hash |
223 | */ |
224 | public function getBundleHash() { |
225 | return $this->bundleHash; |
226 | } |
227 | |
228 | /** |
229 | * Getter method. Returns an array of TargetPage's, or null if they have |
230 | * not been loaded. |
231 | * |
232 | * @return TargetPage[]|null |
233 | */ |
234 | public function getTargetPages() { |
235 | return $this->targetPages; |
236 | } |
237 | |
238 | public function setBundledNotifications( array $notifications ) { |
239 | $this->bundledNotifications = $notifications; |
240 | } |
241 | |
242 | public function getBundledNotifications() { |
243 | return $this->bundledNotifications; |
244 | } |
245 | |
246 | /** |
247 | * @inheritDoc |
248 | */ |
249 | public function canBeBundled() { |
250 | return !$this->isRead(); |
251 | } |
252 | |
253 | /** |
254 | * @inheritDoc |
255 | */ |
256 | public function getBundlingKey() { |
257 | return $this->getBundleHash(); |
258 | } |
259 | |
260 | /** |
261 | * @inheritDoc |
262 | */ |
263 | public function setBundledElements( array $bundleables ) { |
264 | $this->setBundledNotifications( $bundleables ); |
265 | } |
266 | |
267 | /** |
268 | * @inheritDoc |
269 | */ |
270 | public function getSortingKey() { |
271 | return ( $this->isRead() ? '0' : '1' ) . '_' . $this->getTimestamp(); |
272 | } |
273 | |
274 | /** |
275 | * Return the list of fields that should be selected to create |
276 | * a new event with Notification::newFromRow |
277 | * @return string[] |
278 | */ |
279 | public static function selectFields() { |
280 | return array_merge( Event::selectFields(), [ |
281 | 'notification_event', |
282 | 'notification_user', |
283 | 'notification_timestamp', |
284 | 'notification_read_timestamp', |
285 | 'notification_bundle_hash', |
286 | ] ); |
287 | } |
288 | } |
289 | |
290 | class_alias( Notification::class, 'EchoNotification' ); |