esc_url_raw(rest_url('groupentry/v1/')),'nonce'=>wp_create_nonce('wp_rest'),'mode'=>$mode,'admin'=>current_user_can('manage_options'),'staff'=>GroupEntry_API::staff_permission(),'code_enabled'=>$mode==='staff' && GroupEntry_API::staff_code_enabled(),'locale'=>str_replace('_','-',determine_locale())]; $config['color_defaults']=GroupEntry_Core::color_defaults(); if ($mode==='admin' && current_user_can('manage_options')) { $settings=GroupEntry_Core::settings(); $page=$settings['page_id'] ? get_post($settings['page_id']) : null; $config['setup']=[ 'needed'=>!$page || $page->post_type!=='page' || $page->post_status!=='publish' || !has_shortcode($page->post_content,'groupentry_registration'), 'create_page_url'=>esc_url_raw(admin_url('post-new.php?post_type=page')), ]; $headers=get_file_data(GROUPENTRY_FILE,['name'=>'Plugin Name','author'=>'Author','author_url'=>'Author URI','license'=>'License']); $config['info']=$headers + ['version'=>GROUPENTRY_VERSION]; $config['info']['author_url']=esc_url_raw($headers['author_url']); } if ($mode==='staff') { $config['login_url']=esc_url_raw(wp_login_url(get_permalink())); } wp_add_inline_script('groupentry','window.GroupEntry='.wp_json_encode($config,JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT).';','before'); } public static function registration() { self::assets('public'); return '

'.esc_html__('Loading registration…','groupentry').'

'; } public static function entrance() { self::assets('staff'); return '

'.esc_html__('Loading check-in…','groupentry').'

'; } public static function menu() { add_menu_page('GroupEntry','GroupEntry','groupentry_checkin','groupentry',[self::class,'admin_page'],'dashicons-tickets-alt',26); } public static function time_zone_options($settings) { // Group cities only when their known clock changes match for this event and the next ten years. $year=(int)substr($settings['date'],0,4); $from=(new DateTimeImmutable(sprintf('%04d-01-01',max(1,$year-1)),new DateTimeZone('UTC')))->getTimestamp(); $until=(new DateTimeImmutable(sprintf('%04d-01-01',min(9999,$year+11)),new DateTimeZone('UTC')))->getTimestamp(); $at=new DateTimeImmutable($settings['date'].' '.$settings['start'],GroupEntry_Core::timezone($settings)); // Reuse the city labels WordPress translates, without translating dynamic plugin strings. $city_labels=[]; preg_match_all('/]*value="([^"]*)"[^>]*>(.*?)<\/option>/s',wp_timezone_choice($settings['timezone'],get_user_locale()),$choices,PREG_SET_ORDER); foreach($choices as $choice) { $city_labels[html_entity_decode($choice[1],ENT_QUOTES,'UTF-8')]=html_entity_decode(wp_strip_all_tags($choice[2]),ENT_QUOTES,'UTF-8'); } $preferred=['Europe/Prague','Europe/Bratislava','Europe/Vienna','Europe/London','Europe/Lisbon','Europe/Dublin','Europe/Athens','Europe/Helsinki','Europe/Bucharest','America/New_York','America/Toronto','America/Los_Angeles','America/Vancouver','America/Chicago','America/Winnipeg','America/Denver','America/Edmonton','Asia/Tokyo','Asia/Seoul','Asia/Singapore','Asia/Shanghai','Australia/Sydney','Australia/Adelaide','Pacific/Auckland']; $rank=static function($zone) use ($preferred) { $index=array_search($zone,$preferred,true); return $index===false ? count($preferred) : $index; }; $zones=DateTimeZone::listIdentifiers(); if (!preg_match('/^[+-]\d{2}:\d{2}$/D',$settings['timezone']) && !in_array($settings['timezone'],$zones,true)) { $zones[]=$settings['timezone']; } usort($zones,static fn($a,$b)=>($rank($a)<=>$rank($b)) ?: strcmp($a,$b)); $groups=[]; foreach($zones as $name) { $zone=new DateTimeZone($name); $changes=$zone->getTransitions($from,$until); $signature=[]; $previous=null; if ($changes===false) { $signature[]=[0,$zone->getOffset($at)]; } else foreach($changes as $change) { if ($previous!==$change['offset']) { $signature[]=[count($signature) ? $change['ts'] : 0,$change['offset']]; $previous=$change['offset']; } } $key=wp_json_encode($signature); if (!isset($groups[$key])) { $groups[$key]=['value'=>$name,'offset'=>$zone->getOffset($at),'cities'=>[]]; } $parts=explode('/',$name); $city=implode(' — ',array_slice($parts,1)) ?: $name; $city=$city_labels[$name] ?? str_replace('_',' ',$city); if (count($groups[$key]['cities'])<3 && !in_array($city,$groups[$key]['cities'],true)) { $groups[$key]['cities'][]=$city; } if ($name===$settings['timezone']) { $groups[$key]['value']=$name; if (!in_array($city,$groups[$key]['cities'],true)) { if(count($groups[$key]['cities'])===3) array_pop($groups[$key]['cities']); $groups[$key]['cities'][]=$city; } } } if (preg_match('/^[+-]\d{2}:\d{2}$/D',$settings['timezone'])) { $groups['saved-fixed']=['value'=>$settings['timezone'],'offset'=>GroupEntry_Core::timezone($settings)->getOffset($at),'cities'=>[__('Saved fixed offset (no daylight saving)','groupentry')]]; } usort($groups,static fn($a,$b)=>($a['offset']<=>$b['offset']) ?: strcmp(implode(', ',$a['cities']),implode(', ',$b['cities']))); $html=''; foreach($groups as $group) { $html.=''; } return $html; } public static function admin_page() { if (!GroupEntry_API::staff_permission()) { wp_die(esc_html__('Access denied.','groupentry')); } self::assets('admin'); echo '

