summaryrefslogtreecommitdiff
path: root/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/index.php
blob: fc27fa2942d7a3eb12ab09b6c218c0cd0c3ed25c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
<?php
// Phergie Log Viewer ... Currently designed as a single PHP file in order to make it easy to
//  'install' this.  Just drop the index.php (or whatever name you wish to rename it to) wherever
//  you wish, and it will simply work.  Sure, it would be nice to structure some of this stuff into
//  various include files/etc.  But right now this is simple enough of a quick log viewer, that it's
//  just one file.


/********** SETUP **********/

// (Change any of these if/as needed for your setup) 
ini_set('default_charset', 'UTF-8');
date_default_timezone_set('UTC');
$log = "/PATH/AND/FILENAME/TO/YOUR/LOGFILE/PLEASE.db";


/********** PREPARATION **********/

$db = new PDO('sqlite:' . $log);
if (!is_object($db)) {
    // Failure, can't access Phergie Log.  Bail with an error message, not pretty, but works:
    echo "ERROR: Cannot access Phergie Log File, please check the configuration & access privileges";
    exit();
}


/********** DETECTION **********/

// Determine the mode of the application and call the appropriate handler function
$mode = empty($_GET['m']) ? '' : $_GET['m'];
switch ($mode) {
    case 'channel':
        show_days($db);
        break;
    case 'day':
        show_log($db);
        break;
    default:
        show_channels($db);
}

// Exit not really needed here, but reminds us that everything below is support functions:
exit();


/********** MODES **********/

/**
 * show_channels
 *
 * Provide a list of all channel's that we are logging information for:
 *
 * @param PDO A PDO object referring to the database 
 * @return void
 * @author Eli White <eli@eliw.com>
 **/
