Commit bdef2f85 authored by Simon Welsh's avatar Simon Welsh

XHP the things!

git-svn-id: svn+ssh://lsgn/var/logbot-www@45 5f99df0c-6b0a-442a-b40a-040688e84a9d
parent 23073d5c
<?php
class :log:calendar extends :x:element {
attribute
int year @required,
string month @required,
array days @required;
protected function render() {
$year = $this->getAttribute('year');
$month = $this->getAttribute('month');
$days = $this->getAttribute('days');
$stamp = strtotime($days[array_keys($days)[0]][1]);
$stamp = mktime(0, 0, 0, date('n', $stamp), 1, date('Y', $stamp));
if($stamp == mktime(0,0,0,date('n'), 1, date('Y'))) {
$bold = date('j');
} else {
$bold = 0;
}
$body = <tbody>
</tbody>;
$row = <tr />;
$count = date('t', $stamp);
$start = date('w', $stamp) % 7;
$i = 0;
while($i < $start) {
$row->appendChild(<td class="empty" />);
++$i;
}
$day = 1;
while($day <= $count) {
$evenodd = $i % 2 ? 'odd' : 'even';
if(isset($days[$day])) {
$link = "index.php?date={$days[$day][1]}";
$content = <a href={$link} />;
} else {
$content = <x:frag />;
}
if($bold == $day) {
$content->appendChild(<b>{$day}</b>);
} else {
$content->appendChild($day);
}
$row->appendChild(<td class={$evenodd}>{$content}</td>);
if($i % 7 == 6 && $day < $count) {
$body->appendChild($row);
$row = <tr />;
}
++$day;
++$i;
}
while($i % 7) {
$row->appendChild(<td class="empty" />);
++$i;
}
$body->appendChild($row);
return <table class="cal">
<thead>
<tr>
<th>S</th>
<th>M</th>
<th>T</th>
<th>W</th>
<th>T</th>
<th>F</th>
<th>S</th>
</tr>
</thead>
{$body}
</table>;
}
}
<p>
These logs were automatically created by <b><?= $nick ?></b> on
<a href="irc://<?= $server . "/" . substr($channel, 1) ?>"><?= $server ?></a>.
</p>
</body>
</html>
<?php
class :log:foot extends :x:element {
attribute
string nick = 'ss-log',
string channel = '#silverstripe',
string server = 'irc.freenode.net';
protected function render() {
return <p>
These logs were automatically created by <b>{$this->getAttribute('nick')}</b> on <a href="irc://{$this->getAttribute('server')}/{substr($this->getAttribute('channel'), 1)}">{$this->getAttribute('server')}</a>.
</p>;
}
}
<?php
class :log:search extends :x:element {
protected function render() {
$val = isset($_GET['q']) ? $_GET['q'] : '';
return <form id="searchform" method="get" action="search.php">
<input type="text" class="text" name="q" value={$val} />
<input type="submit" class="action" value="L" />
</form>;
}
}
function search_form($val = false) {
if($val) {
$val = htmlentities($val);
} else {
$val = '';
}
$form = sprintf('<form id="searchform" method="get" action="search.php"><input type="text" class="text" name="q" value="%s" /><input type="submit" class="action" value="L" /></form>', $val);
return $form;
}
<?php
require_once 'xhp/init.php';
require_once 'calendar.php';
require_once 'foot.php';
require_once 'form.php';
require_once 'form.php';
require_once 'head.php';
require_once 'info.php';
require_once 'log.php';
require_once 'logs.php';
require_once 'month.php';
require_once 'year.php';
require_once 'years.php';
final class DB {
private $con;
private $inst;
......@@ -42,109 +55,6 @@ final class DB {
}
}
function search_form($val = false) {
if($val) {
$val = htmlentities($val);
} else {
$val = '';
}
$form = sprintf('<form id="searchform" method="get" action="search.php"><input type="text" class="text" name="q" value="%s" /><input type="submit" class="action" value="L" /></form>', $val);
return $form;
}
function handle_log($id, $log, $format = ['H:i:s', 'H:i']) {
$logTime = strtotime($log['Time']);
$index = 'log_' . $log['ID'];
$day = date('Y-m-d', $logTime);
$html = '<tr id="' . $index . '">';
$html .= '<td class="irc-linenum"><a name="L' . ($id) . '"></a><a href="index.php?date=' . $day .
'#' . $index . '">' . $id . '</a>:</td><td class="irc-date">[';
if($log['Old']) {
$html .= date($format[1], $logTime);
} else {
$html .= date($format[0], $logTime);
}
$html .= ']</td> ';
switch($log['Command']) {
case 'PRIVMSG':
$html .= '<td class="irc-black">&lt;' . htmlentities($log['Nick']) . '&gt; ';
$html .= linkify(htmlentities($log['Text']));
$html .= '</td>';
break;
case 'ACTION':
$html .= '<td class="irc-brick">* ' . htmlentities($log['Nick']) . ' ';
$html .= linkify(htmlentities($log['Text']));
$html .= '</td>';
break;
case 'PART':
case 'JOIN':
$html .= '<td class="irc-green">* ' . htmlentities($log['Nick']) . ' has ';
$html .= $log['Command'] == 'PART' ? 'left' : 'joined';
$html .= ' #silverstripe</td>';
break;
case 'QUIT':
$html .= '<td class="irc-navy">* ' . htmlentities($log['Nick']) . ' quit (';
$html .= htmlentities($log['Text']);
$html .= ')</td>';
break;
case 'NICK':
$html .= '<td class="irc-green">* ' . htmlentities($log['Nick']) . ' is now known as ';
$html .= htmlentities($log['Target']);
$html .= '</td>';
break;
case 'TOPIC':
$html .= '<td class="irc-green">* ' . htmlentities($log['Nick']) . ' changed the topic to \'';
$html .= linkify(htmlentities($log['Text']));
$html .= '\'</td>';
break;
case 'NOTICE':
case 'MODE':
case 'KICK':
return null;
default:
$html = '<td id="' . $index . '" class="log">' . var_export($log, true) . '</td>';
break;
}
$html .= '</tr>';
$html .= "\n";
return $html;
}
function get_head() {
global $channel, $server, $nick, $robots, $title;
ob_start();
include 'head.inc';
$head = ob_get_contents();
ob_end_clean();
return $head;
}
function get_foot() {
global $channel, $server, $nick, $robots, $title;
ob_start();
include 'foot.inc';
$foot = ob_get_contents();
ob_end_clean();
return $foot;
}
function load_defaults() {
global $channel, $server, $nick, $robots, $title;
$defaults = array(
'channel' => '#silverstripe',
'server' => 'irc.freenode.net',
'nick' => 'ss-log',
'robots' => 'index,follow'
);
$defaults['title'] = sprintf('IRC Log for %s on %s, collected by %s', $defaults['channel'], $defaults['server'], $defaults['nick']);
foreach($defaults as $name => $val) {
if(!$$name) $$name = $val;
}
}
function build_dates() {
static $dates = array();
if(!$dates) {
......@@ -159,74 +69,3 @@ function build_dates() {
}
return $dates;
}
function linkify($string) {
$pattern = '\b(https?://[^\s()<>"\'/]+|(?:[^\s()<>"\'.;/]+\.)+([\pL]{2,4}))(/[^\s<>()"\'/\?]*|/([^\s<>"\'/\?]*\([^\s<>()"\'/\?]*\)[^\s<>"\'/\?]*)+|\b)+(\?[^\s<>()"\']*)?';
return preg_replace_callback("#$pattern#ix", function($matches) use($string) {
$input = $matches[0];
if(isset($matches[2]) && empty($matches[3]) && in_array(strtolower($matches[2]), ['php', 'yml', 'yaml', 'ss', 'xml', 'jpg', 'png', 'gif', 'jpeg', 'form', 'log', 'lock', 'json', 'main'])) {
return $input;
}
if(strrpos($input, '&') && strrpos($input, '&') != strrpos($input, '&amp;')) {
list($input, $right) = explode('&', $input, 2);
$right = "&$right";
} else {
$right = '';
}
if(in_array(substr($input, -1), ['.',',','?'])) {
$right = substr($input, -1) . $right;
$input = substr($input, 0, -1);
}
$url = preg_match('!^https?://!i', $input) ? $input : "http://$input";
return '<a href="' . $url . '" rel="nofollow" target="_blank">' . "$input</a>$right";
}, $string);
}
// $start is day of week to start on, $count is number of days
function calendar($start, $count, $links, $thisMonth = false) {
$html = '<table class="cal" cellspacing=0>';
$html .= '<thead><tr><th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th></thead>';
$html .= '<tbody><tr>';
$i = 0;
$start = $start % 7;
while($i < $start) {
$html .= "<td class='empty'></td>";
++$i;
}
if($thisMonth) {
$bold = date('j');
} else {
$bold = 0;
}
$day = 1;
while($day <= $count) {
$evenodd = $i % 2 ? 'odd' : 'even';
if(isset($links[$day])) {
if($bold == $day) {
$content = "<a href='index.php?date={$links[$day][1]}'><b>$day</b></a>";
} else {
$content = "<a href='index.php?date={$links[$day][1]}'>$day</a>";
}
} else {
if($bold == $day) {
$content = "<b>$day</b>";
} else {
$content = $day;
}
}
$html .= "<td class='$evenodd'>$content</td>";
if($i % 7 == 6 && $day < $count) {
$html .= '</tr><tr>';
}
++$day;
++$i;
}
while($i % 7) {
$evenodd = $i % 2 ? 'odd' : 'even';
$html .= "<td class='empty'></td>";
++$i;
}
$html .= '</tr></tbody></table>';
return $html;
}
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<!doctype html>
<html>
<head>
<title><?=$title ?></title>
<meta name="description" content="IRC Log for <?=$channel ?>" />
<meta name="keywords" content="IRC Log for <?=$channel ?>" />
<meta name="robots" content="<?=$robots ?>" />
<meta name="viewport" content="width=device-width, user-scalable=1">
<style type="text/css">
<?php
class :log:head extends :x:element {
attribute
string channel = '#silverstripe',
string server = 'irc.freenode.net',
string nick = 'ss-log',
string robots = 'index,follow',
string title = 'IRC Log for #silverstripe on irc.freenode.net, collected by ss-log';
protected function render() {
header('Content-Type: text/html; charset=UTF-8');
return <head>
<title>{$this->getAttribute('title')}</title>
<meta name="description" content={"IRC Log for " . $this->getAttribute('channel')} />
<meta name="keywords" content={"IRC Log for " . $this->getAttribute('channel')} />
<meta name="robots" content={$this->getAttribute('robots')} />
<meta name="viewport" content="width=device-width, user-scalable=1" />
<style type="text/css">
{"
@font-face {
font-family: 'WebSymbolsRegular';
src: url('fonts/websymbols-regular-webfont.eot');
......@@ -149,7 +157,7 @@ form {
font-size: 14px;
position: absolute;
right: 2px;
top: 4px;
top: 0;
cursor: pointer;
border: none;
padding: 5px;
......@@ -197,11 +205,8 @@ form {
.year .nav p {
margin-bottom: 0;
}
"}
</style>
</head>
<body>
<div class="info">
<h1><?=$channel ?> IRC Log <?=search_form(isset($_GET['q']) ? $_GET['q'] : null) ?></h1>
</head>;
}
}
......@@ -12,71 +12,50 @@ if(isset($_GET['date']) && preg_match('#^20[0-9]{2}-[01][0-9]-[0-3][0-9]$#', $_G
$month = date('F', $time);
$day = date('j', $time);
$body = '<h2>IRC Log for ';
$body .= date('j F Y', $time);
$body .= '</h2><a href="index.php">Index</a></div>';
$body .= '<p>Timestamps are in NZT (<a href="http://timeanddate.com/worldclock/city.html?n=264" title="Click here to see what time it is right now in Wellington, New Zealand">New Zealand Time</a>).</p>';
$body = <x:frag />;
$body->appendChild(<log:info>
<h2>IRC log for {date('j F Y', $time)}</h2>
<a href="index.php">Index</a>
</log:info>);
if(isset($dates[$year][$month][$day])) {
$robots = 'index,nofollow';
$body .= '<table class="logs">';
$body->appendChild(<p>
Timestamps are in NZT (<a href="http://timeanddate.com/worldclock/city.html?n=264" title="Click here to see what time it is right now in Wellington, New Zealand">New Zealand Time</a>).
</p>);
$res = DB::inst()->query('SELECT "Time", "Text", "Command", "Target", "Nick", "ID", "Old" FROM "Log" WHERE DATE("Time") = \'' . date('Y-m-d', $time) . '\' ORDER BY "Time" ASC');
if(isset($dates[$year][$month][$day])) {
$head = <log:head robots="index,nofollow" />;
$id = 0;
while($row = pg_fetch_assoc($res)) {
$log = handle_log(++$id, $row);
if($log) {
$body .= $log;
} else {
--$id;
}
}
$body .= '</table>';
$body->appendChild(<logs:logs stamp={$time} />);
} else {
goto NoLog;
}
} else {
NoLog:
$body = '';
$head = <log:head />;
$body = <x:frag />;
if(count($dates) > 1) {
$body .= '<p>Year:';
foreach(array_keys($dates) as $year) {
$body .= " <a href='#$year'>$year</a>";
}
$body .= '</p>';
$body->appendChild(<log:info>
<log:years years={array_keys($dates)} />
</log:info>);
} else {
$body->appendChild(<log:info />);
}
$body .= '</div>';
$thisMonth = mktime(0, 0, 0, date('n'), 1, date('Y'));
foreach($dates as $year => $months) {
$body .= "<div class='year'><div class='nav'><h3><a name='$year'>$year</a></h3>";
if(count($months) > 1) {
$body .= '<p>Month:';
foreach(array_keys($months) as $month) {
$body .= " <a href='#{$year}_$month'>$month</a>";
}
$body .= '</p>';
}
$body .= '</div>';
foreach($months as $month => $days) {
$body .= "<div class='month'><h4><a name='{$year}_$month'>$month</a></h4>";
$stamp = strtotime($days[array_keys($days)[0]][1]);
$stamp = mktime(0, 0, 0, date('n', $stamp), 1, date('Y', $stamp));
$body .= calendar(date('w', $stamp), date('t', $stamp), $days, $stamp == $thisMonth);
$body .= '</div>';
}
$body .= '<div class="clear"><!-- --></div></div>';
$body->appendChild(<log:year year={$year} months={$months} />);
}
}
load_defaults();
$head = get_head();
$foot = get_foot();
$foot = <log:foot />;
echo $head, $body, $foot;
echo <x:doctype><html>
{$head}
<body>
{$body}
{$foot}
</body>
</html></x:doctype>;
<?php
class :log:info extends :x:element {
attribute string channel = '#silverstripe';
protected function render() {
$div = <div class="info">
<h1>{$this->getAttribute('channel')} IRC Log</h1>
<log:search />
</div>;
$div->appendChild($this->getChildren());
return $div;
}
}
<?php
class :logs:log extends :x:element {
attribute
int id @required,
array row @required,
array format;
protected function render() {
$id = $this->getAttribute('id');
$log = $this->getAttribute('row');
$format = $this->getAttribute('format') ?: array('H:i:s', 'H:i');
$logTime = strtotime($log['Time']);
$index = 'log_' . $log['ID'];
$day = date('Y-m-d', $logTime);
$row = <tr id={$index} />;
$row->appendChild(<td class="irc-linenum">
<a name={'L' . $id}></a>
<a href={'index.php?date=' . $day . '#' . $index}>{$id}</a>:
</td>);
if($log['Old']) {
$stamp = date($format[1], $logTime);
} else {
$stamp = date($format[0], $logTime);
}
$row->appendChild(<td class="irc-date">
[{$stamp}]
</td>);
$cell = <td />;
switch($log['Command']) {
case 'PRIVMSG':
// link
$cell->addClass('irc-black');
$cell->appendChild('<' . $log['Nick'] . '> ');
$cell->appendChild($this->linkify($log['Text']));
break;
case 'ACTION':
// link
$cell->addClass('irc-brick');
$cell->appendChild('* ' . $log['Nick'] . ' ');
$cell->appendChild($this->linkify($log['Text']));
break;
case 'PART':
case 'JOIN':
$cell->addClass('irc-green');
$verb = $log['Command'] == 'PART' ? 'left' : 'joined';
$cell->appendChild('* ' . $log['Nick'] . ' has ' . $verb . ' #silverstripe');
break;
case 'QUIT':
$cell->addClass('irc-navy');
$cell->appendChild('* ' . $log['Nick'] . ' quit (' . $log['Text'] . ')');
break;
case 'NICK':
$cell->addClass('irc-green');
$cell->appendChild('* ' . $log['Nick'] . 'is now known as ' . $log['Target']);
break;
case 'TOPIC':
// link
$cell->addClass('irc-green');
$cell->appendChild('* ' . $log['Nick'] . ' changed the topic to \'');
$cell->appendChild($this->linkify($log['Text']));
$cell->appendChild('\'');
break;
case 'NOTICE':
case 'MODE':
case 'KICK':
return <x:frag />;
default:
$cell->addClass('log');
$cell->appendChild(var_export($log, true));
break;
}
$row->appendChild($cell);
return $row;
}
protected function linkify($string) {
$pattern = '\b((?:https?://[^\s()<>"\'/]+|(?:[^\s()<>"\'.;/]+\.)+(?:[\pL]{2,4}))(?:/[^\s<>()"\'/\?]*|/(?:[^\s<>"\'/\?]*\([^\s<>()"\'/\?]*\)[^\s<>"\'/\?]*)+|\b)+(?:\?[^\s<>()"\']*)?)';
$actual = '\b(https?://[^\s()<>"\'/]+|(?:[^\s()<>"\'.;/]+\.)+([\pL]{2,4}))(/[^\s<>()"\'/\?]*|/([^\s<>"\'/\?]*\([^\s<>()"\'/\?]*\)[^\s<>"\'/\?]*)+|\b)+(\?[^\s<>()"\']*)?';
if(preg_match("#$pattern#ix", $string)) {
$bits = preg_split("#$pattern#ix", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$ret = <x:frag />;
foreach($bits as $string) {
if(preg_match("#$actual#ix", $string, $matches)) {
$input = $matches[0];
if(isset($matches[2]) && empty($matches[3]) && in_array(strtolower($matches[2]), ['php', 'yml', 'yaml', 'ss', 'xml', 'jpg', 'png', 'gif', 'jpeg', 'form', 'log', 'lock', 'json', 'main', 'js'])) {
goto LinkBase;
}
if(in_array(substr($input, -1), ['.',',','?'])) {
$right = substr($input, -1);
$input = substr($input, 0, -1);
} else {
$right = '';
}
$url = preg_match('!^https?://!i', $input) ? $input : "http://$input";
$ret->appendChild(<a href={$url} rel="nofollow" target="_blank">{$input}</a>);
$ret->appendChild($right);
} else {
LinkBase: $ret->appendChild($string);
}
}
return $ret;
} else {
return $string;
}
return preg_replace_callback("#$pattern#ix", function($matches) use($string) {
$input = $matches[0];
if(isset($matches[2]) && empty($matches[3]) && in_array(strtolower($matches[2]), ['php', 'yml', 'yaml', 'ss', 'xml', 'jpg', 'png', 'gif', 'jpeg', 'form', 'log', 'lock', 'json', 'main'])) {
return $input;
}
if(strrpos($input, '&') && strrpos($input, '&') != strrpos($input, '&amp;')) {
list($input, $right) = explode('&', $input, 2);
$right = "&$right";
} else {
$right = '';
}
if(in_array(substr($input, -1), ['.',',','?'])) {
$right = substr($input, -1) . $right;
$input = substr($input, 0, -1);
}
$url = preg_match('!^https?://!i', $input) ? $input : "http://$input";
return '<a href="' . $url . '" rel="nofollow" target="_blank">' . "$input</a>$right";
}, $string);
}
}
<?php
class :logs:logs extends :x:element {
attribute
int stamp,
array logs,
array format;
protected function render() {
$stamp = $this->getAttribute('stamp');
$format = $this->getAttribute('format');
$logs = $this->getAttribute('logs');
$table = <table class="logs" />;
if(!$logs) {
$res = DB::inst()->query('SELECT "Time", "Text", "Command", "Target", "Nick", "ID", "Old" FROM "Log" WHERE DATE("Time") = \'' . date('Y-m-d', $stamp) . '\' ORDER BY "Time" ASC');
$logs = array();
while($row = pg_fetch_assoc($res)) {
$logs[] = $row;
}
}
$id = 0;
foreach($logs as $row) {
$log = <logs:log id={++$id} row={$row} format={$format} />;
if((string)$log) {
$table->appendChild($log);
} else {
--$id;
}
}
return $table;
}
}
<?php
class :log:month extends :x:element {
attribute
int year @required,
string month @required,
array days @required;
protected function render() {
$year = $this->getAttribute('year');
$month = $this->getAttribute('month');
$days = $this->getAttribute('days');
return <div class="month">
<h4><a name={$year . '_' . $month}>{$month}</a></h4>
<log:calendar month={$month} year={$year} days={$days} />
</div>;
}
}
......@@ -9,7 +9,7 @@ if(empty($_GET['q']) || !trim($_GET['q'])) {
exit;
}
$robots = 'noindex,nofollow';
$head = <log:head robots="noindex,nofollow" />;
$term = $_GET['q'];
$order = isset($_GET['s']) ? $_GET['s'] : null;
......@@ -18,40 +18,42 @@ if(!in_array($order, ['Relevance', 'Time', null])) {
}
$results = DB::inst()->search($term, $order);
$term = htmlentities($term);
$id = 0;
$body = '<h2>Search results for "';
$body .= $term;
$body .= '"';
$body = <x:frag />;
$header = <h2>Search results for "{$term}"</h2>;
if($order) {
$body .= ' ordered by ' . strtolower($order);
$header->appendChild(' ordered by ');
$header->appendChild(strtolower($order));
}
$body .= '</h2><a href="index.php">Index</a></div><p>Timestamps are in NZT (<a href="http://timeanddate.com/worldclock/city.html?n=264" title="Click here to see what time it is right now in Wellington, New Zealand">New Zealand Time</a>).</p>';