SpecialSemanticWatchlist::getAndDisplaySets()   B
last analyzed

Complexity

Conditions 6
Paths 16

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 0
cts 27
cp 0
rs 8.7057
c 0
b 0
f 0
cc 6
nc 16
nop 1
crap 42
1
<?php
2
3
/**
4
 * Semantic watchlist page listing changes to watched properties.
5
 *
6
 * @since 0.1
7
 *
8
 * @file SemanticWatchlist.php
9
 * @ingroup SemanticWatchlist
10
 *
11
 * @licence GNU GPL v3 or later
12
 * @author Jeroen De Dauw < [email protected] >
13
 */
14
class SpecialSemanticWatchlist extends SpecialPage {
15
16
	/**
17
	 * MediaWiki timestamp of when the watchlist was last viewed by the current user.
18
	 *
19
	 * @since 0.1
20
	 *
21
	 * @var integer
22
	 */
23
	protected $lastViewed;
24
25
	/**
26
	 * Constructor.
27
	 *
28
	 * @since 0.1
29
	 */
30
	public function __construct() {
31
		parent::__construct( 'SemanticWatchlist', 'semanticwatch' );
32
	}
33
34
	/**
35
	 * @see SpecialPage::getDescription
36
	 *
37
	 * @since 0.1
38
	 */
39
	public function getDescription() {
40
		return $this->msg( 'special-' . strtolower( $this->getName() ) )->text();
0 ignored issues
show
Bug introduced by
Consider using $this->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
41
	}
42
43
	/**
44
	 * Sets headers - this should be called from the execute() method of all derived classes!
45
	 *
46
	 * @since 0.1
47
	 */
48
	public function setHeaders() {
49
		global $wgOut;
50
		$wgOut->setArticleRelated( false );
51
		$wgOut->setRobotPolicy( 'noindex,nofollow' );
52
		$wgOut->setPageTitle( $this->getDescription() );
53
	}
54
55
	/**
56
	 * Main method.
57
	 *
58
	 * @since 0.1
59
	 *
60
	 * @param string $subPage
61
	 */
62
	public function execute( $subPage ) {
63
		global $wgOut;
64
65
		$this->setHeaders();
66
		$this->outputHeader();
67
68
		$user = $this->getUser();
69
		// If the user is authorized, display the page, if not, show an error.
70
		if ( !$this->userCanExecute( $user ) ) {
71
			$this->displayRestrictionError();
72
			return;
73
		}
74
75
		$this->registerUserView( $user );
76
77
		$wgOut->addHTML( '<p>' );
78
79
		if ( $user->isAllowed( 'semanticwatchgroups' ) ) {
80
			$wgOut->addWikiMsg( 'swl-watchlist-can-mod-groups', 'Special:WatchlistConditions' );
81
			$wgOut->addHTML( '&#160' );
82
		}
83
84
		if ( $this->userHasWatchlistGroups( $user ) ) {
85
			$wgOut->addWikiMsg( 'swl-watchlist-can-mod-prefs', 'Special:Preferences#mw-prefsection-swl' );
86
			$wgOut->addHTML( '</p>' );
87
88
			$this->getAndDisplaySets( $subPage );
89
		}
90
		else {
91
			$wgOut->addWikiMsg( 'swl-watchlist-no-groups', 'Special:Preferences#mw-prefsection-swl' );
92
			$wgOut->addHTML( '</p>' );
93
		}
94
	}
95
96
	/**
97
	 * Obtains the change sets and displays them by calling displayWatchlist.
98
	 * Also takes care of pagination and displaying appropriate message when there are no results.
99
	 *
100
	 * @since 0.1
101
	 *
102
	 * @param string $subPage
103
	 */
104
	protected function getAndDisplaySets( $subPage ) {
105
		global $wgRequest, $wgOut, $wgLang;
106
107
		$limit = $wgRequest->getInt( 'limit', 20 );
108
		$offset = $wgRequest->getInt( 'offset', 0 );
109
		$continue = $wgRequest->getVal( 'continue' );
110
111
		$changeSetData = $this->getChangeSetsData( $limit, $continue );
112
113
		$sets = array();
114
115
		foreach ( $changeSetData['sets'] as $set ) {
116
			$sets[] = SWLChangeSet::newFromArray( $set );
117
		}
118
119
		$newContinue = false;
120
121
		if ( array_key_exists( 'query-continue', $changeSetData ) ) {
122
			$newContinue = $changeSetData['query-continue']['semanticwatchlist']['swcontinue'];
123
		}
124
125
		if ( $offset != 0 || count( $sets ) > 0 ) {
126
			$wgOut->wrapWikiMsg( '<p>$1</p>', array( 'swl-watchlist-position',
127
				$wgLang->formatNum( count( $sets ) ),
128
				$wgLang->formatNum( $offset + 1 )
129
			) );
130
131
			$wgOut->addHTML( $this->getPagingControlHTML( $limit, $continue, $subPage, $newContinue, $offset ) );
132
		}
133
134
		if ( count( $sets ) > 0 ) {
135
			$this->displayWatchlist( $sets );
136
		}
137
		else {
138
			$wgOut->addWikiMsg( 'swl-watchlist-no-items' );
139
		}
140
	}
141
142
	/**
143
	 * Register the user viewed the watchlist,
144
	 * so we know that following chnages should
145
	 * result into notification emails is desired.
146
	 *
147
	 * @since 0.1
148
	 *
149
	 * @param User $user
150
	 */
151
	protected function registerUserView( User $user ) {
152
		$this->lastViewed = $user->getOption( 'swl_last_view' );
153
154
		if ( is_null( $this->lastViewed ) ) {
155
			$this->lastViewed = wfTimestampNow();
156
		}
157
158
		$user->setOption( 'swl_last_view', wfTimestampNow() );
159
		$user->setOption( 'swl_mail_count',0 );
160
		$user->saveSettings();
161
	}
162
163
	/**
164
	 * @since 0.1
165
	 *
166
	 * @return string
167
	 */
168
	protected function getPagingControlHTML( $limit, $currentContinue, $subPage, $newContinue, $offset ) {
169
		global $wgLang;
170
171
		$nextMsg = $this->msg( 'nextn', $limit )->escaped();
172
		$firstMsg = $this->msg( 'swl-watchlist-firstn', $limit )->escaped();
173
174
		if ( $newContinue === false ) {
175
			$nextLink = $nextMsg;
176
		}
177
		else {
178
			$nextLink = Html::element(
179
				'a',
180
				array(
181
					'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( array(
182
						'limit' => $limit,
183
						'continue' => $newContinue,
184
						'offset' => $offset + $limit
185
					) ) ),
186
					'title' => $this->msg( 'nextn-title', $limit )->escaped(),
187
					'class' => 'mw-nextlink'
188
				),
189
				$nextMsg
190
			);