function show_channels(PDO $db) {
    // Begin the HTML page:
    template_header('Channels');
    echo "\nChannels:\n<ul>\n";

    // Loop through the database reading in each channel, and echoing out a <li> for it.
    // only grab actual channels that start with # ... also pre-lowercase everything.
    // this allows us to 'deal' with variable caps in how the channels were logged.
    $channels = $db->query("
        select distinct lower(chan) as c
        from logs
        where chan like '#%'
        ");
    foreach ($channels as $row) {
        $html = utf8specialchars($row['c']);
        $url = urlencode($row['c']);
        echo "<li><a href=\"?m=channel&w={$url}\">{$html}</a></li>\n";        
    }

    // Finish off the page:
    echo "\n</ul>\n";
    template_footer();
}

/**
 * show_days
 *
 * Create a calendar view of all days available for this particular channel
 * 
 * NOTE: May get unwieldy if large log files.  Perhaps consider in the future
 *  making a paginated version of this?  by year?  Or a separate 'which year' page
 *  before this?  Not to worry about now.
 *
 * @param PDO A PDO object referring to the database 
 * @return void
 * @author Eli White <eli@eliw.com>
 **/
function show_days(PDO $db) {
    $channel = $_GET['w'];
    $url = urlencode($channel);

    // Begin the HTML page:
    template_header('Daily Logs for Channel: ' . utf8specialchars($channel));
    echo "\n<ul>\n";

    // Query the database to discover all days that are available for this channel:
    $data = array();
    $prepared = $db->prepare("
        select distinct date(tstamp) as day
        from logs
        where lower(chan) = :chan
        ");
    $prepared->execute(array(':chan' => $channel));
    foreach ($prepared as $row) {
        list($y, $m, $d) = explode('-', $row['day']);
        $data[$y][$m][$d] = "{$y}-{$m}-{$d}";
    }

    // For now, just loop over them all and provide a list:
    ksort($data);
    foreach ($data as $year => $months) {
        ksort($months);
        foreach ($months as $month => $days) {
            // Figure out a few facts about this month:
            $stamp = mktime(0, 0, 0, $month, 1, $year);
            $first_weekday = idate('w', $stamp);
            $days_in_month = idate('t', $stamp);
            $name = date('F', $stamp);

            // We have a month ... start a new table:
            echo <<<EOTABLE
<div class="month">
  <table>
    <caption>{$name} {$year}</caption>
    <tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th></tr>
EOTABLE;
            // Now we need to start looping through the days in this month:
            echo '<tr>';
            $rowmod = 0;
            // Loop through all day entries, no matter how many blanks we need:
            for ($d = (-$first_weekday + 1); $d < $days_in_month + 1; $d++) {
                if (!($rowmod++ % 7)) {
                    // Stop/start a new row:
                    echo '</tr><tr>';
                }
                echo '<td>';
                // If this day is pre or post actual month days, make it blank:
                if (($d < 1) || ($d > $days_in_month)) {
                    echo '&nbsp;';
                } elseif (isset($days[$d])) {
                    // Make a link to the day's log:
                    echo "<a href=\"?m=day&w={$url}&d={$days[$d]}\">{$d}</a>";            
                } else {
                    // Just a dead number:
                    echo $d;
                }
                echo '</td>';
            }
            // Finish off any blanks needed for a complete table row:
            while ($rowmod++ % 7) {
                echo '<td>&nbsp;</td>';
            }
            echo "</tr></table></div>\n";
        }
    }

    // Finish off the page:
    echo "\n</ul>\n";
    template_footer();    
}

/**
 * show_log
 *
 * Actually show the log for this specific day
 *
 * @param PDO A PDO object referring to the database 
 * @return void
 * @author Eli White <eli@eliw.com>
 **/
function show_log(PDO $db) {
    $channel = $_GET['w'];
    $day = $_GET['d'];
    $parts = explode('-', $day);
    $formatted_date = "{$parts[0]}-{$parts[1]}-{$parts[2]}";

    // Begin the HTML page:
    template_header('Date: ' . utf8specialchars($formatted_date) .
        ' - Channel: ' . utf8specialchars($channel));

    // Query the database to get all log lines for this date:
    $prepared = $db->prepare("
        select time(tstamp) as t, type, nick, message
        from logs
        where lower(chan) = :chan and date(tstamp) = :day
        order by tstamp asc
        ");
    $prepared->execute(array(
        ':chan' => $channel,
        ':day' => $day,
        ));

    // Loop through each line,
    foreach ($prepared as $row) {
        // Prepare some basic details for output:
        $color = nick_color($row['nick']);
        $time = utf8specialchars($row['t']);
        $msg = utf8specialchars($row['message']);
        $nick = utf8specialchars($row['nick']);
        $type = false;
        
        // Now change the format of the line based upon the type:
        switch ($row['type']) {
            case 4: // PRIVMSG (A Regular Message)
                echo "[$time] <span style=\"color:#{$color};\">&lt;{$nick}&gt;</span> {$msg}<br />\n";
                break;
            case 5: // ACTION (emote)
                echo "[$time] <span style=\"color:#{$color};\">*{$nick} {$msg}</span><br />\n";
                break;
            case 1: // JOIN
                echo "[$time] -> {$nick} joined the room.<br />\n";
                break;
            case 2: // PART (leaves channel)
                echo "[$time] -> {$nick} left the room: {$msg}<br />\n";
                break;
            case 3: // QUIT (quits the server)
                echo "[$time] -> {$nick} left the server: {$msg}<br />\n";
                break;
            case 6: // NICK (changes their nickname)
                echo "[$time] -> {$nick} is now known as: {$msg}<br />\n";
                break;
            case 7: // KICK (booted)
                echo "[$time] -> {$nick} boots {$msg} from the room.<br />\n";
                break;
            case 8: // MODE (changed their mode)
                $type = 'MODE';
            case 9: // TOPIC (changed the topic)
                $type = $type ? $type : 'TOPIC';
                echo "[$time] -> {$nick}: :{$type}: {$msg}<br />\n";
        }
    }
        
    // Finish up the page:
    template_footer();
}

/**
 * nick_color
 *
 * Uses a silly little algorithm to pick a consistent but unique(ish) color for
 *  any given username.  NOTE: Augment this in the future to make it not generate
 *  'close to white' ones, also maybe to ensure uniqueness?  (Not allow two to have
 *  colors that are close to each other?)
 *
 * @return string A CSS valid hex color string
 * @author Eli White <eli@eliw.com>
 **/
function nick_color($user) {
    static $colors = array();
    
    if (!isset($colors[$user])) {
        $colors[$user] = substr(md5($user), 0, 6);
    }

    return $colors[$user];
}

/**
 * utf8specialchars
 *
 * Just a quick wrapper around htmlspecialchars
 *
 * @param string The UTF8 string to escape
 * @return string An escaped and ready for HTML use string
 * @author Eli White <eli@eliw.com>
 **/
function utf8specialchars($string) {
    return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
}


/********** TEMPLATES **********/

/**
 * template_header
 *
 * Echo out the header for each HTML page
 *
 * @param $title string The title to be used for this page.
 * @return void
 * @author Eli White <eli@eliw.com>
 **/
function template_header($title) {
    $css = template_css();
    echo <<<EOHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Phergie LogViewer - {$title}</title>
    <style type="text/css" media="all">{$css}</style>
  </head>
  <body>
    <h2>Phergie LogViewer - {$title}</h2>
EOHTML;
}

/**
 * template_footer
 *
 * Echo out the bottom of each HTML page
 *
 * @return void
 * @author Eli White <eli@eliw.com>
 **/
function template_footer() {
    echo <<<EOHTML
  </body>
</html>
EOHTML;
}

/**
 * template_css
 *
 * Generate the CSS used by these HTML pages & return it.
 *
 * @return string The CSS in question:
 * @author Eli White <eli@eliw.com>
 **/
function template_css() {
    return <<<EOCSS
    div.month {
        float: left;
        height: 15em;
    }

    div.month table {
        border-collapse: collapse;
        border: 2px solid black;
        margin-right: 2em;
    }

    div.month td, div.month th {
        text-align: center;
        vertical-align: bottom;
        border: 1px solid gray;
        width: 2em;
        height: 1.7em;
        padding: 1px;
        margin: 0px;
    }

    div.month th {
        text-decoration: bold;
        border: 2px solid black;
    }

    div.month a {
        text-decoration: none;
    }

    a:visited, a:link {
        color: blue;
    }

    a:active, a:hover {
        color: red;
    }
EOCSS;
}