Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 93 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
EventsTablePager | |
0.00% |
0 / 93 |
|
0.00% |
0 / 11 |
812 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
formatValue | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
132 | |||
getFieldNames | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getIndexField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDefaultSort | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isFieldSortable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTableClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCellAttrs | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
getModuleStyles | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getModules | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSubqueryInfo | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | |
3 | declare( strict_types=1 ); |
4 | |
5 | namespace MediaWiki\Extension\CampaignEvents\Pager; |
6 | |
7 | use LogicException; |
8 | use MediaWiki\Cache\LinkBatchFactory; |
9 | use MediaWiki\Context\IContextSource; |
10 | use MediaWiki\DAO\WikiAwareEntity; |
11 | use MediaWiki\Extension\CampaignEvents\Database\CampaignsDatabaseHelper; |
12 | use MediaWiki\Extension\CampaignEvents\Event\EventRegistration; |
13 | use MediaWiki\Extension\CampaignEvents\Event\Store\EventStore; |
14 | use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsPageFactory; |
15 | use MediaWiki\Extension\CampaignEvents\MWEntity\CentralUser; |
16 | use MediaWiki\Extension\CampaignEvents\MWEntity\PageURLResolver; |
17 | use MediaWiki\Extension\CampaignEvents\Special\SpecialEditEventRegistration; |
18 | use MediaWiki\Extension\CampaignEvents\Special\SpecialEventDetails; |
19 | use MediaWiki\Linker\LinkRenderer; |
20 | use MediaWiki\Pager\TablePager; |
21 | use MediaWiki\SpecialPage\SpecialPage; |
22 | use OOUI\ButtonWidget; |
23 | |
24 | /** |
25 | * This pager can be used to display a list of events organized by the given user, formatted as a table. In the future, |
26 | * this might be expanded to allow listing all events, not just those of a single user. |
27 | */ |
28 | class EventsTablePager extends TablePager { |
29 | use EventPagerTrait { |
30 | EventPagerTrait::getSubqueryInfo as getDefaultSubqueryInfo; |
31 | } |
32 | |
33 | public const STATUS_ANY = 'any'; |
34 | public const STATUS_OPEN = 'open'; |
35 | public const STATUS_CLOSED = 'closed'; |
36 | |
37 | private const SORT_INDEXES = [ |
38 | 'event_start_utc' => [ 'event_start_utc', 'event_name', 'event_id' ], |
39 | 'event_name' => [ 'event_name', 'event_start_utc', 'event_id' ], |
40 | 'num_participants' => [ 'num_participants', 'event_start_utc', 'event_id' ], |
41 | ]; |
42 | |
43 | private CampaignsPageFactory $campaignsPageFactory; |
44 | private PageURLResolver $pageURLResolver; |
45 | private LinkBatchFactory $linkBatchFactory; |
46 | |
47 | private CentralUser $centralUser; |
48 | |
49 | private string $search; |
50 | private string $status; |
51 | |
52 | /** |
53 | * @param IContextSource $context |
54 | * @param LinkRenderer $linkRenderer |
55 | * @param CampaignsDatabaseHelper $databaseHelper |
56 | * @param CampaignsPageFactory $campaignsPageFactory |
57 | * @param PageURLResolver $pageURLResolver |
58 | * @param LinkBatchFactory $linkBatchFactory |
59 | * @param string $search |
60 | * @param string $status One of the self::STATUS_* constants |
61 | * @param CentralUser $user |
62 | */ |
63 | public function __construct( |
64 | IContextSource $context, |
65 | LinkRenderer $linkRenderer, |
66 | CampaignsDatabaseHelper $databaseHelper, |
67 | CampaignsPageFactory $campaignsPageFactory, |
68 | PageURLResolver $pageURLResolver, |
69 | LinkBatchFactory $linkBatchFactory, |
70 | string $search, |
71 | string $status, |
72 | CentralUser $user |
73 | ) { |
74 | // Set the database before calling the parent constructor, otherwise it'll use the local one. |
75 | $this->mDb = $databaseHelper->getDBConnection( DB_REPLICA ); |
76 | parent::__construct( $context, $linkRenderer ); |
77 | $this->campaignsPageFactory = $campaignsPageFactory; |
78 | $this->pageURLResolver = $pageURLResolver; |
79 | $this->linkBatchFactory = $linkBatchFactory; |
80 | $this->centralUser = $user; |
81 | $this->search = $search; |
82 | $this->status = $status; |
83 | } |
84 | |
85 | /** |
86 | * @inheritDoc |
87 | */ |
88 | public function formatValue( $name, $value ): string { |
89 | switch ( $name ) { |
90 | case 'event_start_utc': |
91 | return htmlspecialchars( $this->getLanguage()->userDate( $value, $this->getUser() ) ); |
92 | case 'event_name': |
93 | return $this->getLinkRenderer()->makeKnownLink( |
94 | SpecialPage::getTitleFor( SpecialEventDetails::PAGE_NAME, $this->mCurrentRow->event_id ), |
95 | $value, |
96 | [ 'class' => 'ext-campaignevents-events-table-eventpage-link' ] |
97 | ); |
98 | case 'event_location': |
99 | $meetingType = EventStore::getMeetingTypeFromDBVal( $this->mCurrentRow->event_meeting_type ); |
100 | if ( $meetingType === EventRegistration::MEETING_TYPE_ONLINE ) { |
101 | $msgKey = 'campaignevents-eventslist-location-online'; |
102 | } elseif ( $meetingType === EventRegistration::MEETING_TYPE_IN_PERSON ) { |
103 | $msgKey = 'campaignevents-eventslist-location-in-person'; |
104 | } elseif ( $meetingType === EventRegistration::MEETING_TYPE_ONLINE_AND_IN_PERSON ) { |
105 | $msgKey = 'campaignevents-eventslist-location-online-and-in-person'; |
106 | } else { |
107 | throw new LogicException( "Unexpected meeting type: $meetingType" ); |
108 | } |
109 | return $this->msg( $msgKey )->escaped(); |
110 | case 'num_participants': |
111 | return htmlspecialchars( $this->getLanguage()->formatNum( $value ) ); |
112 | case 'manage_event': |
113 | $eventID = $this->mCurrentRow->event_id; |
114 | $btnLabel = $this->msg( 'campaignevents-eventslist-manage-btn-info' )->text(); |
115 | // This will be replaced with a ButtonMenuSelectWidget in JS. |
116 | $btn = new ButtonWidget( [ |
117 | 'framed' => false, |
118 | 'label' => $btnLabel, |
119 | 'title' => $btnLabel, |
120 | 'invisibleLabel' => true, |
121 | 'icon' => 'ellipsis', |
122 | 'href' => SpecialPage::getTitleFor( |
123 | SpecialEditEventRegistration::PAGE_NAME, |
124 | $eventID |
125 | )->getLocalURL(), |
126 | 'classes' => [ 'ext-campaignevents-eventspager-manage-btn' ], |
127 | ] ); |
128 | $eventStatus = EventStore::getEventStatusFromDBVal( $this->mCurrentRow->event_status ); |
129 | $eventPage = $this->getEventPageFromRow( $this->mCurrentRow ); |
130 | $btn->setAttributes( [ |
131 | 'data-mw-event-id' => $eventID, |
132 | 'data-mw-event-name' => $this->mCurrentRow->event_name, |
133 | 'data-mw-is-closed' => $eventStatus === EventRegistration::STATUS_CLOSED ? 1 : 0, |
134 | 'data-mw-event-page-url' => $this->pageURLResolver->getUrl( $eventPage ), |
135 | 'data-mw-label' => $btnLabel, |
136 | 'data-mw-is-local-wiki' => $eventPage->getWikiId() === WikiAwareEntity::LOCAL, |
137 | ] ); |
138 | return $btn->toString(); |
139 | default: |
140 | throw new LogicException( "Unexpected name $name" ); |
141 | } |
142 | } |
143 | |
144 | /** |
145 | * @inheritDoc |
146 | */ |
147 | protected function getFieldNames(): array { |
148 | return [ |
149 | 'event_start_utc' => $this->msg( 'campaignevents-eventslist-column-date' )->text(), |
150 | 'event_name' => $this->msg( 'campaignevents-eventslist-column-name' )->text(), |
151 | 'event_location' => $this->msg( 'campaignevents-eventslist-column-location' )->text(), |
152 | 'num_participants' => $this->msg( 'campaignevents-eventslist-column-participants-number' )->text(), |
153 | 'manage_event' => '' |
154 | ]; |
155 | } |
156 | |
157 | /** |
158 | * Overridden to provide additional columns to order by, since most columns are not unique. |
159 | * @inheritDoc |
160 | */ |
161 | public function getIndexField(): array { |
162 | // XXX Work around T308697: TablePager and IndexPager seem to be incompatible and the correct |
163 | // index is not chosen automatically. |
164 | return [ self::SORT_INDEXES[$this->mSort] ]; |
165 | } |
166 | |
167 | /** |
168 | * @inheritDoc |
169 | */ |
170 | public function getDefaultSort(): string { |
171 | return 'event_start_utc'; |
172 | } |
173 | |
174 | /** |
175 | * @inheritDoc |
176 | */ |
177 | protected function isFieldSortable( $field ): bool { |
178 | return array_key_exists( $field, self::SORT_INDEXES ); |
179 | } |
180 | |
181 | /** |
182 | * @inheritDoc |
183 | */ |
184 | protected function getTableClass() { |
185 | return parent::getTableClass() . ' ext-campaignevents-events-table'; |
186 | } |
187 | |
188 | /** |
189 | * @inheritDoc |
190 | */ |
191 | protected function getCellAttrs( $field, $value ) { |
192 | $ret = parent::getCellAttrs( $field, $value ); |
193 | $addClass = null; |
194 | if ( $field === 'manage_event' ) { |
195 | $addClass = 'ext-campaignevents-events-table-cell-manage'; |
196 | } |
197 | if ( $addClass ) { |
198 | $ret['class'] = isset( $ret['class'] ) ? $ret['class'] . " $addClass" : $addClass; |
199 | } |
200 | return $ret; |
201 | } |
202 | |
203 | /** |
204 | * @inheritDoc |
205 | */ |
206 | public function getModuleStyles() { |
207 | return array_merge( |
208 | parent::getModuleStyles(), |
209 | [ |
210 | 'ext.campaignEvents.specialPages.styles', |
211 | 'oojs-ui.styles.icons-interactions' |
212 | ] |
213 | ); |
214 | } |
215 | |
216 | /** |
217 | * @return string[] An array of (non-style) RL modules. |
218 | */ |
219 | public function getModules(): array { |
220 | return [ 'ext.campaignEvents.specialPages' ]; |
221 | } |
222 | |
223 | /** |
224 | * @inheritDoc |
225 | */ |
226 | public function getSubqueryInfo(): array { |
227 | $query = $this->getDefaultSubqueryInfo(); |
228 | switch ( $this->status ) { |
229 | case self::STATUS_ANY: |
230 | break; |
231 | case self::STATUS_OPEN: |
232 | $query['conds']['event_status'] = EventStore::getEventStatusDBVal( EventRegistration::STATUS_OPEN ); |
233 | break; |
234 | case self::STATUS_CLOSED: |
235 | $query['conds']['event_status'] = EventStore::getEventStatusDBVal( EventRegistration::STATUS_CLOSED ); |
236 | break; |
237 | default: |
238 | // Invalid statuses can only be entered by messing with the HTML or query params, ignore. |
239 | } |
240 | // This should be abstracted at a later date. |
241 | // the current implementation ties presentation and data retrieval too closely |
242 | $query['join_conds']['ce_organizers'][1]['ceo_user_id'] = $this->centralUser->getCentralID(); |
243 | return $query; |
244 | } |
245 | } |