191
		}
192
193
		$limitLinks = array();
194
		$limitLinkArgs = array();
195
196
		if ( $currentContinue == '' ) {
197
			$firstLink = $firstMsg;
198
		}
199
		else {
200
			$limitLinkArgs['continue'] = $currentContinue;
201
202
			$firstLink = Html::element(
203
				'a',
204
				array(
205
					'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( array( 'limit' => $limit ) ) ),
206
					'title' => $this->msg( 'swl-watchlist-firstn-title', $limit )->escaped()
207
				),
208
				$firstMsg
209
			);
210
		}
211
212
		foreach ( array( 20, 50, 100, 250, 500 ) as $limitValue ) {
213
			$limitLinkArgs['limit'] = $limitValue;
214
			if ( $offset != 0 ) {
215
				$limitLinkArgs['offset'] = $offset;
216
			}
217
218
			$limitLinks[] = Html::element(
219
				'a',
220
				array(
221
					'href' => $this->getTitle( $subPage )->getLocalURL( wfArrayToCGI( $limitLinkArgs ) ),
222
					'title' => $this->msg( 'shown-title', $limitValue )->escaped()
223
				),
224
				$wgLang->formatNum( $limitValue )
225
			);
226
		}
227
228
		return Html::rawElement(
229
			'p',
230
			array(),
231
			$this->msg( 'swl-watchlist-pagincontrol' )->rawParams( $wgLang->pipeList( array( $firstLink, $nextLink ) ), $wgLang->pipeList( $limitLinks ) )->escaped()
232
		);