GroupEntry – Event Registration & Check-in

'; echo '

'.esc_html__('Loading…','groupentry').'

'; if (current_user_can('manage_options')) { echo ''; echo ''; echo ''; echo ''; } echo '
'; } public static function csv_cell($v) { $v=(string)$v; if (preg_match('/^[\s\x00-\x1F]*[=+\-@]/u',$v)) { $v="'".$v; } return $v; } public static function export() { if (!current_user_can('manage_options')) { wp_die(esc_html__('Access denied.','groupentry')); } check_admin_referer('groupentry_export'); global $wpdb; $r=GroupEntry_Core::table('registrations'); $p=GroupEntry_Core::table('participants'); $s=GroupEntry_Core::table('slots'); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Plugin-owned transaction state and live capacity require fresh database access. $rows=$wpdb->get_results($wpdb->prepare('SELECT r.*,p.first_name,p.last_name,p.age,s.starts_at,s.ends_at FROM %i r JOIN %i p ON p.registration_id=r.id JOIN %i s ON s.id=r.slot_id ORDER BY s.starts_at,r.contact_last,r.id,p.id',$r,$p,$s),ARRAY_A); nocache_headers(); header('Content-Type: text/csv; charset=UTF-8'); header('Content-Disposition: attachment; filename="groupentry-'.gmdate('Y-m-d').'.csv"'); $out=fopen('php://output','w'); echo "\xEF\xBB\xBF"; fputcsv($out,[__('Code','groupentry'),__('Date','groupentry'),__('Start','groupentry'),__('End','groupentry'),__('Contact first name','groupentry'),__('Contact last name','groupentry'),__('Email','groupentry'),__('Participant first name','groupentry'),__('Participant last name','groupentry'),__('Age','groupentry'),__('Status','groupentry'),__('Check-in time','groupentry'),__('Event time zone','groupentry'),__('Mail status','groupentry')],';','"',''); foreach($rows as $row) { $cells=[$row['code'],substr($row['starts_at'],0,10),substr($row['starts_at'],11,5),substr($row['ends_at'],11,5),$row['contact_first'],$row['contact_last'],$row['email'],$row['first_name'],$row['last_name'],$row['age'],$row['status'],$row['checked_at']?wp_date('Y-m-d H:i',strtotime($row['checked_at'].' UTC'),GroupEntry_Core::timezone()):'',GroupEntry_Core::timezone()->getName(),$row['mail_status']]; fputcsv($out,array_map([self::class,'csv_cell'],$cells),';','"',''); } exit; // PHP closes the output stream; no files are written to disk. } }