*/ class ParTCP_Badges { public $fileSystem; public $partcp; // ParTCP object for sending messages public $baseDir; public function __construct( $fileSystem, &$partcp ){ $this->fileSystem = $fileSystem; $this->partcp = $partcp; } public function set_base_dir( $baseDir ){ $this->baseDir = trim( $baseDir, '/' ); } public function get_badge_data( $code, $includeSession = FALSE ){ if ( $code == '00000000' ){ return -1; } $badgeDir = "{$this->baseDir}/badges/codes/{$code}"; if ( $this->fileSystem->exists( "{$badgeDir}/badge-invalidation" ) ){ return -2; // badge has been invalidated } $contents = $this->fileSystem->get_contents( "{$badgeDir}/badge-assignment" ); if ( ! $contents ){ return -1; // badge does not exist } $msg = yaml_parse( $contents ); $data = $msg['Badge-Data']; if ( $includeSession ){ $data['current_session'] = ''; $dir = "{$this->baseDir}/badges/codes/{$code}/sessions"; $contents = $this->fileSystem->get_recent_contents( $dir, '????????-??????-{in,out}-*' ); if ( $contents ){ $msg = yaml_parse( $contents ); $data['current_session'] = $msg['Current-Session'] ?? ''; } } return $data; } public function attr2category( $ptcpData, $eventData ){ if ( empty( $eventData['badge_categories'] ) ){ return FALSE; } foreach ( $eventData['badge_categories'] as $category => $data ){ $flagsAreSet = TRUE; foreach ( $data['mapping'] as $flag ){ if ( empty( $ptcpData['attributes'][ $flag ] ) ){ $flagsAreSet = FALSE; break; } } if ( $flagsAreSet ){ return [ 'id' => $category, 'name' => $data['display_name'], 'color' => $data['color'] ]; } } return FALSE; } public function get_holder_badge_data( $ptcpId ){ $dir = "{$this->baseDir}/badges/holders/{$ptcpId}"; $contents = $this->fileSystem->get_recent_contents( $dir ); if ( ! $contents ){ return FALSE; } $msg = yaml_parse( $contents ); return $msg['Badge-Data'] ?? FALSE; } public function get_holder_badge_code( $ptcpId ){ $dir = "{$this->baseDir}/badges/holders/{$ptcpId}"; $contents = $this->fileSystem->get_recent_contents( $dir ); if ( ! $contents ){ return FALSE; } $msg = yaml_parse( $contents ); return $msg['Badge-Data']['code'] ?? FALSE; } public function get_holder_accreditation_time( $ptcpId ){ $dir = "{$this->baseDir}/badges/holders/{$ptcpId}"; $listing = $this->fileSystem->get_listing( $dir, '*-badge-assignment' ); if ( ! $listing ){ return FALSE; } $dt = $listing[0]; $dateString = substr( $dt, 0, 4 ) . '-' . substr( $dt, 4, 2 ) . '-' . substr( $dt, 6, 2 ) . ' ' . substr( $dt, 9, 2 ) . ':' . substr( $dt, 11, 2 ) . ':' . substr( $dt, 13, 2 ); return strtotime( $dateString ); } public function assign_badge( $code, $ptcpId ){ $badgeDir = "{$this->baseDir}/badges/codes/{$code}"; $holderDir = "{$this->baseDir}/badges/holders/{$ptcpId}"; return [ "{$badgeDir}/badge-assignment", "{$holderDir}/%TS%-badge-assignment" ]; } public function invalidate_badge( $code, $ptcpId = NULL ){ if ( is_null( $ptcpId ) ){ $data = $this->get_badge_data( $code ); $ptcpId = $data['participant_id'] ?? NULL; } $result[] = "{$this->baseDir}/badges/codes/{$code}/badge-invalidation"; if ( $ptcpId ){ $result[] = "{$this->baseDir}/badges/holders/{$ptcpId}" . '/%TS%-badge-invalidation'; } return $result; } public function define_session( $id ){ $sessionDir = "{$this->baseDir}/sessions/{$id}"; if ( $this->fileSystem->exists( $sessionDir ) ){ return -1; // session with same ID is already defined } $this->fileSystem->mkdir( $sessionDir ); return [ "{$sessionDir}/%TS%-session-definition" ]; } public function get_session_data( $id ){ $sessionDir = "{$this->baseDir}/sessions/{$id}"; $contents = $this->fileSystem->get_recent_contents( $sessionDir ); if ( ! $contents ){ return [ 'id' => $id ]; } $msg = yaml_parse( $contents ); return $msg['Session-Data']; } public function get_current_session( $badgeCode ){ $dir = "{$this->baseDir}/badges/codes/{$badgeCode}/sessions"; $contents = $this->fileSystem->get_recent_contents( $dir, '????????-??????-{in,out}-*' ); if ( ! $contents ){ return ''; } $msg = yaml_parse( $contents ); return $msg['Current-Session'] ?? ''; } public function capture_badge( $code, $sessionId, $direction, $category ){ if ( ! in_array( $direction, [ 'in', 'out', 'conflict' ] ) ){ return FALSE; // invalid direction } if ( $direction != 'conflict' ){ $result[] = "{$this->baseDir}/badges/codes/{$code}/sessions" . "/%TS%-{$direction}-{$sessionId}"; } $result[] = "{$this->baseDir}/badges/sessions/{$sessionId}" . "/%TS%-{$direction}-{$category}"; return $result; } public function count_holders( $categories, $considerExpiration = FALSE ){ $dir = "{$this->baseDir}/badges/holders"; $result['total'] = 0; $result['voting_members'] = 0; $holderList = $this->fileSystem->get_listing( $dir ); $votingCategories = []; foreach ( $categories as $catId => $catData ){ $result[ $catId ] = 0; if ( ! empty( $catData['voting_member'] ) ){ $votingCategories[] = $catId; } } foreach ( $holderList as $holder ){ $badgeData = $this->get_holder_badge_data( $holder ); if ( ! $badgeData || ( $considerExpiration && substr( $badgeData['issued_on'], 0, 10 ) != date('Y-m-d') ) || empty( $badgeData['category'] ) || ! isset( $categories[ $badgeData['category'] ] ) ){ continue; } $result[ $badgeData['category'] ]++; $result['total']++; if ( in_array( $badgeData['category'], $votingCategories ) ){ $result['voting_members']++; } } return $result; } public function count_holders_by_attribute( $attrib, $pattern = NULL, $categories = NULL, $considerExpiration = NULL ){ $baseDir = "{$this->baseDir}/badges/holders"; $holderList = $this->fileSystem->get_listing( $baseDir ); $result = []; foreach ( $holderList as $holder ){ $badgeData = $this->get_holder_badge_data( $holder ); if ( ! $badgeData || ( $considerExpiration && substr( $badgeData['issued_on'], 0, 10 ) != date('Y-m-d') ) || ( $categories && ( empty( $badgeData['category'] ) || ! in_array( $badgeData['category'], $categories ) ) ) ){ continue; } $ptcpData = ptcp_get_id_data( $badgeData['participant_id'] ); $value = $ptcpData['attributes'][ $attrib ] ?? NULL; if ( ! $value ){ $key = '~'; } elseif ( $pattern ){ preg_match( $pattern, $value, $matches ); $key = $matches[1] ?? $matches[0] ?? (string) $value; } else { $key = (string) $value; } $result[ $key ] = 1 + ( $result[ $key ] ?? 0 ); } ksort( $result ); $result['total'] = array_sum( $result ); return $result; } public function count_attendees( $sessionId, $categories, $deadline = NULL ){ $baseDir = "{$this->baseDir}/badges/sessions/$sessionId"; if ( $deadline ){ foreach ( $categories as $catId => $catData ){ $result['categories'][ $catId ] = 0; } $timeStr = date( 'Ymd-His', $deadline ); $notes = $this->fileSystem->get_listing( $baseDir ); foreach ( $notes as $note ){ if ( substr( $note, 0, 15 ) > $timeStr ){ continue; } if ( substr( $note, 16, 2 ) == 'in' ){ $catId = substr( $note, 19 ); $result['categories'][ $catId ]++; } elseif ( substr( $note, 16, 3 ) == 'out' ){ $catId = substr( $note, 20 ); $result['categories'][ $catId ]--; } } } else { foreach ( $categories as $catId => $catData ){ $in = $this->fileSystem->count_items( $baseDir, "*-in-{$catId}" ); $out = $this->fileSystem->count_items( $baseDir, "*-out-{$catId}" ); $result['categories'][ $catId ] = max( $in - $out, 0 ); } } $result['total'] = 0; $result['voting_members'] = 0; foreach ( $categories as $catId => $catData ){ $result['total'] += $result['categories'][ $catId ]; if ( ! empty( $catData['voting_member'] ) ){ $result['voting_members'] += $result['categories'][ $catId ]; } } return $result; } public function block_participant( $ptcpId, $mutexEvent ){ $mutexPtcpId = $this->local_id_to_mutex_id( $ptcpId, $mutexEvent ); if ( ! $mutexPtcpId ){ return -1; } $this->partcp->set_remote_id( $mutexEvent['issue_server'] ); $this->lastResponse = $this->partcp->send_message([ 'Message-Type' => 'issue-service-block-order', 'Event-Server' => $mutexEvent['event_server'], 'Event-Id' => $mutexEvent['event_id'], 'Participant-Id' => $mutexPtcpId ]); if ( ! $this->lastResponse ){ return -2; } return 1; } public function unblock_participant( $ptcpId, $mutexEvent ){ $mutexPtcpId = $this->local_id_to_mutex_id( $ptcpId, $mutexEvent ); if ( ! $mutexPtcpId ){ return -1; } $this->partcp->set_remote_id( $mutexEvent['issue_server'] ); $this->lastResponse = $this->partcp->send_message([ 'Message-Type' => 'issue-service-unblock-order', 'Event-Server' => $mutexEvent['event_server'], 'Event-Id' => $mutexEvent['event_id'], 'Participant-Id' => $mutexPtcpId ]); if ( ! $this->lastResponse ){ return -2; } return 1; } public function local_id_to_mutex_id( $ptcpId, $mutexEvent ){ $regex = $mutexEvent['ptcp_regex'] ?? NULL; $template = $mutexEvent['ptcp_template'] ?? NULL; if ( ! $regex || ! $template || ! preg_match( $regex, $ptcpId, $matches ) ){ return FALSE; } return sprintf( $template, $matches[1] ); } } // end of file badges.class.php