233
	}
234
235
	/**
236
	 * Displays the watchlist.
237
	 *
238
	 * @since 0.1
239
	 *
240
	 * @param array $sets Array of SWLChangeSet
241
	 */
242
	protected function displayWatchlist( array $sets ) {
243
		global $wgOut, $wgLang;
244
245
		$changeSetsHTML = array();
246
247
		foreach ( $sets as /* SWLChangeSet */ $set ) {
248
			$dayKey = substr( $set->getEdit()->getTime(), 0, 8 ); // Get the YYYYMMDD part.
249
250
			if ( !array_key_exists( $dayKey, $changeSetsHTML ) ) {
251
				$changeSetsHTML[$dayKey] = array();
252
			}
253
254
			$changeSetsHTML[$dayKey][] = $this->getChangeSetHTML( $set );
255
		}
256
257
		krsort( $changeSetsHTML );
258
259
		foreach ( $changeSetsHTML as $dayKey => $daySets ) {
260
			$wgOut->addHTML( Html::element(
261
				'h4',
262
				array(),
263
				$wgLang->date( str_pad( $dayKey, 14, '0' ) )
264
			) );
265
266
			$wgOut->addHTML( '<ul>' );
267
268
			foreach ( $daySets as $setHTML ) {
269
				$wgOut->addHTML( $setHTML );
270
			}
271
272
			$wgOut->addHTML( '</ul>' );
273
		}
274
275
		SMWOutputs::commitToOutputPage( $wgOut );
276
277
		$wgOut->addModules( 'ext.swl.watchlist' );
278
	}
279
280
	/**
281
	 * Returns the response of an internal request to the API semanticwatchlist query module.
282
	 *
283
	 * @since 0.1
284
	 *
285
	 * @param integer $limit
286
	 * @param string $continue
287
	 *
288
	 * @return array
289
	 */
290
	protected function getChangeSetsData( $limit, $continue ) {
291
		$requestData = array(
292
			'action' => 'query',
293
			'list' => 'semanticwatchlist',
294
			'format' => 'json',
295
			'swuserid' => $GLOBALS['wgUser']->getId(),
296
			'swlimit' => $limit,
297
			'swcontinue' => $continue,
298
			'swmerge' => '1'
299
		);
300
301
		$api = new ApiMain( new FauxRequest( $requestData, true ), true );
302
		$api->execute();
303
		return $api->getResult()->getResultData( null, [
304
												 'BC' => [],
305
												 'Types' => [],
306
												 'Strip' => 'all',
307
		] );
308
	}
309
310
	/**
311
	 * Gets the HTML for a single change set (edit).
312
	 *
313
	 * @since 0.1
314
	 *
315
	 * @param SWLChangeSet $changeSet
316
	 *
317
	 * @return string
318
	 */
