Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.56% |
68 / 90 |
|
42.86% |
3 / 7 |
CRAP | |
0.00% |
0 / 1 |
UserNotificationGateway | |
75.56% |
68 / 90 |
|
42.86% |
3 / 7 |
24.27 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getDB | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
markRead | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
5.00 | |||
markUnRead | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
30 | |||
markAllRead | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
2.00 | |||
getCappedNotificationCount | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
3.00 | |||
getUnreadNotifications | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Notifications\Gateway; |
4 | |
5 | use MediaWiki\Config\Config; |
6 | use MediaWiki\Extension\Notifications\DbFactory; |
7 | use MediaWiki\Extension\Notifications\NotifUser; |
8 | use MediaWiki\User\UserIdentity; |
9 | |
10 | /** |
11 | * Database gateway which handles direct database interaction with the |
12 | * echo_notification & echo_event for a user, that wouldn't require |
13 | * loading data into models |
14 | */ |
15 | class UserNotificationGateway { |
16 | |
17 | /** |
18 | * @var DbFactory |
19 | */ |
20 | protected $dbFactory; |
21 | |
22 | /** |
23 | * @var UserIdentity |
24 | */ |
25 | protected $user; |
26 | |
27 | /** |
28 | * The tables for this gateway. |
29 | * |
30 | * @var string |
31 | */ |
32 | protected static $eventTable = 'echo_event'; |
33 | |
34 | /** |
35 | * The tables for this gateway. |
36 | * |
37 | * @var string |
38 | */ |
39 | protected static $notificationTable = 'echo_notification'; |
40 | |
41 | /** |
42 | * @var Config |
43 | */ |
44 | private $config; |
45 | |
46 | /** |
47 | * @param UserIdentity $user |
48 | * @param DbFactory $dbFactory |
49 | * @param Config $config |
50 | */ |
51 | public function __construct( UserIdentity $user, DbFactory $dbFactory, Config $config ) { |
52 | $this->user = $user; |
53 | $this->dbFactory = $dbFactory; |
54 | $this->config = $config; |
55 | } |
56 | |
57 | public function getDB( $dbSource ) { |
58 | return $this->dbFactory->getEchoDb( $dbSource ); |
59 | } |
60 | |
61 | /** |
62 | * Mark notifications as read |
63 | * @param array $eventIDs |
64 | * @return bool Returns true when data has been updated in DB, false on |
65 | * failure, or when there was nothing to update |
66 | */ |
67 | public function markRead( array $eventIDs ) { |
68 | if ( !$eventIDs ) { |
69 | return false; |
70 | } |
71 | |
72 | $dbw = $this->getDB( DB_PRIMARY ); |
73 | if ( $dbw->isReadOnly() ) { |
74 | return false; |
75 | } |
76 | |
77 | $success = true; |
78 | foreach ( |
79 | array_chunk( $eventIDs, $this->config->get( 'UpdateRowsPerQuery' ) ) as $batch |
80 | ) { |
81 | $dbw->newUpdateQueryBuilder() |
82 | ->update( self::$notificationTable ) |
83 | ->set( [ 'notification_read_timestamp' => $dbw->timestamp( wfTimestampNow() ) ] ) |
84 | ->where( [ |
85 | 'notification_user' => $this->user->getId(), |
86 | 'notification_event' => $batch, |
87 | 'notification_read_timestamp' => null, |
88 | ] ) |
89 | ->caller( __METHOD__ ) |
90 | ->execute(); |
91 | $success = $dbw->affectedRows() && $success; |
92 | } |
93 | |
94 | return $success; |
95 | } |
96 | |
97 | /** |
98 | * Mark notifications as unread |
99 | * @param array $eventIDs |
100 | * @return bool Returns true when data has been updated in DB, false on |
101 | * failure, or when there was nothing to update |
102 | */ |
103 | public function markUnRead( array $eventIDs ) { |
104 | if ( !$eventIDs ) { |
105 | return false; |
106 | } |
107 | |
108 | $dbw = $this->getDB( DB_PRIMARY ); |
109 | if ( $dbw->isReadOnly() ) { |
110 | return false; |
111 | } |
112 | |
113 | $success = true; |
114 | foreach ( |
115 | array_chunk( $eventIDs, $this->config->get( 'UpdateRowsPerQuery' ) ) as $batch |
116 | ) { |
117 | $dbw->newUpdateQueryBuilder() |
118 | ->update( self::$notificationTable ) |
119 | ->set( [ 'notification_read_timestamp' => null ] ) |
120 | ->where( [ |
121 | 'notification_user' => $this->user->getId(), |
122 | 'notification_event' => $batch, |
123 | $dbw->expr( 'notification_read_timestamp', '!=', null ), |
124 | ] ) |
125 | ->caller( __METHOD__ ) |
126 | ->execute(); |
127 | $success = $dbw->affectedRows() && $success; |
128 | } |
129 | return $success; |
130 | } |
131 | |
132 | /** |
133 | * Mark all notification as read, use NotifUser::markAllRead() instead |
134 | * @deprecated may need this when running in a job or revive this when we |
135 | * have updateJoin() |
136 | */ |
137 | public function markAllRead() { |
138 | $dbw = $this->getDB( DB_PRIMARY ); |
139 | if ( $dbw->isReadOnly() ) { |
140 | return false; |
141 | } |
142 | |
143 | $dbw->newUpdateQueryBuilder() |
144 | ->update( self::$notificationTable ) |
145 | ->set( [ 'notification_read_timestamp' => $dbw->timestamp( wfTimestampNow() ) ] ) |
146 | ->where( [ |
147 | 'notification_user' => $this->user->getId(), |
148 | 'notification_read_timestamp' => null, |
149 | ] ) |
150 | ->caller( __METHOD__ ) |
151 | ->execute(); |
152 | |
153 | return true; |
154 | } |
155 | |
156 | /** |
157 | * Get notification count for the types specified |
158 | * @param int $dbSource use primary database or replica storage to pull count |
159 | * @param array $eventTypesToLoad event types to retrieve |
160 | * @param int $cap Max count |
161 | * @return int |
162 | */ |
163 | public function getCappedNotificationCount( |
164 | $dbSource, |
165 | array $eventTypesToLoad = [], |
166 | $cap = NotifUser::MAX_BADGE_COUNT |
167 | ) { |
168 | // double check |
169 | if ( !in_array( $dbSource, [ DB_REPLICA, DB_PRIMARY ] ) ) { |
170 | $dbSource = DB_REPLICA; |
171 | } |
172 | |
173 | if ( !$eventTypesToLoad ) { |
174 | return 0; |
175 | } |
176 | |
177 | $db = $this->getDB( $dbSource ); |
178 | return $db->newSelectQueryBuilder() |
179 | ->select( '1' ) |
180 | ->from( self::$notificationTable ) |
181 | ->leftJoin( self::$eventTable, null, 'notification_event=event_id' ) |
182 | ->where( [ |
183 | 'notification_user' => $this->user->getId(), |
184 | 'notification_read_timestamp' => null, |
185 | 'event_deleted' => 0, |
186 | 'event_type' => $eventTypesToLoad, |
187 | ] ) |
188 | ->limit( $cap ) |
189 | ->caller( __METHOD__ ) |
190 | ->fetchRowCount(); |
191 | } |
192 | |
193 | /** |
194 | * IMPORTANT: should only call this function if the number of unread notification |
195 | * is reasonable, for example, unread notification count is less than the max |
196 | * display defined in oNotifUser::MAX_BADGE_COUNT |
197 | * @param string $type |
198 | * @return int[] |
199 | */ |
200 | public function getUnreadNotifications( $type ) { |
201 | $dbr = $this->getDB( DB_REPLICA ); |
202 | $res = $dbr->newSelectQueryBuilder() |
203 | ->select( 'notification_event' ) |
204 | ->from( self::$notificationTable ) |
205 | ->join( self::$eventTable, null, 'notification_event = event_id' ) |
206 | ->where( [ |
207 | 'notification_user' => $this->user->getId(), |
208 | 'notification_read_timestamp' => null, |
209 | 'event_deleted' => 0, |
210 | 'event_type' => $type, |
211 | ] ) |
212 | ->caller( __METHOD__ ) |
213 | ->fetchResultSet(); |
214 | |
215 | $eventIds = []; |
216 | foreach ( $res as $row ) { |
217 | $eventIds[$row->notification_event] = $row->notification_event; |
218 | } |
219 | |
220 | return $eventIds; |
221 | } |
222 | |
223 | } |