This is the source code for the Horizon weblog system used by
Beth.
Direct any questions to dmd@3e.org.
Feel free to use this code for your own nefarious purposes; I'd appreciate
it if you'd acknowledge my contribution and perhaps notify me of where
and how you're using my code.
-- Daniel Drucker
SQL table creation
CREATE TABLE cats (
catname varchar(30) DEFAULT '' NOT NULL,
catid varchar(30) DEFAULT '' NOT NULL,
catdesc text DEFAULT '' NOT NULL,
PRIMARY KEY (catid)
);
CREATE TABLE tidbits (
tidid varchar(30) DEFAULT '' NOT NULL,
tdate datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
entry text NOT NULL,
PRIMARY KEY (tidid)
);
CREATE TABLE catstids (
catid varchar(30) DEFAULT '' NOT NULL,
tidid varchar(30) DEFAULT '' NOT NULL
);
index.php
<?
/*
Copyright (C) 2001 Daniel M. Drucker <dmd@3e.org>
Distributed under the terms of the GNU General Public License,
included here by reference.
*/
?>
<HTML>
<!-- all your meme are belong to us -->
<HEAD>
<TITLE>Just a Log</TITLE>
<LINK HREF="justalog.css" rel="stylesheet" type="text/css">
</HEAD>
<?
include("otherinfo.php");
include("horizon.php");
?>
<SCRIPT LANGUAGE="JavaScript">
<!--
function checkform ( form )
{
if (form.searchword.value == "") {
alert( "You can't search for nothing." );
form.searchword.focus();
return false ;
}
return true ;
}
//-->
</SCRIPT>
<BODY background="store/marsambg.gif">
<center>
<a title="main view" href="index.php"><img border=0 src="store/marble2.gif"></a><br>
<h1>Just a Log</h1>
by <a href="http://www.bethroberts.com/">Beth</a>
<P><? print $headline; // (from otherinfo.php) ?><P>
<table width=85% border="0" valign=top>
<tr>
<td width="25%" valign=top>
<font face="Verdana, sans-serif" size="2">
<form method="post" action="index.php">
<b>log archives</b><br>
<!-- The way month subsets are selected is only good for ONE YEAR. When the months
wrap around back to July (in 2001), this system will need to change. In the
meantime, I'm too lazy. -->
<select name="tmonth" onChange="this.form.submit()">
<option value="">Go to month... </option>
<option value="">current entries</option>
<option value="7">July 2000</option>
<option value="8">August 2000</option>
<option value="9">September 2000</option>
<option value="10">October 2000</option>
<option value="11">November 2000</option>
<option value="12">December 2000</option>
<option value="1">January 2001</option>
<option value="2">February 2001</option>
<option value="3">March 2001</option>
</select>
<input type=submit value="Go">
</form>
<form method="post" action="index.php" onsubmit="return checkform(this);">
<b>search</b><br>
<input name="searchword" type=text size=12>
<input type=submit value="Go">
</form>
<?
// the BOX-o-DOOM
$addy = "box@bethroberts.com";
// process the form just submitted
if ($action == "sendbox")
{
// add IP signature to outgoing message
$message .= "\n\n--\n" . gethostbyaddr($REMOTE_ADDR) . " is DOOMED!";
mail($addy, "box o doom", stripslashes($message), "From: box_o_doom@bethroberts.com");
print "<font face=Verdana size=-1>
<b>Your doomed input has been received, and awaits its
unsavory fate. Thanks!</b></font>\n";
// generate the box-o-doom HTML
} else {
print "<font face=Verdana size=-1>
<form method=post> <input type=hidden name=action value=sendbox>\n
<b>box o doom</b><br>\n
<input type=text name=message size=12>\n
<input type=submit value=send>\n
</form></font>";
}
print $otherpages; // (from otherinfo.php)
?>
<p>
</font>
</td>
<td valign=top>
<font face="Verdana, sans-serif" size="2">
<?
$count = 10; // how many entries per page
/*
Now we're in the main part of the page. The first thing
we need to check for is if we're in a special case, such
as a view of a particular month (via the dropdown box),
category (by clicking on one), a permalink, or a search
(via the search box). If so, we give the user the option
of returning to the main view:
*/
if ($catchoice or $tmonth or $onetid or $searchword)
print ('[ Viewing a subset. <a href="index.php">Go to main view</a>? ]<P>');
// if we haven't used the 'more entries' link, the start-value
// for the SELECT won't be defined, so we need to define it.
if (! $start) $start = 0;
// all the real work happens here (see horizon.php)
getTidbits($start,$count,$catchoice,$tmonth,$onetid,$searchword);
print "<p> </p>
To see archives of this log by month, use the drop-down box
in the left column (near the top of this page.) In addition,
a <a href=allentries.php>complete archive</a> is available.<p>\n";
print $ifyoucare; // (from otherinfo.php)
?>
<p>
<a href="admin.php?action=create"><img src="create.gif" border=0></a>
</font>
</td>
</tr>
</table>
</center>
<BR> <BR> <BR> <BR>
... <a href="http://www.bethroberts.com/">beth@3e.org</a>
</BODY>
</HTML>
horizon.php
<?
/*
Copyright (C) 2001 Daniel M. Drucker <dmd@3e.org>
Distributed under the terms of the GNU General Public License,
included here by reference.
*/
require("/home/threee/3e.org/password.inc");
@mysql_connect("localhost","threee",$password);
@mysql_select_db("threee");
/*
Only these tags are permitted in an entry, to avoid breaking
the entire page with one bad apple. (Consider what would happen
if an entry contained a </TABLE> or </DIV> tag.)
*/
$allowedtags="<strike>,<s>,<a>,<img>,<i>,<br>,<b>,<u>,<em>,<strong>,<font>,<pre>,<ul>,<li>,<ol>,<blockquote>,<p>,<center>";
function getCats($tidid="")
{
/*
This function does double duty. If called with a $tidid
argument, returns a list of categories that the tidid
belongs to. If called with no argument, returns ALL
category names.
*/
$categories = Array(); // be an array EVEN IF EMPTY, to avoid errors later
// If we're just looking for the list of categories, this is all we need:
$query = "SELECT COUNT(*) AS count,
cats.catname AS catname,
cats.catid AS catid,
cats.catdesc AS catdesc
FROM cats,catstids
WHERE cats.catid = catstids.catid ";
// If, OTOH, we're looking for the categories an entry belongs to,
// we extend the query:
if ($tidid) $query .= " AND catstids.tidid = '$tidid' ";
// In any case, we want the results to be in alphabetical order:
$query .= " GROUP BY cats.catid ORDER BY cats.catname";
$result = mysql_query($query);
// dump the results of the SELECT into an array and return it.
while ($row = mysql_fetch_array($result)) $categories[] = $row;
return($categories);
}
function getTidbits($start=0,$count=10,$category="",$tmonth="",$onetid="",$searchword="")
{
// This is the meat of the entire system.
if (! $onetid) // the normal case
{
$query = "SELECT tidbits.tidid as tidid,tdate,entry FROM tidbits ";
// if looking at one category, we need this table too
if ($category) $query .= " ,catstids ";
// don't know which of next three lines
$query .= " WHERE 1=1 "; // will be true, so need a base WHERE
// to allow the AND to come after it
// month() is a MySQL function, 'LIKE' is for the searchbox
if ($tmonth) $query .= " AND month(tdate) = $tmonth ";
if ($searchword) $query .= " AND entry LIKE \"%$searchword%\" ";
/*
The next line bears some examination. Understand this, and
you understand how relational databases work. This is an
SQL join. We have a category we're seeking ($category).
<< catid = $category >> retrieves all the catid-tidid rows
from catstids (the category-tidbit relationship table)...
so now we have a list of ALL tidids in a particular category.
Then, << catstids.tidid = tidbits.tidid >> establishes the
relation (join) we wish to make. We want to retrieve all rows
from tidbits where the tidid of that row is found in the list
we just made from catstids.
*/
if ($category) $query .= " AND catid = '$category' AND catstids.tidid = tidbits.tidid ";
// reverse order by tidid, which is really just the date
$query .= " ORDER BY tidid DESC ";
// subset views aren't page-at-a-time
if (! ($tmonth or $category or $searchword)) $query .= " LIMIT $start,$count ";
}
else if ($onetid == "everything") // archive edition (see allentries.php)
{
$query = "SELECT tidid,tdate,entry FROM tidbits ORDER BY tidid DESC";
}
else // permalink support (if we're here, $onetid is a tidid)
{
$query = "SELECT tidid,tdate,entry FROM tidbits WHERE tidid = '$onetid'";
}
$result = mysql_query($query);
while(list($e_tidid,$e_tdate,$e_entry) = mysql_fetch_row($result))
{
print "<div class=tidbit>[ ";
foreach (getCats($e_tidid) as $iter)
{
print '<a title="view all in category \''
. $iter['catname']
. '\'" href=".?catchoice='
. $iter['catid']
. '">'
. $iter['catname'] . '</a> ';
}
print "]\n <font size=-2>"
. substr($e_tdate,0,10)
. '</font>
<a title="edit tidbit"
style="TEXT-DECORATION: none"
href="admin.php?action=change&tidid='
. strip_tags($e_tidid,$allowedtags)
. '">…</a>
<a title="permanent URL for this entry"
style="TEXT-DECORATION: none"
href="index.php?onetid='
. $e_tidid
. '"><IMG border=0 src=blink.gif></a><br>'
. $e_entry
. "\n</div>\n<p>\n\n";
}
if (! ($tmonth or $category or $onetid or $searchword)) // subset views aren't page-at-a-time
{
// make the forward and back links
print '[<a href=".?start='
. ($start + $count)
. '"><<<</a> more entries ';
if ($start - $count >= 0)
{
print '<a href=".?start='
. ($start - $count)
. '">>>></a> ';
}
print "]";
}
}
?>
admin.php
<?
/*
Copyright (C) 2001 Daniel M. Drucker <dmd@3e.org> except where noted.
Distributed under the terms of the GNU General Public License,
included here by reference.
*/
/*
Code from http://www.zend.com/zend/tut/authentication.php
Copyright (C) 2000 - 2001 by Zend Technologies Ltd.
*/
$auth = false; // Assume user is not authenticated
if (isset( $PHP_AUTH_USER ) && isset($PHP_AUTH_PW)) {
$filename = '/home/threee/.htpasswd';
$fp = fopen( $filename, 'r' );
$file_contents = fread( $fp, filesize( $filename ) );
fclose( $fp );
$lines = explode ( "\n", $file_contents );
foreach ( $lines as $line ) {
list( $username, $password ) = explode( ':', $line );
if ( $username == "$PHP_AUTH_USER" ) {
$salt = substr( $password , 0 , 2 );
$enc_pw = crypt( $PHP_AUTH_PW, $salt );
if ( $password == "$enc_pw" ) {
$auth = true;
break;
}
}
}
}
if ( ! $auth ) {
header( 'WWW-Authenticate: Basic realm="Horizon"' );
header( 'HTTP/1.0 401 Unauthorized' );
echo 'This is the administrative interface; authorization is required.';
exit;
}
/*
END code from Zend; remaining code (C) 2000 - 2001 Daniel M. Drucker
*/
?>
<HTML>
<HEAD>
<TITLE>Just a Log - Editing Interface</TITLE>
<LINK HREF="justalog.css" rel="stylesheet" type="text/css">
</HEAD>
<BODY background="store/marsambg.gif">
<center>
<a title="main view" href="index.php"><img border=0 src="store/marble2.gif"></a><br>
<h1>Just a Log - Editing Interface</h1><p>
<?
include("horizon.php");
switch($action)
{
// I use the variable $iter as a placeholder for ITERating
// over values of an array in foreach statements
///////////////////////////////////////////////////////////////
case "change":
// DISPLAY the form to edit or delete a tidbit
$result = mysql_query("SELECT entry FROM tidbits WHERE tidid = '$tidid'");
list($entry) = mysql_fetch_array($result);
$incats = Array();
foreach (getCats($tidid) as $iter)
{
// make $incats array of categories tidbit is in
$incats[] = $iter['catid'];
}
print '<h3>Editing a tidbit.</h3>
<a href="admin.php?action=do_delete&deletetid='
. $tidid
. '">delete</a>
<br>
<form method=post action="admin.php?action=do_edit&edittid='
. $tidid
. '"><font color=red size=+1>Editing '
. $tidid
. '</font><br><input type=hidden name=tidid value="'
. $tidid
. '"><select multiple size=24 name="category[]">';
foreach (getCats() as $iter) // create the category select listbox
{
print '<option ';
// here's what $incats was for; to retain current categorization:
if (in_array($iter['catid'],$incats)) print ' selected ';
print 'value="'
. $iter['catid']
. '">'
. $iter['catname']
. "</option>\n";
}
print '</select>
<textarea cols=60 rows=24 name="tidtext">'
. $entry
. '</textarea><input value="Save" type="submit"></form>';
break; // end of case "change"
///////////////////////////////////////////////////////////////
case "create":
// adding a new entry
print '<h3>Creating a new tidbit.</h3>
<form action="admin.php?action=do_create" method="post">
<select multiple size=24 name="category[]">';
foreach (getCats() as $iter) // create the category select listbox
{
print '<option value="'
. $iter['catid']
. '">'
. $iter['catname']
. "</option>\n";
}
print '</select><textarea cols=60 rows=24 name="tidtext"></textarea>
<input type="submit" value="Add New Entry"></form>';
break; // end of case "create"
///////////////////////////////////////////////////////////////
case "do_edit":
// perform the edit operation
// default cat is "random thoughts"
if (! $category) $category[] = "2000-10-06T14:57:07Z";
// delete current categorization, create new ones
mysql_query("DELETE FROM catstids WHERE tidid = '$tidid'");
foreach ($category as $iter)
{
mysql_query("INSERT INTO catstids (catid,tidid) VALUES ('$iter', '$tidid')");
}
// alter the text
$query = "UPDATE tidbits SET entry='$tidtext' WHERE tidid='$tidid'";
mysql_query($query);
print "Update successful.";
break; // end of case "do_edit"
///////////////////////////////////////////////////////////////
case "do_create":
$tidid = date("Y-m-d\TH:i:s");
$tdate = date("Y-m-d H:i:s");
// add to catstids
// default cat is "random thoughts"
if (! $category) $category[] = "2000-10-06T14:57:07Z";
foreach ($category as $iter)
{
mysql_query("INSERT INTO catstids (catid,tidid) VALUES ('$iter', '$tidid')");
}
// add to tidbits
$query = "INSERT INTO tidbits (tidid,tdate,entry) VALUES ('$tidid', '$tdate', '$tidtext')";
mysql_query($query);
print 'Posted.';
break; // end of case "do_create"
///////////////////////////////////////////////////////////////
case "do_delete":
mysql_query("DELETE FROM tidbits WHERE tidid = '$deletetid'");
mysql_query("DELETE FROM catstids WHERE tidid = '$deletetid'");
print "$deletetid deleted";
break; // end of case "do_delete"
///////////////////////////////////////////////////////////////
default:
print "something bad happened, here's what I know:<br><br>\n\n";
phpinfo();
}
?>
</center>
<BR> <BR> <BR> <BR>
... <a href="http://www.bethroberts.com/">beth@3e.org</a>
</BODY>
</HTML>
allentries.php
<HTML>
<HEAD>
<TITLE>Just a Log</TITLE>
<LINK HREF="justalog.css" rel="stylesheet" type="text/css">
</HEAD>
<?
include("horizon.php");
?>
<BODY background="store/marsambg.gif">
<center>
<a title="main view" href="index.php"><img border=0 src="store/marble2.gif"></a><br>
<h1>Just a Log - Archive Edition</h1>
by <a href="http://www.bethroberts.com/">Beth</a>
<P>Every entry, finish to start.<P>
</center>
<font face="Verdana, sans-serif" size="2">
<?
getTidbits(0,99999,$catchoice,$tmonth,"everything");
print "<p> <p>\n";
?>
</font>
<BR> <BR> <BR> <BR>
... <a href="http://www.bethroberts.com/">beth@3e.org</a>
</BODY>
</HTML>
category.php
<?
/*
Copyright (C) 2001 Daniel M. Drucker <dmd@3e.org>
Distributed under the terms of the GNU General Public License,
included here by reference.
*/
?>
<HTML>
<HEAD>
<TITLE>Just a Log - Guide to Categories</TITLE>
<LINK HREF="justalog.css" rel="stylesheet" type="text/css">
</HEAD>
<?
include("horizon.php");
?>
<body background="store/marsambg.gif">
<center>
<img src="store/marble2.gif"><h2>Guide to Categories</h2>
<hr noshade>
</center>
Just a little bit of info to sort out what the categories that I use in
<a href="index.php">my log</a> are supposed to represent.
Many of these are self-explanatory.
<hr noshade>
<table border=0 cellpadding=10 cellspacing=0 width=75%>
<?
// for each category, display name as link, number of entries, description
foreach (getCats() as $iter)
{
print '<tr><td width=5%></td><td valign=top width=15%>
<a href="index.php?catchoice='
. $iter['catid']
. '">'
. $iter['catname']
. '</a><br>('
. $iter['count']
. " entries)</td>\n"
. '<td valign=top>'
. $iter['catdesc']
. "</td></tr>\n";
}
?>
</table>
<BR> <BR> <BR> <BR>
... <a href="http://www.bethroberts.com/">beth@3e.org</a>
</BODY>
</HTML>
otherinfo.php
<?
$headline = "<i>Life does not consist mainly - or even largely of facts and happenings. <br>It
consists mainly of the storm of thoughts that is forever blowing through
one's head.</i><br><font size=-1>Mark Twain</font>";
$ifyoucare = "<small>P.S. In case you care, the list of other weblogs above is mainly for me to just keep track of the ones I think I might want to go back and read some more. They are not necessarily all my favorites or anything (though some certainly are). Some of them I don't know enough about to really judge. I'm sure I'll shuffle them around periodically and remove some, and add more, and whatnot.
</small>";
$otherpages = '<p><b>My other pages:</b>
<br>
<a href="http://www.bethroberts.com/">Home</a><br>
<a href="http://www.bethroberts.com/about.html">About</a><br>
<a href="http://www.bethroberts.com/idiolect.html">Idiolect</a><br>
<a href="http://www.bethroberts.com/writings.html" >Writings</a><br>
<a href="category.php">Categories</a><br>
<a href="source.php">Weblog code</a><br>
<a href="http://www.bethroberts.com/cwhumor.html">Cow Orker Humor</a><br>
<p>
<b>Some other weblogs:</b>
<br>
<a href="http://www.davidchess.com/words/log.html">David Chess</a><br>
<a href="http://www.sevencrabrangoon.com/index.htm">Sevencrabrangoon</a><br>
<a href="http://www.eod.com/">An Entirely Other Day</a><br>
<a href="http://www.lileks.com/bleats/index.html">The Bleat</a><br>
<a href="http://www.stevewhite.org/log/current/index.htm">Plurp</a><br>
<a href="http://www.aigeek.com/entropy/">aigeek: Entropy</a><br>
<a href="http://www.girlhacker.com/log.html">Girlhacker</a><br>
<a href="http://world.std.com/~emg/blogger.html">Follow Me Here</a><br>
<a href="http://www.whalley.org/cgi-bin/blog.cgi">Whalley.org</a><br>
<a href="http://www.genehack.org/">Genehack</a><br>
<a href="http://www.almostcool.org/cs/index.html">Come to My Senses</a><br>
<a href="http://www.memepool.com/">Memepool</a><br>
<a href="http://www.larkfarm.com/weblog.asp">Mike (of Larkfarm)</a><br>
<a href="http://torrez.org/">Torrez</a><br>
<a href="http://www.ratbastard.org">Ratbastard</a><br>
<a href="http://www.syrup.org/">Mirror, Mirror</a><br>
<a href="http://www.metagrrrl.com/">Metagrrrl</a><br>
<a href="http://www.camworld.com/">Camworld</a><br>
<a href="http://nowthis.com/log/">Now This</a><br>
<a href="http://baylink.pitas.com/">Baylink</a><br>
<a href="http://www.cramper.com/log/log.html">Camper\'s log</a><br>
<a href="http://joel.editthispage.com/">Joel on Software</a><br>
<a href="http://www.kitschbitch.com/index.html">kitschbitch</a><br>
<a href="http://www.uncorked.org/medley/">Medley</a><br>
<a href="http://www.sixfoot6.com/">Sixfoot6</a><br>
<a href="http://rc3.org/">rc3.org</a><br>
<a href="http://rubberducky.nu/girl/breasts/">The Breast Chronicles</a><br>
<a href="http://www.xplane.com/xblog/">Xblog</a><br>
<a href="http://bitch.shutdown.com/">The Misanthropic Bitch</a><br>
<a href="http://twernt.com/weblog/index.php3">Twernt</a><br>
<a href="http://ptypes.pitas.com/">Personality Types</a><br>
<a href="http://www.frykitty.com/">Frytopia</a><br>
<a href="http://www.50cups.com/strange/default.asp">strange brew</a><br>
<a href="http://www.harrumph.com/">harrumph!</a><br>
<a href="http://www.robotwisdom.com/">Robot Wisdom</a><br>
<a href="http://www.evhead.com/">evhead</a><br>
<a href="http://booknotes.weblogs.com/">booknotes</a><br>
<a href="http://viewfromtheheart.editthispage.com/">ViewFromTheHeart</a><br>
<a href="http://www.loony.org/">Loony.org</a><br>
<a href="http://wmf.editthispage.com/">Hack the Planet</a><br>
<a href="http://www.wwa.com/~dhartung/weblog/">Lake Effect</a><br>
<a href="http://www.gumbopages.com/looka/">Looka</a><br>
<a href="http://www.slumberland.org/">Slumberland</a><br>
<a href="http://www.geekish.com/weblog/blogger.html">Geekish</a><br>
<p>
<b>Discussion sites:</b>
<br>
<a href="http://www.edgecase.org/">edgecase</a><br>
<a href="http://www.metafilter.com/">Metafilter</a><br>
<a href="http://boards2.parentsplace.com/cgi-bin/boards/atheist">Atheist Parenting</a><br>
<a href="http://www.kuro5hin.org/">kuro5hin</a><br>
<p>
<b>Other Reading:</b>
<br>
<a href="http://catless.ncl.ac.uk/Risks/index.21.html">Risks digest</a><br>
<a href="http://www.useit.com/alertbox/">Jakob Neilsen\'s Alertbox</a><br>
<a href="http://www.salon.com/">Salon</a><br>
<a href="http://commons.somewhere.com/rre/">Red Rock Eater Digest</a><br>';
?>
... webmaster@3e.org