319
	protected function getChangeSetHTML( SWLChangeSet $changeSet ) {
320
		global $wgLang;
321
322
		$edit = $changeSet->getEdit();
323
324
		$html = '';
325
326
		$html .= '<li>';
327
328
		$html .=
329
			'<p>' .
330
				$wgLang->time( $edit->getTime(), true ) . ' ' .
331
				Html::element(
332
					'a',
333
					array( 'href' => $edit->getTitle()->getLocalURL() ),
334
					$edit->getTitle()->getText()
335
				) . ' (' .
336
				Html::element(
337
					'a',
338
					array( 'href' => $edit->getTitle()->getLocalURL( 'action=history' ) ),
339
					$this->msg( 'hist' )->text()
340
				) . ') . . ' .
341
				Html::element(
342
					'a',
343
					array( 'href' => $edit->getUser()->getUserPage()->getLocalURL() ),
344
					$edit->getUser()->getName()
345
				) . ' (' .
346
				Html::element(
347
					'a',
348
					array( 'href' => $edit->getUser()->getTalkPage()->getLocalURL() ),
349
					$this->msg( 'talkpagelinktext' )->text()
350
				) . ' | ' .
351
				( $edit->getUser()->isAnon() ? '' :
352
					Html::element(
353
						'a',
354
						array( 'href' => SpecialPage::getTitleFor( 'Contributions', $edit->getUser()->getName() )->getLocalURL() ),
355
						$this->msg( 'contribslink' )->text()
356
					) . ' | '
357
				) .
358
				Html::element(
359
					'a',
360
					array( 'href' => SpecialPage::getTitleFor( 'Block', $edit->getUser()->getName() )->getLocalURL() ),
361
					$this->msg( 'blocklink' )->text()
362
				) . ')' .
363
				( $edit->getTime() > $this->lastViewed ? ' <b>' . $this->msg( 'swl-new-item' )->text() . '</b>' : '' )	.
364
			'</p>'
365
		;
366
367
		$propertyHTML= array();
368
369
		foreach ( $changeSet->getAllProperties() as /* SMWDIProperty */ $property ) {
370
			$propertyHTML[] = $this->getPropertyHTML( $property, $changeSet->getAllPropertyChanges( $property ) );
371
		}
372
373
		$html .= implode( '', $propertyHTML );
374
375
		$html .=  '</li>';
376
377
		return $html;
378
	}
379
380
	/**
381
	 * Returns the HTML for the changes to a single propety.
382
	 *
383
	 * @param SMWDIProperty $property
384
	 * @param array $changes Array of SWLPropertyChange
385
	 *
386
	 * @return string
387
	 */
388
	protected function getPropertyHTML( SMWDIProperty $property, array $changes ) {
389
		$insertions = array();
390
		$deletions = array();
391
392
		// Convert the changes into a list of insertions and a list of deletions.
393
		foreach ( $changes as /* SWLPropertyChange */ $change ) {
394
			if ( !is_null( $change->getOldValue() ) ) {
395
				$deletions[] = SMWDataValueFactory::newDataItemValue( $change->getOldValue(), $property )->getLongHTMLText();
396
			}
397
			if ( !is_null( $change->getNewValue() ) ) {
398
				$insertions[] = SMWDataValueFactory::newDataItemValue( $change->getNewValue(), $property )->getLongHTMLText();
399
			}
400
		}
401
402
		$lines = array();
403
404
		if ( count( $deletions ) > 0 ) {
405
			$lines[] = Html::element( 'div', array( 'class' => 'swl-watchlist-deletions' ), $this->msg( 'swl-watchlist-deletions' )->text() ) . ' ' . implode( ', ', $deletions );
406
		}
407
408
		if ( count( $insertions ) > 0 ) {
409
			$lines[] = Html::element( 'div', array( 'class' => 'swl-watchlist-insertions' ), $this->msg( 'swl-watchlist-insertions' )->text() ) . ' ' . implode( ', ', $insertions );
410
		}
411
412
		$html = Html::element( 'span', array( 'class' => 'swl-watchlist-prop' ), $property->getLabel() );
413
414
		$html .= Html::rawElement(
415
			'div',
416
			array( 'class' => 'swl-prop-div' ),
417
			implode( '<br />', $lines )
418
		);
419
420
		return $html;
421
	}
422
423
	/**
424
	 * Rteurns if the specified user has any watchlist groups in his notification list.
425
	 *
426
	 * @since 0.1
427
	 *
428
	 * @param User $user
429
	 *
430
	 * @return boolean
431
	 */
432
	protected function userHasWatchlistGroups( User $user ) {
433
		if ( !$user->isLoggedIn() ) {
434
			return false;
435
		}
436
437
		$dbr = wfGetDB( DB_REPLICA );
438
439
		$group = $dbr->selectRow(
440
			'swl_users_per_group',
441
			array( 'upg_group_id' ),
442
			array( 'upg_user_id' => $user->getId() )
443
		);
444
445
		return $group !== false;
446
	}
447
448
}
449