Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 158 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
SpecialNotifications | |
0.00% |
0 / 158 |
|
0.00% |
0 / 4 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 143 |
|
0.00% |
0 / 1 |
210 | |||
buildSubtitle | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Notifications\Special; |
4 | |
5 | use MediaWiki\Extension\Notifications\DataOutputFormatter; |
6 | use MediaWiki\Extension\Notifications\NotifUser; |
7 | use MediaWiki\Extension\Notifications\OOUI\LabelIconWidget; |
8 | use MediaWiki\Extension\Notifications\SeenTime; |
9 | use MediaWiki\Html\Html; |
10 | use MediaWiki\SpecialPage\SpecialPage; |
11 | use OOUI; |
12 | |
13 | class SpecialNotifications extends SpecialPage { |
14 | |
15 | /** |
16 | * Number of notification records to display per page/load |
17 | */ |
18 | private const DISPLAY_NUM = 20; |
19 | |
20 | public function __construct() { |
21 | parent::__construct( 'Notifications' ); |
22 | } |
23 | |
24 | /** |
25 | * @param string|null $par |
26 | */ |
27 | public function execute( $par ) { |
28 | $this->setHeaders(); |
29 | |
30 | $out = $this->getOutput(); |
31 | $out->setPageTitleMsg( $this->msg( 'echo-specialpage' ) ); |
32 | |
33 | $this->addHelpLink( 'Help:Notifications/Special:Notifications' ); |
34 | |
35 | $out->addJsConfigVars( 'wgNotificationsSpecialPageLinks', [ |
36 | 'preferences' => SpecialPage::getTitleFor( 'Preferences', false, 'mw-prefsection-echo' )->getLinkURL(), |
37 | ] ); |
38 | |
39 | $user = $this->getUser(); |
40 | if ( !$user->isRegistered() ) { |
41 | // Redirect to login page and inform user of the need to login |
42 | $this->requireLogin( 'echo-notification-loginrequired' ); |
43 | return; |
44 | } |
45 | |
46 | $out->addSubtitle( $this->buildSubtitle() ); |
47 | |
48 | $out->enableOOUI(); |
49 | |
50 | $pager = new NotificationPager( $this->getContext() ); |
51 | $pager->setOffset( $this->getRequest()->getVal( 'offset' ) ); |
52 | $pager->setLimit( $this->getRequest()->getInt( 'limit', self::DISPLAY_NUM ) ); |
53 | $notifications = $pager->getNotifications(); |
54 | |
55 | $noJSDiv = new OOUI\Tag(); |
56 | $noJSDiv->addClasses( [ 'mw-echo-special-nojs' ] ); |
57 | |
58 | // If there are no notifications, display a message saying so |
59 | if ( !$notifications ) { |
60 | // Wrap this with nojs so it is still hidden if JS is loading |
61 | $noJSDiv->appendContent( |
62 | new OOUI\LabelWidget( [ 'label' => $this->msg( 'echo-none' )->text() ] ) |
63 | ); |
64 | $out->addHTML( $noJSDiv ); |
65 | $out->addModules( [ 'ext.echo.special' ] ); |
66 | return; |
67 | } |
68 | |
69 | $notif = []; |
70 | foreach ( $notifications as $notification ) { |
71 | $output = DataOutputFormatter::formatOutput( $notification, 'special', $user, $this->getLanguage() ); |
72 | if ( $output ) { |
73 | $notif[] = $output; |
74 | } |
75 | } |
76 | |
77 | // Add the notifications to the page (interspersed with date headers) |
78 | $dateHeader = ''; |
79 | $anyUnread = false; |
80 | $seenTime = SeenTime::newFromUser( $user )->getTime(); |
81 | $notifArray = []; |
82 | foreach ( $notif as $row ) { |
83 | if ( !$row['*'] ) { |
84 | continue; |
85 | } |
86 | |
87 | $classes = [ 'mw-echo-notification' ]; |
88 | |
89 | if ( $seenTime !== null && $row['timestamp']['mw'] > $seenTime ) { |
90 | $classes[] = 'mw-echo-notification-unseen'; |
91 | } |
92 | |
93 | // Output the date header if it has not been displayed |
94 | if ( $dateHeader !== $row['timestamp']['date'] ) { |
95 | $dateHeader = $row['timestamp']['date']; |
96 | } |
97 | // Collect unread IDs |
98 | if ( !isset( $row['read'] ) ) { |
99 | $classes[] = 'mw-echo-notification-unread'; |
100 | $anyUnread = true; |
101 | $notifArray[ $dateHeader ][ 'unread' ][] = $row['id']; |
102 | } |
103 | |
104 | $li = new OOUI\Tag( 'li' ); |
105 | $li |
106 | ->addClasses( $classes ) |
107 | ->setAttributes( [ |
108 | 'data-notification-category' => $row['category'], |
109 | 'data-notification-event' => $row['id'], |
110 | 'data-notification-type' => $row['type'] |
111 | ] ) |
112 | ->appendContent( new OOUI\HtmlSnippet( $row['*'] ) ); |
113 | |
114 | // Store |
115 | $notifArray[ $dateHeader ][ 'notices' ][] = $li; |
116 | } |
117 | |
118 | $markAllAsReadFormWrapper = ''; |
119 | // Ensure there are some unread notifications |
120 | if ( $anyUnread ) { |
121 | $markReadSpecialPage = new SpecialNotificationsMarkRead(); |
122 | $markReadSpecialPage->setContext( $this->getContext() ); |
123 | $notifUser = NotifUser::newFromUser( $user ); |
124 | $unreadCount = $notifUser->getAlertCount() + $notifUser->getMessageCount(); |
125 | |
126 | $markAllAsReadText = $this |
127 | ->msg( 'echo-mark-all-as-read' ) |
128 | ->numParams( $unreadCount ) |
129 | ->text(); |
130 | $markAllAsReadLabelIcon = new LabelIconWidget( [ |
131 | 'label' => $markAllAsReadText, |
132 | 'icon' => 'checkAll', |
133 | ] ); |
134 | |
135 | $markAllAsReadForm = $markReadSpecialPage->getMinimalForm( |
136 | [ 'ALL' ], |
137 | $markAllAsReadText, |
138 | true, |
139 | $markAllAsReadLabelIcon->toString() |
140 | ); |
141 | |
142 | // First submission attempt |
143 | $formHtml = $markAllAsReadForm->prepareForm()->getHTML( false ); |
144 | |
145 | $markAllAsReadFormWrapper = new OOUI\Tag(); |
146 | $markAllAsReadFormWrapper |
147 | ->addClasses( [ 'mw-echo-special-markAllReadButton' ] ) |
148 | ->appendContent( new OOUI\HtmlSnippet( $formHtml ) ); |
149 | } |
150 | |
151 | // Build the list |
152 | $notices = new OOUI\Tag( 'ul' ); |
153 | $notices->addClasses( [ 'mw-echo-special-notifications' ] ); |
154 | |
155 | $markReadSpecialPage = new SpecialNotificationsMarkRead(); |
156 | $markReadSpecialPage->setContext( $this->getContext() ); |
157 | foreach ( $notifArray as $section => $data ) { |
158 | $heading = ( new OOUI\Tag( 'li' ) )->addClasses( [ 'mw-echo-date-section' ] ); |
159 | |
160 | $dateTitle = new OOUI\LabelWidget( [ |
161 | 'classes' => [ 'mw-echo-date-section-text' ], |
162 | 'label' => $section |
163 | ] ); |
164 | |
165 | $heading->appendContent( $dateTitle ); |
166 | |
167 | // Mark all read button |
168 | if ( isset( $data[ 'unread' ] ) ) { |
169 | // tell the UI to show 'unread' notifications only (instead of 'all') |
170 | $out->addJsConfigVars( 'wgEchoReadState', 'unread' ); |
171 | |
172 | $markReadSectionText = $this->msg( 'echo-specialpage-section-markread' )->text(); |
173 | $markAsReadLabelIcon = new LabelIconWidget( [ |
174 | 'label' => $markReadSectionText, |
175 | 'icon' => 'checkAll', |
176 | ] ); |
177 | |
178 | // There are unread notices. Add the 'mark section as read' button |
179 | $markSectionAsReadForm = $markReadSpecialPage->getMinimalForm( |
180 | $data[ 'unread' ], |
181 | $markReadSectionText, |
182 | true, |
183 | $markAsReadLabelIcon->toString() |
184 | ); |
185 | |
186 | // First submission attempt |
187 | $formHtml = $markSectionAsReadForm->prepareForm()->getHTML( false ); |
188 | |
189 | $formWrapper = new OOUI\Tag(); |
190 | $formWrapper |
191 | ->addClasses( [ 'mw-echo-markAsReadSectionButton' ] ) |
192 | ->appendContent( new OOUI\HtmlSnippet( $formHtml ) ); |
193 | |
194 | $heading->appendContent( $formWrapper ); |
195 | } |
196 | |
197 | // These two must be separate, because $data[ 'notices' ] |
198 | // is an array |
199 | $notices |
200 | ->appendContent( $heading ) |
201 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset https://github.com/phan/phan/issues/4735 |
202 | ->appendContent( $data[ 'notices' ] ); |
203 | } |
204 | |
205 | $navBar = $pager->getNavigationBar(); |
206 | |
207 | $navTop = new OOUI\Tag(); |
208 | $navBottom = new OOUI\Tag(); |
209 | $container = new OOUI\Tag(); |
210 | |
211 | $navTop |
212 | ->addClasses( [ 'mw-echo-special-navbar-top' ] ) |
213 | ->appendContent( new OOUI\HtmlSnippet( $navBar ) ); |
214 | $navBottom |
215 | ->addClasses( [ 'mw-echo-special-navbar-bottom' ] ) |
216 | ->appendContent( new OOUI\HtmlSnippet( $navBar ) ); |
217 | |
218 | // Put it all together |
219 | $container |
220 | ->addClasses( [ 'mw-echo-special-container' ] ) |
221 | ->appendContent( |
222 | $navTop, |
223 | $markAllAsReadFormWrapper, |
224 | $notices, |
225 | $navBottom |
226 | ); |
227 | |
228 | // Wrap with nojs div |
229 | $noJSDiv->appendContent( $container ); |
230 | |
231 | $out->addHTML( $noJSDiv ); |
232 | |
233 | $out->addModules( [ 'ext.echo.special' ] ); |
234 | |
235 | // For no-js support |
236 | $out->addModuleStyles( [ |
237 | 'ext.echo.styles.notifications', |
238 | 'ext.echo.styles.special', |
239 | 'oojs-ui.styles.icons-alerts', |
240 | 'oojs-ui.styles.icons-interactions', |
241 | ] ); |
242 | } |
243 | |
244 | /** |
245 | * Build the subtitle (more info and preference links) |
246 | * @return string HTML for the subtitle |
247 | */ |
248 | public function buildSubtitle() { |
249 | $lang = $this->getLanguage(); |
250 | $subtitleLinks = []; |
251 | // Preferences link |
252 | $subtitleLinks[] = Html::element( |
253 | 'a', |
254 | [ |
255 | 'href' => SpecialPage::getTitleFor( 'Preferences', false, 'mw-prefsection-echo' )->getLinkURL(), |
256 | 'id' => 'mw-echo-pref-link', |
257 | 'class' => 'mw-echo-special-header-link', |
258 | 'title' => $this->msg( 'preferences' )->text() |
259 | ], |
260 | $this->msg( 'preferences' )->text() |
261 | ); |
262 | // using pipeList to make it easier to add some links in the future |
263 | return $lang->pipeList( $subtitleLinks ); |
264 | } |
265 | |
266 | protected function getGroupName() { |
267 | return 'login'; |
268 | } |
269 | } |