diff --git a/page-schedule.php b/page-schedule.php new file mode 100644 index 0000000..c8a4342 --- /dev/null +++ b/page-schedule.php @@ -0,0 +1,54 @@ + +
+

Програма

+ + + + + + + + + + + + +
 
+
+ +
+
+ + +
+
+
+ + + + diff --git a/schedule/class.SmartCurl.php b/schedule/class.SmartCurl.php new file mode 100644 index 0000000..b963a31 --- /dev/null +++ b/schedule/class.SmartCurl.php @@ -0,0 +1,137 @@ +cache_dir = __DIR__ . DIRECTORY_SEPARATOR . $cache_dir; + } + + if (!file_exists($this->cache_dir)) { + mkdir($this->cache_dir); + } + + $this->cache_index = $this->cache_dir . '.json'; + + $cache = file_exists($this->cache_index) ? file_get_contents($this->cache_index) : false; + + if ($cache !== false) { + $cache = json_decode($cache, true); + } + + if ($cache !== false) { + static::importEtags($cache); + } + + if (!is_null($url_root)) { + $this->url_root = $url_root; + } + + $ch = curl_init(); + + if ($ch === false) { + throw new Exception('curl init failed'); + } + + $this->ch = $ch; + + if (curl_setopt_array($ch, [ + CURLOPT_FAILONERROR => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + //CURLINFO_HEADER_OUT => true, + ]) === false) { + throw new Exception('curl setopt failed'); + } + } + + public function __destruct() { + curl_close($this->ch); + file_put_contents($this->cache_index, json_encode(static::exportEtags())); + } + + public static function importEtags(array $etags) { + static::$etags = array_merge(static::$etags, $etags); + } + + public static function exportEtags() { + return static::$etags; + } + + public function getUrl($filename) { + if (is_null($this->url_root)) { + $url = $filename; + } + else { + $url = $this->url_root . $filename; + } + + if (curl_setopt($this->ch, CURLOPT_URL, $url) === false) { + throw new Exception('set url failed'); + } + + $cache_file = $this->cache_dir . DIRECTORY_SEPARATOR . $filename; + + $etag = array_key_exists($url, static::$etags) && file_exists($cache_file) ? static::$etags[$url] : null; + + if (curl_setopt($this->ch, CURLOPT_HTTPHEADER, [ + 'If-None-Match:' . (is_null($etag) ? '' : ' ' . $etag), + ]) === false) { + throw new Exception('set etag failed'); + } + + $response = curl_exec($this->ch); + + if ($response === false) { + return false; + } + + //var_dump(curl_getinfo($this->ch, CURLINFO_HEADER_OUT)); + //var_dump($response); + + $header_size = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE); + $header = substr($response, 0, $header_size); + $headers = array_filter(explode("\r\n", $header)); + + foreach ($headers as $header_line) { + if (stripos($header_line, 'etag:') === 0) { + static::$etags[$url] = trim(substr($header_line, strlen('etag:'))); + } + } + + $http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE); + + if ($http_code === 304 && !is_null($this->url_root)) { + // use cache + + if (file_exists($cache_file)) { + return file_get_contents($cache_file); + } + else { + return false; + } + } + + if (strlen($response) === $header_size) { + return false; + } + + $body = substr($response, $header_size); + + if ($http_code === 200) { + $dirname = dirname($filename); + + if ($dirname !== '.') { + mkdir($this->cache_dir . DIRECTORY_SEPARATOR . $dirname, 0777, true); + } + + file_put_contents($cache_file, $body); + } + + return $body; + } +} diff --git a/schedule/index.php b/schedule/index.php new file mode 100644 index 0000000..32fc571 --- /dev/null +++ b/schedule/index.php @@ -0,0 +1,49 @@ + + +Test schedule + + + +
+
+
+ + + + + + + + + + + + +
 
+
+ 'events.json', + 'speakers' => 'speakers.json', + 'tracks' => 'tracks.json', + 'event_types' => 'event_types.json', + 'halls' => 'halls.json', + 'slots' => 'slots.json', +]; + +$data = []; + +foreach ($filenames as $name => $filename) { + $curl = new SmartCurl($base_url); + $json = $curl->getUrl($filename); + + if ($json === false) { + echo 'get failed: ', $filename, PHP_EOL; + exit; + } + + $decoded = json_decode($json, true); + + if ($decoded === false) { + echo 'decode failed: ', $filename, PHP_EOL; + exit; + } + + $add = true; + + switch ($name) { + case 'halls': + $decoded = array_map(function($el) { + return $el['name']; + }, $decoded); + break; + case 'slots': + $decoded = array_map(function($el) { + foreach (['starts_at', 'ends_at'] as $key) { + $el[$key] = strtotime($el[$key]); + } + + return $el; + }, $decoded); + break; + } + + $data[$name] = $decoded; +} + +function compareKeys($a, $b, $key) { + $valA = &$a[$key]; + $valB = &$b[$key]; + + return ($valA < $valB) ? -1 : (($valA > $valB) ? 1 : 0); +} + +uasort($data['slots'], function($a, $b) { + return compareKeys($a, $b, 'starts_at') ?: compareKeys($a, $b, 'hall_id'); +}); + +array_pop($data['halls']); + +return $data; diff --git a/schedule/parse.php b/schedule/parse.php new file mode 100644 index 0000000..4ef5deb --- /dev/null +++ b/schedule/parse.php @@ -0,0 +1,147 @@ + $slot) { + $slotTime = $slot['starts_at']; + $slotDate = date('d', $slotTime); + + if ($slotDate !== $date) { + $lines[] = ''; + $lines[] = '' . date('d F - l', $slotTime) . ''; + $lines[] = ' '; + $lines[] = ''; + + $date = $slotDate; + } + + if ($slotTime !== $time) { + if ($time !== 0) { + $lines[] = ''; + } + + $lines[] = ''; + $lines[] = '' . date('H:i', $slot['starts_at']) . ' - ' . date('H:i', $slot['ends_at']) . ''; + + $time = $slotTime; + } + + $eid = &$slot['event_id']; + $event = &$data['events'][$eid]; + + if (is_null($eid)) { + $lines[] = 'TBA'; + } + else { + $title = mb_substr($event['title'], 0, $cut_len) . (strlen($event['title']) > $cut_len ? '...' : ''); + $speakers = ''; + + if (count($event['participant_user_ids']) > 0) { + $speakers = json_encode($event['participant_user_ids']) . '
'; + + $spk = array(); + $speaker_name = array(); + foreach ($event['participant_user_ids'] as $uid) { + /* The check for uid==4 is for us not to show the "Opefest Team" as a presenter for lunches, etc. */ + if ($uid == 4 || empty ($data['speakers'][$uid])) { + continue; + } else { + /* TODO: fix the URL */ + $name = $data['speakers'][$uid]['first_name'] . ' ' . $data['speakers'][$uid]['last_name']; + $spk[$uid] = '' . $name . ''; + } + } + $speakers = implode (', ', $spk); + } + + + /* Hack, we don't want language for the misc track. This is the same for all years. */ + if ('misc' !== $data['tracks'][$event['track_id']]['name']['en']) { + $csslang = "schedule-".$event['language']; + } else { + $csslang = ""; + } + $cssclass = &$data['tracks'][$event['track_id']]['css_class']; + $style = ' class="' . $cssclass . ' ' . $csslang . '"'; + $content = '' . htmlspecialchars($title) . '
' . $speakers; + + + /* these are done by $eid, as otherwise we get some talks more than once (for example the lunch) */ + $fulltalks[$eid] = ''; + $fulltalks[$eid] .= '
'; + /* We don't want '()' when we don't have a speaker name */ + $fulltalk_spkr = strlen($speakers)>1 ? ' (' . $speakers . ')' : ''; + $fulltalks[$eid] .= '

' . $event['title'] . ' ' . $fulltalk_spkr . ''; + $fulltalks[$eid] .= '

' . $event['abstract'] . '

'; + $fulltalks[$eid] .= '
'; + + if ($slot['event_id'] === $prev_event_id) { + array_pop($lines); + $lines[] = '' . $content . ''; + } + else { + $lines[] = '' . $content . ''; + $colspan = 1; + } + } + + $prev_event_id = $slot['event_id']; +} + +$lines[] = ''; + +$gspk = []; +$fspk = []; +$types = []; +$types['twitter']['url']='https://twitter.com/'; +$types['twitter']['class']='fa fa-twitter'; +$types['github']['url']='https://github.com/'; +$types['github']['class']='fa fa-github'; +$types['email']['url']='mailto:'; +$types['email']['class']='fa fa-envelope'; + +$gspk[] = '
'; + +foreach ($data['speakers'] as $speaker) { + $name = $speaker['first_name'] . ' ' . $speaker['last_name']; + + $gspk[] = ''; + + $fspk[] = '
'; + $fspk[] = '' . $name .''; + $fspk[] = '

' . $name . '

'; + $fspk[] = '
'; + foreach ($types as $type => $parm) { + if (!empty($speaker[$type])) { + $fspk[] = ''; + } + } + $fspk[] = '

' . $speaker['biography'] . '

'; + $fspk[] = '
'; +} + +$gspk[] = '
'; + +return array_merge($data, compact('lines', 'fulltalks', 'gspk', 'fspk'));