Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
8.06% |
20 / 248 |
|
5.26% |
1 / 19 |
CRAP | |
0.00% |
0 / 1 |
EventsListPager | |
8.06% |
20 / 248 |
|
5.26% |
1 / 19 |
2763.91 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
1 | |||
doExtraPreprocessing | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
42 | |||
getRow | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getLimitSelectList | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
isHeaderRowNeeded | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
formatRow | |
0.00% |
0 / 52 |
|
0.00% |
0 / 1 |
6 | |||
getOrganizersText | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
42 | |||
getIndexField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNavigationBar | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getMeetingTypeMsg | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
30 | |||
getYearFromTimestamp | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getMonthHeader | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getDayFromTimestamp | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
offsetTimestamp | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getSubqueryInfo | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
buildQueryInfo | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
56 | |||
getDateRangeCond | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
getWikiList | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
getWikiListWidget | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | declare( strict_types=1 ); |
4 | |
5 | namespace MediaWiki\Extension\CampaignEvents\Pager; |
6 | |
7 | use MediaWiki\Cache\LinkBatchFactory; |
8 | use MediaWiki\Extension\CampaignEvents\Database\CampaignsDatabaseHelper; |
9 | use MediaWiki\Extension\CampaignEvents\Event\EventRegistration; |
10 | use MediaWiki\Extension\CampaignEvents\Event\Store\EventStore; |
11 | use MediaWiki\Extension\CampaignEvents\Event\Store\EventWikisStore; |
12 | use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsCentralUserLookup; |
13 | use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsPageFactory; |
14 | use MediaWiki\Extension\CampaignEvents\MWEntity\PageURLResolver; |
15 | use MediaWiki\Extension\CampaignEvents\MWEntity\UserLinker; |
16 | use MediaWiki\Extension\CampaignEvents\MWEntity\WikiLookup; |
17 | use MediaWiki\Extension\CampaignEvents\Organizers\Organizer; |
18 | use MediaWiki\Extension\CampaignEvents\Organizers\OrganizersStore; |
19 | use MediaWiki\Extension\CampaignEvents\Organizers\Roles; |
20 | use MediaWiki\Extension\CampaignEvents\Special\SpecialEventDetails; |
21 | use MediaWiki\Extension\CampaignEvents\Widget\TextWithIconWidget; |
22 | use MediaWiki\Html\Html; |
23 | use MediaWiki\Pager\IndexPager; |
24 | use MediaWiki\Pager\ReverseChronologicalPager; |
25 | use MediaWiki\SpecialPage\SpecialPage; |
26 | use MediaWiki\User\Options\UserOptionsLookup; |
27 | use MediaWiki\Utils\MWTimestamp; |
28 | use MediaWiki\WikiMap\WikiMap; |
29 | use OOUI\Exception; |
30 | use OOUI\HtmlSnippet; |
31 | use OOUI\Tag; |
32 | use stdClass; |
33 | use UnexpectedValueException; |
34 | use Wikimedia\Rdbms\IResultWrapper; |
35 | use Wikimedia\Timestamp\TimestampException; |
36 | |
37 | class EventsListPager extends ReverseChronologicalPager { |
38 | use EventPagerTrait { |
39 | EventPagerTrait::getSubqueryInfo as getDefaultSubqueryInfo; |
40 | } |
41 | |
42 | private const DISPLAYED_WIKI_COUNT = 3; |
43 | private UserLinker $userLinker; |
44 | private CampaignsPageFactory $campaignsPageFactory; |
45 | private PageURLResolver $pageURLResolver; |
46 | private OrganizersStore $organizerStore; |
47 | private LinkBatchFactory $linkBatchFactory; |
48 | private UserOptionsLookup $userOptionsLookup; |
49 | private CampaignsCentralUserLookup $centralUserLookup; |
50 | private WikiLookup $wikiLookup; |
51 | private EventWikisStore $eventWikisStore; |
52 | |
53 | private string $search; |
54 | /** One of the EventRegistration::MEETING_TYPE_* constants */ |
55 | private ?int $meetingType; |
56 | /** dbnames of the wikis chosen */ |
57 | private array $filterWiki; |
58 | private string $startDate; |
59 | private string $endDate; |
60 | private bool $showOngoing; |
61 | |
62 | private string $lastHeaderTimestamp; |
63 | /** @var array<int,Organizer|null> Maps event ID to the event creator, if available, else to null. */ |
64 | private array $creators = []; |
65 | /** |
66 | * @var array<int,Organizer[]> Maps event ID to a list of additional event organizers, |
67 | * NOT including the event creator. |
68 | */ |
69 | private array $extraOrganizers = []; |
70 | /** @var array<int,int> Maps event ID to the total number of organizers of that event. */ |
71 | private array $organizerCounts = []; |
72 | /** @var array<int,string[]|true> Maps event ID to all wikis assigned to the event. */ |
73 | private array $eventWikis = []; |
74 | |
75 | public function __construct( |
76 | UserLinker $userLinker, |
77 | CampaignsPageFactory $pageFactory, |
78 | PageURLResolver $pageURLResolver, |
79 | OrganizersStore $organizerStore, |
80 | LinkBatchFactory $linkBatchFactory, |
81 | UserOptionsLookup $userOptionsLookup, |
82 | CampaignsDatabaseHelper $databaseHelper, |
83 | CampaignsCentralUserLookup $centralUserLookup, |
84 | WikiLookup $wikiLookup, |
85 | EventWikisStore $eventWikisStore, |
86 | string $search, |
87 | ?int $meetingType, |
88 | string $startDate, |
89 | string $endDate, |
90 | bool $showOngoing, |
91 | array $filterWiki |
92 | ) { |
93 | // Set the database before calling the parent constructor, otherwise it'll use the local one. |
94 | $this->mDb = $databaseHelper->getDBConnection( DB_REPLICA ); |
95 | parent::__construct( $this->getContext(), $this->getLinkRenderer() ); |
96 | |
97 | $this->userLinker = $userLinker; |
98 | $this->campaignsPageFactory = $pageFactory; |
99 | $this->pageURLResolver = $pageURLResolver; |
100 | $this->organizerStore = $organizerStore; |
101 | $this->linkBatchFactory = $linkBatchFactory; |
102 | $this->userOptionsLookup = $userOptionsLookup; |
103 | $this->centralUserLookup = $centralUserLookup; |
104 | |
105 | $this->search = $search; |
106 | $this->meetingType = $meetingType; |
107 | $this->startDate = $startDate; |
108 | $this->endDate = $endDate; |
109 | $this->showOngoing = $showOngoing; |
110 | |
111 | $this->getDateRangeCond( $startDate, $endDate ); |
112 | $this->mDefaultDirection = IndexPager::DIR_ASCENDING; |
113 | $this->lastHeaderTimestamp = ''; |
114 | $this->filterWiki = $filterWiki; |
115 | $this->wikiLookup = $wikiLookup; |
116 | $this->eventWikisStore = $eventWikisStore; |
117 | } |
118 | |
119 | /** |
120 | * @see EventPagerTrait::doExtraPreprocessing |
121 | */ |
122 | private function doExtraPreprocessing( IResultWrapper $result ): void { |
123 | $eventIDs = []; |
124 | foreach ( $result as $row ) { |
125 | $eventIDs[] = (int)$row->event_id; |
126 | } |
127 | $result->seek( 0 ); |
128 | |
129 | $this->eventWikis = $this->eventWikisStore->getEventWikisMulti( $eventIDs ); |
130 | |
131 | $this->creators = $this->organizerStore->getEventCreators( |
132 | $eventIDs, |
133 | OrganizersStore::GET_CREATOR_EXCLUDE_DELETED |
134 | ); |
135 | $this->extraOrganizers = $this->organizerStore->getOrganizersForEvents( $eventIDs, 2 ); |
136 | $this->organizerCounts = $this->organizerStore->getOrganizerCountForEvents( $eventIDs ); |
137 | |
138 | $organizerUserIDsMap = []; |
139 | foreach ( $this->creators as $creator ) { |
140 | if ( $creator ) { |
141 | $organizerUserIDsMap[$creator->getUser()->getCentralID()] = null; |
142 | } |
143 | } |
144 | foreach ( $this->extraOrganizers as $eventExtraOrganizer ) { |
145 | foreach ( $eventExtraOrganizer as $organizer ) { |
146 | $organizerUserIDsMap[ $organizer->getUser()->getCentralID() ] = null; |
147 | } |
148 | } |
149 | |
150 | // Run a single query to get all the organizer names at once, and also check for user page existence. |
151 | $organizerNames = $this->centralUserLookup->getNamesIncludingDeletedAndSuppressed( $organizerUserIDsMap ); |
152 | $this->userLinker->preloadUserLinks( $organizerNames ); |
153 | } |
154 | |
155 | /** |
156 | * @inheritDoc |
157 | */ |
158 | public function getRow( $row ): string { |
159 | $s = ''; |
160 | |
161 | $timestampField = $this->getTimestampField(); |
162 | $timestamp = $row->$timestampField; |
163 | $closeList = $this->isHeaderRowNeeded( $timestamp ); |
164 | if ( $closeList ) { |
165 | $s .= $this->getEndGroup(); |
166 | } |
167 | if ( $this->isHeaderRowNeeded( $timestamp ) ) { |
168 | $s .= $this->getHeaderRow( $this->getMonthHeader( $timestamp ) ); |
169 | $this->lastHeaderTimestamp = $timestamp; |
170 | } |
171 | $s .= $this->formatRow( $row ); |
172 | |
173 | return $s; |
174 | } |
175 | |
176 | /** |
177 | * Copied from {@see TablePager::getLimitSelectList()}. |
178 | * XXX This should probably live elsewhere in core and be easier to reuse. |
179 | * |
180 | * @return array |
181 | */ |
182 | public function getLimitSelectList() { |
183 | # Add the current limit from the query string |
184 | # to avoid that the limit is lost after clicking Go next time |
185 | if ( !in_array( $this->mLimit, $this->mLimitsShown, true ) ) { |
186 | $this->mLimitsShown[] = $this->mLimit; |
187 | sort( $this->mLimitsShown ); |
188 | } |
189 | $ret = []; |
190 | foreach ( $this->mLimitsShown as $key => $value ) { |
191 | # The pair is either $index => $limit, in which case the $value |
192 | # will be numeric, or $limit => $text, in which case the $value |
193 | # will be a string. |
194 | if ( is_int( $value ) ) { |
195 | $limit = $value; |
196 | $text = $this->getLanguage()->formatNum( $limit ); |
197 | } else { |
198 | $limit = $key; |
199 | $text = $value; |
200 | } |
201 | $ret[$text] = $limit; |
202 | } |
203 | return $ret; |
204 | } |
205 | |
206 | protected function isHeaderRowNeeded( string $date ): bool { |
207 | if ( !$this->lastHeaderTimestamp ) { |
208 | return true; |
209 | } |
210 | $month = $this->getMonthHeader( $date ); |
211 | $prevMonth = $this->getMonthHeader( $this->lastHeaderTimestamp ); |
212 | $year = $this->getYearFromTimestamp( $date ); |
213 | $prevYear = $this->getYearFromTimestamp( $this->lastHeaderTimestamp ); |
214 | return $month !== $prevMonth || $year !== $prevYear; |
215 | } |
216 | |
217 | /** |
218 | * @inheritDoc |
219 | */ |
220 | public function formatRow( $row ) { |
221 | $htmlRow = ( new Tag( 'li' ) ) |
222 | ->addClasses( [ 'ext-campaignevents-events-list-row' ] ); |
223 | $page = $this->getEventPageFromRow( $row ); |
224 | $pageUrlResolver = $this->pageURLResolver; |
225 | $timestampField = $this->getTimestampField(); |
226 | $timestamp = $row->$timestampField; |
227 | $htmlRow->appendContent( ( new Tag() ) |
228 | ->addClasses( [ 'ext-campaignevents-events-list-day' ] ) |
229 | ->appendContent( $this->getDayFromTimestamp( $timestamp ) ) ); |
230 | $detailContainer = ( new Tag() ) |
231 | ->addClasses( [ 'ext-campaignevents-events-list-details' ] ); |
232 | $eventPageLinkElement = ( new Tag( 'a' ) ) |
233 | ->setAttributes( [ |
234 | "href" => $pageUrlResolver->getUrl( $page ), |
235 | "class" => 'ext-campaignevents-events-list-link' |
236 | ] ) |
237 | ->appendContent( $row->event_name ); |
238 | $detailContainer->appendContent( |
239 | ( new Tag( 'h4' ) )->appendContent( $eventPageLinkElement ) |
240 | ); |
241 | $datesText = Html::element( |
242 | 'strong', |
243 | [], |
244 | $this->msg( |
245 | 'campaignevents-eventslist-date-separator', |
246 | $this->getLanguage()->userDate( $row->event_start_utc, $this->getUser() ), |
247 | $this->getLanguage()->userDate( $row->event_end_utc, $this->getUser() ) |
248 | )->text() |
249 | ); |
250 | $detailContainer->appendContent( new HtmlSnippet( Html::rawElement( 'div', [], $datesText ) ) ); |
251 | $detailContainer->appendContent( |
252 | new TextWithIconWidget( [ |
253 | 'icon' => 'mapPin', |
254 | 'content' => $this->msg( $this->getMeetingTypeMsg( $row ) )->text(), |
255 | 'label' => $this->msg( 'campaignevents-eventslist-meeting-type-label' )->text(), |
256 | 'icon_classes' => [ 'ext-campaignevents-events-list-icon' ], |
257 | ] ) |
258 | ); |
259 | if ( $this->eventWikis[$row->event_id] ) { |
260 | $detailContainer->appendContent( |
261 | $this->getWikiList( $row->event_id ) |
262 | ); |
263 | } |
264 | $detailContainer->appendContent( |
265 | new TextWithIconWidget( [ |
266 | 'icon' => 'userRights', |
267 | 'content' => $this->getOrganizersText( $row ), |
268 | 'label' => $this->msg( 'campaignevents-eventslist-organizer-label' )->text(), |
269 | 'icon_classes' => [ 'ext-campaignevents-events-list-icon' ], |
270 | 'classes' => [ 'ext-campaignevents-events-list-organizers' ], |
271 | ] ) |
272 | ); |
273 | return $htmlRow->appendContent( $detailContainer ); |
274 | } |
275 | |
276 | private function getOrganizersText( stdClass $row ): HtmlSnippet { |
277 | $eventID = (int)$row->event_id; |
278 | $organizersToShow = []; |
279 | $creator = $this->creators[$eventID]; |
280 | if ( $creator ) { |
281 | $organizersToShow[] = $creator; |
282 | } |
283 | foreach ( $this->extraOrganizers[$eventID] as $organizer ) { |
284 | if ( !$organizer->hasRole( Roles::ROLE_CREATOR ) ) { |
285 | $organizersToShow[] = $organizer; |
286 | } |
287 | if ( count( $organizersToShow ) === 2 ) { |
288 | break; |
289 | } |
290 | } |
291 | |
292 | $language = $this->getLanguage(); |
293 | $organizerLinks = array_map( |
294 | fn ( Organizer $organizer ) => $this->userLinker->generateUserLinkWithFallback( |
295 | $organizer->getUser(), |
296 | $language->getCode() |
297 | ), |
298 | $organizersToShow |
299 | ); |
300 | |
301 | $organizerCount = $this->organizerCounts[$eventID]; |
302 | if ( $organizerCount > 2 ) { |
303 | $organizerLinks[] = $this->getLinkRenderer()->makeKnownLink( |
304 | SpecialPage::getTitleFor( SpecialEventDetails::PAGE_NAME, (string)$eventID ), |
305 | $this->msg( 'campaignevents-eventslist-organizers-more' ) |
306 | ->numParams( $organizerCount - 2 ) |
307 | ->text() |
308 | ); |
309 | } |
310 | |
311 | return new HtmlSnippet( $language->listToText( $organizerLinks ) ); |
312 | } |
313 | |
314 | /** |
315 | * @inheritDoc |
316 | */ |
317 | public function getIndexField() { |
318 | return [ [ 'event_start_utc', 'event_id' ] ]; |
319 | } |
320 | |
321 | public function getNavigationBar(): string { |
322 | if ( !$this->isNavigationBarShown() ) { |
323 | return ''; |
324 | } |
325 | |
326 | if ( $this->mNavigationBar !== null ) { |
327 | return $this->mNavigationBar; |
328 | } |
329 | |
330 | $navBuilder = $this->getNavigationBuilder() |
331 | ->setPrevMsg( 'prevn' ) |
332 | ->setNextMsg( 'nextn' ) |
333 | ->setFirstMsg( 'page_first' ) |
334 | ->setLastMsg( 'page_last' ); |
335 | |
336 | $this->mNavigationBar = $navBuilder->getHtml(); |
337 | |
338 | return $this->mNavigationBar; |
339 | } |
340 | |
341 | /** |
342 | * @param stdClass $row |
343 | * @return string |
344 | */ |
345 | private function getMeetingTypeMsg( stdClass $row ): string { |
346 | $meetingType = EventStore::getMeetingTypeFromDBVal( $row->event_meeting_type ); |
347 | switch ( $meetingType ) { |
348 | case EventRegistration::MEETING_TYPE_IN_PERSON: |
349 | return 'campaignevents-eventslist-location-in-person'; |
350 | case EventRegistration::MEETING_TYPE_ONLINE: |
351 | return 'campaignevents-eventslist-location-online'; |
352 | case EventRegistration::MEETING_TYPE_ONLINE_AND_IN_PERSON: |
353 | return 'campaignevents-eventslist-location-online-and-in-person'; |
354 | default: |
355 | throw new UnexpectedValueException( "Unexpected meeting type $meetingType" ); |
356 | } |
357 | } |
358 | |
359 | /** |
360 | * @param string $timestamp |
361 | * @return string |
362 | */ |
363 | private function getYearFromTimestamp( string $timestamp ): string { |
364 | $timestamp = $this->offsetTimestamp( $timestamp ); |
365 | return $this->getLanguage()->sprintfDate( 'Y', $timestamp ); |
366 | } |
367 | |
368 | /** |
369 | * @param string $timestamp |
370 | * @return string |
371 | */ |
372 | private function getMonthHeader( string $timestamp ): string { |
373 | $timestamp = $this->offsetTimestamp( $timestamp ); |
374 | // TODO This is not guaranteed to return the month name in a format suitable for section headings (e.g., |
375 | // it may need to be capitalized). |
376 | return $this->getLanguage()->sprintfDate( 'F Y', $timestamp ); |
377 | } |
378 | |
379 | /** |
380 | * @param string $timestamp |
381 | * @return string |
382 | */ |
383 | private function getDayFromTimestamp( string $timestamp ): string { |
384 | $timestamp = $this->offsetTimestamp( $timestamp ); |
385 | return $this->getLanguage()->sprintfDate( 'j', $timestamp ); |
386 | } |
387 | |
388 | /** |
389 | * @param string $timestamp |
390 | * @return string |
391 | */ |
392 | private function offsetTimestamp( string $timestamp ): string { |
393 | $offset = $this->userOptionsLookup |
394 | ->getOption( $this->getUser(), 'timecorrection' ); |
395 | |
396 | return $this->getLanguage()->userAdjust( $timestamp, $offset ); |
397 | } |
398 | |
399 | /** |
400 | * @return array |
401 | */ |
402 | public function getSubqueryInfo(): array { |
403 | $query = $this->getDefaultSubqueryInfo(); |
404 | if ( $this->meetingType !== null ) { |
405 | $query['conds']['event_meeting_type'] = EventStore::meetingTypeToDBVal( $this->meetingType ); |
406 | } |
407 | if ( $this->filterWiki && $this->getConfig()->get( 'CampaignEventsEnableEventWikis' ) ) { |
408 | $query['tables'][] = 'ce_event_wikis'; |
409 | array_push( $query['fields'], 'ceew_wiki', 'ceew_event_id' ); |
410 | $query['join_conds']['ce_event_wikis'] = [ |
411 | 'JOIN', |
412 | [ |
413 | 'event_id=ceew_event_id', |
414 | 'ceew_wiki' => [ ...$this->filterWiki, EventWikisStore::ALL_WIKIS_DB_VALUE ] |
415 | ] |
416 | ]; |
417 | } |
418 | return $query; |
419 | } |
420 | |
421 | /** |
422 | * @param int|null|string $offset |
423 | * @param int $limit |
424 | * @param bool $order |
425 | * @return array |
426 | */ |
427 | public function buildQueryInfo( $offset, $limit, $order ): array { |
428 | [ $tables, $fields, $conds, $fname, $options, $join_conds ] = parent::buildQueryInfo( $offset, $limit, $order ); |
429 | // this is required to set the offsets correctly |
430 | $offsets = $this->getDateRangeCond( $this->startDate, $this->endDate ); |
431 | if ( $offsets ) { |
432 | [ $startOffset, $endOffset ] = $offsets; |
433 | |
434 | if ( $this->showOngoing ) { |
435 | if ( $startOffset ) { |
436 | $conds[] = $this->mDb->expr( 'event_end_utc', '>=', $startOffset ); |
437 | } |
438 | if ( $endOffset ) { |
439 | $conds[] = $this->mDb->expr( 'event_start_utc', '<=', $endOffset ); |
440 | } |
441 | } else { |
442 | if ( $startOffset ) { |
443 | $conds[] = $this->mDb->expr( 'event_start_utc', '>=', $startOffset ); |
444 | } |
445 | if ( $endOffset ) { |
446 | $conds[] = $this->mDb->expr( 'event_start_utc', '<=', $endOffset ); |
447 | } |
448 | } |
449 | } |
450 | return [ $tables, $fields, $conds, $fname, $options, $join_conds ]; |
451 | } |
452 | |
453 | /** |
454 | * @param string $startDate |
455 | * @param string $endDate |
456 | * @return array|null |
457 | */ |
458 | private function getDateRangeCond( string $startDate, string $endDate ): ?array { |
459 | try { |
460 | $startOffset = null; |
461 | if ( $startDate !== '' ) { |
462 | $startTimestamp = MWTimestamp::getInstance( $startDate ); |
463 | $startOffset = $this->mDb->timestamp( $startTimestamp->getTimestamp() ); |
464 | } |
465 | |
466 | if ( $endDate !== '' ) { |
467 | $endTimestamp = MWTimestamp::getInstance( $endDate ); |
468 | // Turned to use '<' for consistency with the parent class, |
469 | // add one second for compatibility with existing use cases |
470 | $endTimestamp->timestamp = $endTimestamp->timestamp->modify( '+1 second' ); |
471 | $this->endOffset = $this->mDb->timestamp( $endTimestamp->getTimestamp() ); |
472 | |
473 | // populate existing variables for compatibility with parent |
474 | $this->mYear = (int)$endTimestamp->format( 'Y' ); |
475 | $this->mMonth = (int)$endTimestamp->format( 'm' ); |
476 | $this->mDay = (int)$endTimestamp->format( 'd' ); |
477 | } |
478 | |
479 | return [ $startOffset, $this->endOffset ]; |
480 | } catch ( TimestampException $ex ) { |
481 | return null; |
482 | } |
483 | } |
484 | |
485 | private function getWikiList( string $eventID ): TextWithIconWidget { |
486 | $eventWikis = $this->eventWikis[(int)$eventID]; |
487 | |
488 | if ( $eventWikis === EventRegistration::ALL_WIKIS ) { |
489 | $wikiName = [ $this->msg( 'campaignevents-eventslist-all-wikis' )->text() ]; |
490 | return $this->getWikiListWidget( $eventID, $wikiName ); |
491 | } |
492 | $currentWikiId = WikiMap::getCurrentWikiId(); |
493 | $curWikiKey = array_search( $currentWikiId, $eventWikis, true ); |
494 | if ( $curWikiKey !== false ) { |
495 | unset( $eventWikis[$curWikiKey] ); |
496 | array_unshift( $eventWikis, $currentWikiId ); |
497 | } |
498 | return $this->getWikiListWidget( $eventID, $eventWikis ); |
499 | } |
500 | |
501 | /** |
502 | * @param string $eventID |
503 | * @param string[] $eventWikis |
504 | * @return TextWithIconWidget |
505 | * @throws Exception |
506 | */ |
507 | public function getWikiListWidget( string $eventID, array $eventWikis ): TextWithIconWidget { |
508 | $language = $this->getLanguage(); |
509 | $displayedWikiNames = $this->wikiLookup->getLocalizedNames( |
510 | array_slice( $eventWikis, 0, self::DISPLAYED_WIKI_COUNT ) |
511 | ); |
512 | $wikiCount = count( $eventWikis ); |
513 | $escapedWikiNames = []; |
514 | foreach ( $displayedWikiNames as $name ) { |
515 | $escapedWikiNames[] = htmlspecialchars( $name ); |
516 | } |
517 | |
518 | if ( $wikiCount > self::DISPLAYED_WIKI_COUNT ) { |
519 | $escapedWikiNames[] = $this->getLinkRenderer()->makeKnownLink( |
520 | SpecialPage::getTitleFor( SpecialEventDetails::PAGE_NAME, $eventID ), |
521 | $this->msg( 'campaignevents-eventslist-wikis-more' ) |
522 | ->numParams( $wikiCount - self::DISPLAYED_WIKI_COUNT ) |
523 | ->text() |
524 | ); |
525 | } |
526 | return new TextWithIconWidget( [ |
527 | 'icon' => $this->wikiLookup->getWikiIcon( $eventWikis ), |
528 | 'content' => new HtmlSnippet( $language->listToText( $escapedWikiNames ) ), |
529 | 'label' => $this->msg( 'campaignevents-eventslist-wiki-label' )->text(), |
530 | 'icon_classes' => [ 'ext-campaignevents-events-list-icon' ], |
531 | ] ); |
532 | } |
533 | } |