<?php
declare( strict_types=1 );
namespace MediaWiki\Extension\CampaignEvents\Event\Store;
use InvalidArgumentException;
use LogicException;
use MediaWiki\Extension\CampaignEvents\Database\CampaignsDatabaseHelper;
use MediaWiki\Extension\CampaignEvents\Event\EventRegistration;
use MediaWiki\Extension\CampaignEvents\Event\ExistingEventRegistration;
use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsPageFactory;
use MediaWiki\Extension\CampaignEvents\MWEntity\ICampaignsPage;
use MediaWiki\Extension\CampaignEvents\Utils;
use StatusValue;
use stdClass;
class EventStore implements IEventStore, IEventLookup {
private const EVENT_STATUS_MAP = [
EventRegistration::STATUS_OPEN => 1,
EventRegistration::STATUS_CLOSED => 2,
];
private const EVENT_TYPE_MAP = [
EventRegistration::TYPE_GENERIC => 'generic',
];
private const EVENT_MEETING_TYPE_MAP = [
EventRegistration::MEETING_TYPE_IN_PERSON => 1,
EventRegistration::MEETING_TYPE_ONLINE => 2,
];
/** @var CampaignsDatabaseHelper */
private $dbHelper;
/** @var CampaignsPageFactory */
private $campaignsPageFactory;
/**
* @param CampaignsDatabaseHelper $dbHelper
* @param CampaignsPageFactory $campaignsPageFactory
*/
public function __construct(
CampaignsDatabaseHelper $dbHelper,
CampaignsPageFactory $campaignsPageFactory
) {
$this->dbHelper = $dbHelper;
$this->campaignsPageFactory = $campaignsPageFactory;
}
/**
* @inheritDoc
*/
public function getEventByID( int $eventID ): ExistingEventRegistration {
$eventRow = $this->dbHelper->getDBConnection( DB_REPLICA )->selectRow(
'campaign_events',
'*',
[ 'event_id' => $eventID ]
);
if ( !$eventRow ) {
throw new EventNotFoundException( "Event $eventID not found" );
}
return $this->newEventFromDBRow( $eventRow );
}
/**
* @inheritDoc
*/
public function getEventByPage( ICampaignsPage $page ): ExistingEventRegistration {
$eventRow = $this->dbHelper->getDBConnection( DB_REPLICA )->selectRow(
'campaign_events',
'*',
[
'event_page_namespace' => $page->getNamespace(),
'event_page_title' => $page->getDBkey(),
'event_page_wiki' => Utils::getWikiIDString( $page->getWikiId() ),
]
);
if ( !$eventRow ) {
throw new EventNotFoundException(
"No event found for the given page (ns={$page->getNamespace()}, " .
"dbkey={$page->getDBkey()}, wiki={$page->getWikiId()})"
);
}
return $this->newEventFromDBRow( $eventRow );
}
/**
* @inheritDoc
*/
public function getEventsByOrganizer( int $organizerID, int $limit = null ): array {
$events = [];
$eventsRow = $this->dbHelper->getDBConnection( DB_REPLICA )->select(
[ 'campaign_events', 'ce_organizers' ],
'*',
[ 'ceo_user_id' => $organizerID ],
$limit !== null ? [ 'LIMIT' => $limit ] : [],
[
'ce_organizers' => [ 'INNER JOIN', [ 'event_id=ceo_event_id' ] ]
]
);
foreach ( $eventsRow as $row ) {
$events[] = $this->newEventFromDBRow( $row );
}
return $events;
}
/**
* @inheritDoc
*/
public function getEventsByParticipant( int $participantID, int $limit = null ): array {
$events = [];
$eventsRow = $this->dbHelper->getDBConnection( DB_REPLICA )->select(
[ 'campaign_events', 'ce_participants' ],
'*',
[
'cep_user_id' => $participantID,
'cep_unregistered_at' => null,
],
$limit !== null ? [ 'LIMIT' => $limit ] : [],
[
'ce_participants' => [ 'INNER JOIN', [ 'event_id=cep_event_id' ] ]
]
);
foreach ( $eventsRow as $row ) {
$events[] = $this->newEventFromDBRow( $row );
}
return $events;
}
/**
* @param stdClass $row
* @return ExistingEventRegistration
*/
private function newEventFromDBRow( stdClass $row ): ExistingEventRegistration {
$eventPage = $this->campaignsPageFactory->newPageFromDB(
(int)$row->event_page_namespace,
$row->event_page_title,
$row->event_page_prefixedtext,
$row->event_page_wiki
);
$dbMeetingType = (int)$row->event_meeting_type;
$meetingType = 0;
foreach ( self::EVENT_MEETING_TYPE_MAP as $eventVal => $dbVal ) {
if ( $dbMeetingType & $dbVal ) {
$meetingType |= $eventVal;
}
}
return new ExistingEventRegistration(
(int)$row->event_id,
$row->event_name,
$eventPage,
$row->event_chat_url !== '' ? $row->event_chat_url : null,
$row->event_tracking_tool !== '' ? $row->event_tracking_tool : null,
$row->event_tracking_url !== '' ? $row->event_tracking_url : null,
array_search( (int)$row->event_status, self::EVENT_STATUS_MAP, true ),
wfTimestamp( TS_UNIX, $row->event_start ),
wfTimestamp( TS_UNIX, $row->event_end ),
array_search( $row->event_type, self::EVENT_TYPE_MAP, true ),
$meetingType,
$row->event_meeting_url !== '' ? $row->event_meeting_url : null,
$row->event_meeting_country !== '' ? $row->event_meeting_country : null,
$row->event_meeting_address !== '' ? $row->event_meeting_address : null,
wfTimestamp( TS_UNIX, $row->event_created_at ),
wfTimestamp( TS_UNIX, $row->event_last_edit ),
wfTimestampOrNull( TS_UNIX, $row->event_deleted_at )
);
}
/**
* @inheritDoc
*/
public function saveRegistration( EventRegistration $event ): StatusValue {
$dbw = $this->dbHelper->getDBConnection( DB_PRIMARY );
$curDBTimestamp = $dbw->timestamp();
$meetingType = 0;
foreach ( self::EVENT_MEETING_TYPE_MAP as $eventVal => $dbVal ) {
if ( $event->getMeetingType() & $eventVal ) {
$meetingType |= $dbVal;
}
}
$curCreationTS = $event->getCreationTimestamp();
$curDeletionTS = $event->getDeletionTimestamp();
$newRow = [
'event_name' => $event->getName(),
'event_page_namespace' => $event->getPage()->getNamespace(),
'event_page_title' => $event->getPage()->getDBkey(),
'event_page_prefixedtext' => $event->getPage()->getPrefixedText(),
'event_page_wiki' => Utils::getWikiIDString( $event->getPage()->getWikiId() ),
'event_chat_url' => $event->getChatURL() ?? '',
'event_tracking_tool' => $event->getTrackingToolName() ?? '',
'event_tracking_url' => $event->getTrackingToolURL() ?? '',
'event_status' => self::EVENT_STATUS_MAP[$event->getStatus()],
'event_start' => $dbw->timestamp( $event->getStartTimestamp() ),
'event_end' => $dbw->timestamp( $event->getEndTimestamp() ),
'event_type' => self::EVENT_TYPE_MAP[$event->getType()],
'event_meeting_type' => $meetingType,
'event_meeting_url' => $event->getMeetingURL() ?: '',
'event_meeting_country' => $event->getMeetingCountry() ?: '',
'event_meeting_address' => $event->getMeetingAddress() ?: '',
'event_created_at' => $curCreationTS ? $dbw->timestamp( $curCreationTS ) : $curDBTimestamp,
'event_last_edit' => $curDBTimestamp,
'event_deleted_at' => $curDeletionTS ? $dbw->timestamp( $curDeletionTS ) : null,
];
$eventID = $event->getID();
if ( $eventID === null ) {
$dbw->insert( 'campaign_events', $newRow );
} else {
$dbw->update( 'campaign_events', $newRow, [ 'event_id' => $eventID ] );
}
return StatusValue::newGood( $event->getID() ?? $dbw->insertId() );
}
/**
* @inheritDoc
*/
public function deleteRegistration( ExistingEventRegistration $registration ): bool {
$dbw = $this->dbHelper->getDBConnection( DB_PRIMARY );
$dbw->update(
'campaign_events',
[ 'event_deleted_at' => $dbw->timestamp() ],
[
'event_id' => $registration->getID(),
'event_deleted_at' => null
]
);
return $dbw->affectedRows() > 0;
}
/**
* Converts a meeting type as stored in the DB into a combination of the EventRegistration::MEETING_TYPE_* constants
* @param string $dbMeetingType
* @return int
*/
public static function getMeetingTypeFromDBVal( string $dbMeetingType ): int {
$ret = 0;
$dbMeetingTypeNum = (int)$dbMeetingType;
foreach ( self::EVENT_MEETING_TYPE_MAP as $eventVal => $dbVal ) {
if ( $dbMeetingTypeNum & $dbVal ) {
$ret |= $eventVal;
}
}
return $ret;
}
/**
* Converts an EventRegistration::STATUS_* constant into the respective DB value.
* @param string $eventStatus
* @return int
*/
public static function getEventStatusDBVal( string $eventStatus ): int {
if ( isset( self::EVENT_STATUS_MAP[$eventStatus] ) ) {
return self::EVENT_STATUS_MAP[$eventStatus];
}
throw new LogicException( "Unknown status $eventStatus" );
}
/**
* Converts an event status as stored in the database to an EventRegistration::STATUS_* constant
* @param string $eventStatus
* @return string
*/
public static function getEventStatusFromDBVal( string $eventStatus ): string {
$val = array_search( (int)$eventStatus, self::EVENT_STATUS_MAP, true );
if ( $val === false ) {
throw new InvalidArgumentException( "Unknown event status: $eventStatus" );
}
return $val;
}
}