From c1a88d5d6733f1cd17fe677456cb445edd6a7afd Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Sat, 16 Dec 2000 08:39:01 +0000
Subject: [PATCH] Again, a large batch of updates - I'm twisting things around
 here:

 1. improved .htaccess to be more "secure": to keep prying
    eyes out

 2. rewrote the administration section from scratch using a
    modular approach

 3. improved the information gathered by error.php - we can
    now (hopefully) track what bots are crawling us.

 4. fixed a bug in submit.php, fixed a bug in theme zaphod,
    fixed a bug in theme marvin.

 5. rewrote cron from scratch - it now interfaces with
    modules as it should have been from the beginning.
    Very cool if you ask me - it can use UNIX/Linux
    crontabs.

 6. updated widget.inc to be module aware - needs more
    work though - maybe this afternoon?

 7. updated most modules: small bugfixes, improvements, and
    even the documentation

 8. removed diary.php and made it a module - you can now
    run a drop.org site without a diary system if someone
    would prefer so

 9. updated all themes to use the new modules where
    appropriate

 10. added a robots.txt because the error message in the
     watchdog become annoying.

 11. added the new configuration system (mutliple vhosts
     on the same source tree) - use hostname.conf instead
     of config.inc

 12. removed calendar.inc and made it a module

 13. added format_interval() to functions.inc (UnConeD)

 14. whatever I forgot ...
---
 .htaccess                              |  19 ++-
 account.php                            |   2 +-
 admin.php                              |   2 +
 cron.php                               |   7 +-
 diary.php                              | 186 ----------------------
 error.php                              |   4 +-
 includes/calendar.inc                  |  76 ---------
 includes/cron.inc                      |  35 +++--
 includes/function.inc                  |  19 +++
 includes/{config.inc => hostname.conf} |  37 ++---
 includes/widget.inc                    |  10 +-
 modules/account.module                 |   9 +-
 modules/backend.module                 |   6 +-
 modules/cron.module                    |  34 ++--
 modules/diary.module                   | 209 +++++++++++++++++++++++--
 modules/documentation.module           |  33 ++--
 modules/watchdog.module                |   9 +-
 modules/watchdog/watchdog.module       |   9 +-
 modules/wishlist.module                |   8 +-
 robots.txt                             |   2 +
 scripts/cron-lynx                      |   2 +
 submit.php                             |   4 +-
 themes/marvin/marvin.theme             |   9 +-
 themes/unconed/unconed.theme           |   6 +-
 24 files changed, 362 insertions(+), 375 deletions(-)
 delete mode 100644 diary.php
 delete mode 100644 includes/calendar.inc
 rename includes/{config.inc => hostname.conf} (85%)
 create mode 100644 robots.txt
 create mode 100644 scripts/cron-lynx

diff --git a/.htaccess b/.htaccess
index c4e32bebaba2..73b209cfa859 100644
--- a/.htaccess
+++ b/.htaccess
@@ -7,26 +7,30 @@
   ForceType application/x-httpd-php
 </Files>
 
-# Protect .inc files:
-#  .inc files can be read from the web so make sure we keep it 
-#  away from the casual prying eyes.  Especially `config.inc'.
-<Files *.inc>
+# Protect includes-directory:
+<Files *includes>
   order deny,allow
   deny from all
 </Files>
 
-# Protect CVS directories:
-<Files *CVS>
+# Protect modules-directory:
+<Files *modules>
   order deny,allow
   deny from all
 </Files>
 
-# Protect theme directories:
+# Protect themes-directory:
 <Files *themes>
   order deny,allow
   deny from all
 </Files>
 
+# Protect CVS directories:
+<Files *CVS>
+  order deny,allow
+  deny from all
+</Files>
+
 # Customized server error messages:
 ErrorDocument 400 /error.php
 ErrorDocument 402 /error.php
@@ -47,4 +51,3 @@ ErrorDocument 500 /error.php
    php_value session.gc_maxlifetime  2000000
    php_value session.cache_expire    200000
 </IfModule>
-
diff --git a/account.php b/account.php
index 2e4ed875ac7f..fd755d59c637 100644
--- a/account.php
+++ b/account.php
@@ -254,7 +254,7 @@ function account_user($uname) {
 
     $result = db_query("SELECT d.* FROM diaries d LEFT JOIN users u ON u.id = d.author WHERE u.userid = '$uname' AND d.timestamp > ". (time() - 1209600) ."  ORDER BY id DESC LIMIT 2");
     while ($diary = db_fetch_object($result)) {
-      $box3 .= "<DL><DT><B>". date("l, F jS", $diary->timestamp) .":</B></DT><DD><P>". check_output($diary->text) ."</P><P>[ <A HREF=\"diary.php?op=view&name=$uname\">more</A> ]</P></DD></DL>\n";
+      $box3 .= "<DL><DT><B>". date("l, F jS", $diary->timestamp) .":</B></DT><DD><P>". check_output($diary->text) ."</P><P>[ <A HREF=\"module.php?mod=diary&op=view&name=$uname\">more</A> ]</P></DD></DL>\n";
       $diaries++;
     }
     
diff --git a/admin.php b/admin.php
index df0478d233ad..aa3c3ed71c3b 100644
--- a/admin.php
+++ b/admin.php
@@ -4,6 +4,7 @@
 if (!$user->id || $user->id > 4) exit();
 
 include "includes/admin.inc";
+include "includes/cron.inc";
 
 // display admin header:
 admin_header();
@@ -17,6 +18,7 @@
     }
     else {
       include_once "modules/$filename.module";
+      if ($module["cron"] && !cron_get($filename)) cron_set($filename, 172800); 
       if ($module["admin"]) $output .= "<A HREF=\"admin.php?mod=$filename\">$filename</A> | ";
     }
   }
diff --git a/cron.php b/cron.php
index ebde1b30b21c..376792366bba 100644
--- a/cron.php
+++ b/cron.php
@@ -1,12 +1,7 @@
 <?
 
-include "includes/theme.inc";
 include "includes/cron.inc";
 
-$result = db_query("SELECT * FROM cron");
-
-while ($cron = db_fetch_object($result)) {
-  if (time() - $cron->timestamp > $cron->scheduled) cron_execute($cron);
-}
+cron_run();
 
 ?>
diff --git a/diary.php b/diary.php
deleted file mode 100644
index 43f1d560088f..000000000000
--- a/diary.php
+++ /dev/null
@@ -1,186 +0,0 @@
-<?
-
-include "includes/theme.inc";
-
-function diary_overview($num = 20) {
-  global $theme, $user;
-
-  $result = db_query("SELECT d.*, u.userid FROM diaries d LEFT JOIN users u ON d.author = u.id ORDER BY d.timestamp DESC LIMIT $num");
-
-  $output .= "<P>This part of the website is dedicated to providing easy to write and easy to read online diaries or journals filled with daily thoughts, poetry, boneless blather, spiritual theories, intimate details, valuable experiences, cynical rants, semi-coherent comments, writing experiments, artistic babblings, critics on actuality, fresh insights, diverse dreams, chronicles and general madness available for general human consumption.</P>";
-
-  while ($diary = db_fetch_object($result)) {
-    if ($time != date("F jS", $diary->timestamp)) {
-      $output .= "<B>". date("l, F jS", $diary->timestamp) ."</B>\n";
-      $time = date("F jS", $diary->timestamp);
-    }
-    $output .= "<DL>\n";
-    $output .= " <DD><P><B>$diary->userid wrote:</B></P></DD>\n";
-    $output .= " <DL>\n";
-    $output .= "  <DD><P>". check_output($diary->text, 1) ."</P><P>[ <A HREF=\"diary.php?op=view&name=$diary->userid\">more</A> ]</P></DD>\n";
-    $output .= " </DL>\n";
-    $output .= "</DL>\n";
-  }
-
-  $theme->header();
-  $theme->box("Online diary", $output);
-  $theme->footer();
-
-}
-
-function diary_entry($timestamp, $text, $id = 0) {
-  if ($id) {
-    $output .= "<DL>\n";
-    $output .= " <DT><B>". date("l, F jS", $timestamp) .":</B> </DT>\n";
-    $output .= " <DD><P>[ <A HREF=\"diary.php?op=edit&id=$id\">edit</A> ]</P><P>". check_output($text, 1) ."</P></DD>\n";
-    $output .= "</DL>\n";
-  }
-  else {
-    $output .= "<DL>\n";
-    $output .= " <DT><B>". date("l, F jS", $timestamp) .":</B></DT>\n";
-    $output .= " <DD><P>". check_output($text, 1) ."</P></DD>\n";
-    $output .= "</DL>\n";
-  }
-  return $output;
-}
-
-function diary_display($username) {
-  global $theme, $user;
-
-  $result = db_query("SELECT d.*, u.userid FROM diaries d LEFT JOIN users u ON d.author = u.id WHERE u.userid = '$username' ORDER BY timestamp DESC");
-
-  if ($username == $user->userid) {
-    $output .= diary_entry(time(), "<BIG><A HREF=\"diary.php?op=add\">Add new diary entry!</A></BIG><P>");
-    while ($diary = db_fetch_object($result)) $output .= diary_entry($diary->timestamp, $diary->text, $diary->id);
-  }
-  else {
-    $output .= "<P>". format_username($username) ."'s diary:</P>\n";
-    while ($diary = db_fetch_object($result)) $output .= diary_entry($diary->timestamp, $diary->text);
-  }
-
-  $theme->header();
-  $theme->box("$username's online diary", $output);
-  $theme->footer();
-}
-
-function diary_add() {
-  global $theme, $user, $allowed_html;
-  
-  $output .= "<FORM ACTION=\"diary.php\" METHOD=\"post\">\n";
-
-  $output .= "<P>\n"; 
-  $output .= " <B>Enter new diary entry:</B><BR>\n";
-  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\" MAXLENGTH=\"20\"></TEXTAREA><BR>\n";
-  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
-  $output .= "</P>\n";
-
-  $output .= "<P>\n";
-  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\">\n";
-  $output .= "</P>\n";
-
-  $output .= "</FORM>\n";
-  
-  $theme->header();
-  $theme->box("Online diary", $output);
-  $theme->footer();
-}
-
-function diary_edit($id) {
-  global $theme, $user, $allowed_html;
-
-  $result = db_query("SELECT * FROM diaries WHERE id = $id");
-  $diary = db_fetch_object($result);
-
-  $output .= diary_entry($diary->timestamp, $diary->text);
-
-  $output .= "<FORM ACTION=\"diary.php\" METHOD=\"post\">\n";
-
-  $output .= "<P>\n";
-  $output .= " <B>Edit diary entry:</B><BR>\n";
-  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\">". check_input($diary->text) ."</TEXTAREA><BR>\n";
-  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
-  $output .= "</P>\n";
-
-  $output .= "<P>\n";
-  $output .= " <INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$diary->id\">\n";
-  $output .= " <INPUT TYPE=\"hidden\" NAME=\"timestamp\" VALUE=\"$diary->timestamp\">\n";
-  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\"> <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Submit diary entry\">\n";
-  $output .= "</P>\n";
-
-  $output .= "</FORM>\n";
-  
-  $theme->header();
-  $theme->box("Online diary", $output);
-  $theme->footer();
-}
-
-function diary_preview($text, $timestamp, $id = 0) {
-  global $theme, $user, $allowed_html;
-
-  $output .= diary_entry($timestamp, $text);
-
-  $output .= "<FORM ACTION=\"diary.php\" METHOD=\"post\">\n";
-
-  $output .= "<P>\n";
-  $output .= " <B>Preview diary entry:</B><BR>\n";
-  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\">". check_output($text) ."</TEXTAREA><BR>\n";
-  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
-  $output .= "</P>\n";
-
-  $output .= "<P>\n";
-  $output .= " <INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$id\">\n";
-  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\">\n";
-  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Submit diary entry\">\n";
-  $output .= "</P>\n";
-
-  $output .= "</FORM>\n";
-
-  $theme->header();
-  $theme->box("Online diary", $output);
-  $theme->footer();
-}
-
-function diary_submit($text, $id = 0) {
-  global $user, $theme;
-
-  if ($id) {
-    watchdog("message", "old diary entry updated");
-    db_query("UPDATE diaries SET text =  '". check_input($text) ."' WHERE id = $id");
-  }
-  else {
-    watchdog("diary", "new diary entry added");
-    db_query("INSERT INTO diaries (author, text, timestamp) VALUES ('$user->id', '". check_input($text) ."', '". time() ."')");
-  }
-
-  header("Location: diary.php?op=view&name=$user->userid");
-}
-
-### Security check:
-if (strstr($id, " ") || strstr($name, " ")) {
-  watchdog("error", "diary: attempt to provide malicious input through URI");
-  exit();
-}
-
-switch($op) {
-  case "add":
-    diary_add();
-    break;
-  case "edit":
-    diary_edit($id);
-    break;
-  case "view":
-    diary_display($name);
-    break;
-  case "Preview diary entry":
-    if ($id) diary_preview($text, $timestamp, $id);
-    else diary_preview($text, time());
-    break;
-  case "Submit diary entry":
-    if ($id) diary_submit($text, $id);
-    else diary_submit($text);
-    break;
-  default:
-    diary_overview();
-}
-
-?>
diff --git a/error.php b/error.php
index cae1a1a6f009..c75e55edb15b 100644
--- a/error.php
+++ b/error.php
@@ -7,7 +7,7 @@ function error_flood() {
 } 
 
 function error_httpd() {
-  global $REDIRECT_STATUS, $REDIRECT_URL, $HTTP_REFERER;
+  global $REDIRECT_STATUS, $REDIRECT_URL, $HTTP_REFERER, $HTTP_USER_AGENT;
 
   switch($REDIRECT_STATUS) {
     case 500:
@@ -29,7 +29,7 @@ function error_httpd() {
       $message = "unknown error";
   }
 
-  watchdog("error", "message: `$message' - requested url: $REDIRECT_URL - referring url: $HTTP_REFERER");
+  watchdog("error", "message: `$message' - requested url: $REDIRECT_URL - referring url: $HTTP_REFERER - user agent: $HTTP_USER_AGENT");
  
   print "<PRE>\n";
   print "<H1>Oops, an error occured!</H1>\n";
diff --git a/includes/calendar.inc b/includes/calendar.inc
deleted file mode 100644
index 3748c36997eb..000000000000
--- a/includes/calendar.inc
+++ /dev/null
@@ -1,76 +0,0 @@
-<?
-
-class Calendar {
-  var $date;
-
-  function calendar($date) {
-    $this->date = $date;
-  }
-
-  function display() {
-    global $PHP_SELF;
-
-    ### Extract information from the given date:
-    $month  = date("n", $this->date);
-    $year = date("Y", $this->date);
-    $day = date("d", $this->date);
-
-    ### Extract first day of the month:
-    $first = date("w", mktime(0, 0, 0, $month, 1, $year));
-        
-    ### Extract last day of the month:
-    $last = date("t", mktime(0, 0, 0, $month, 1, $year));
-
-    ### Calculate previous and next months dates:
-    $prev = mktime(0, 0, 0, $month - 1, $day, $year);
-    $next = mktime(0, 0, 0, $month + 1, $day, $year);
-
-    ### Generate calendar header:
-    $output .= "\n<!-- calendar -->\n";
-    $output .= "<TABLE WIDTH=\"100%\" BORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"1\">\n";
-    $output .= " <TR><TD ALIGN=\"center\" COLSPAN=\"7\"><SMALL><A HREF=\"$PHP_SELF?date=$prev\">&lt;</A> &nbsp; ". date("F Y", $this->date) ." &nbsp; <A HREF=\"$PHP_SELF?date=$next\">&gt;</A></SMALL></TD></TR>\n";
-    $output .= " <TR><TD ALIGN=\"center\"><SMALL>S</SMALL></TD><TD ALIGN=\"center\"><SMALL>M</SMALL></TD><TD ALIGN=\"center\"><SMALL>T</SMALL></TD><TD ALIGN=\"center\"><SMALL>W</SMALL></TD><TD ALIGN=\"center\"><SMALL>T</SMALL></TD><TD ALIGN=\"center\"><SMALL>F</SMALL></TD><TD ALIGN=\"center\"><SMALL>S</SMALL></TD></TR>\n";
- 
-    ### Initialize temporary variables:
-    $nday = 1;
-    $sday = $first;
-   
-    ### Loop through all the days of the month:
-    while ($nday <= $last) {
-      ### Set up blank days for first week of the month:
-      if ($first) {
-        $output .= " <TR><TD COLSPAN=\"$first\">&nbsp</TD>\n";
-        $first = 0;
-      }
-        
-      ### Start every week on a new line:
-      if ($sday == 0) $output .=  " <TR>\n";
-    
-      ### Print one cell:
-      $date = mktime(24, 0, 0, $month, $nday, $year);
-      if ($nday == $day) $output .= "  <TD ALIGN=\"center\"><SMALL><B>$nday</B></SMALL></TD>\n";
-      else if ($date > time()) $output .= "  <TD ALIGN=\"center\"><SMALL>$nday</SMALL></TD>\n";
-      else $output .= "  <TD ALIGN=\"center\"><SMALL><A HREF=\"$PHP_SELF?date=$date\" STYLE=\"text-decoration: none;\">$nday</A></SMALL></TD>\n";
-     
-      ### Start every week on a new line:
-      if ($sday == 6) $output .=  " </TR>\n";
-        
-      ### Update temporary variables:
-      $sday++;
-      $sday = $sday % 7;
-      $nday++;
-    }
-    
-    ### Complete the calendar:
-    if ($sday) {
-      $end = 7 - $sday;
-      $output .= "  <TD COLSPAN=\"$end\">&nbsp;</TD>\n </TR>\n";
-    }
-    $output .= "</TABLE>\n\n";
-
-    ### Return calendar:
-    return $output;
-  }
-}
-
-?>
diff --git a/includes/cron.inc b/includes/cron.inc
index 59ff0a07cd32..4cfb74fb866d 100644
--- a/includes/cron.inc
+++ b/includes/cron.inc
@@ -1,24 +1,41 @@
 <?
 
-function cron_add($name, $help, $code, $scheduled, $message = "") {
-  if (empty($name) || empty($code) || empty($scheduled)) {
+include "includes/theme.inc";
+
+function cron_set($module, $scheduled, $message = "") {
+  if (empty($module) || empty($scheduled)) {
     $message = "failed: information missing";
   }
+  else if (db_fetch_object(db_query("SELECT * FROM cron WHERE module = '$module'"))) {
+    db_query("UPDATE cron SET scheduled = $scheduled WHERE module = '$module'");
+  }
   else {
-    $result = db_query("INSERT INTO cron (name, help, code, scheduled) VALUES ('". check_input($name) ."', '". check_input($help) ."', '". check_code($code) ."', '". check_input($scheduled) ."')");
+    db_query("INSERT INTO cron (module, scheduled, timestamp) VALUES ('". check_input($module) ."', '". check_input($scheduled) ."', '42')");
   } 
 }
 
-function cron_delete($id) {
-  $result = db_query("DELETE FROM cron WHERE id = $id");
+function cron_get($module) {
+  return db_fetch_object(db_query("SELECT * FROM cron WHERE module = '$module'"), 0);
+}
+
+function cron_delete($module) {
+  $result = db_query("DELETE FROM cron WHERE module = '$module'");
 }
 
-function cron_execute($cron) {
-  eval($cron->code);
+function cron_run($cron) {
+  $time = time();
   
-  db_query("UPDATE cron SET timestamp = '". time() ."' WHERE id = $cron->id");
+  $result = db_query("SELECT * FROM cron WHERE $time - timestamp > scheduled");
+
+  while ($task = db_fetch_object($result)) {
+    include "modules/". $task->module .".module";
+    if ($function = $module["cron"]) {
+      watchdog("message", "cron: executed '". $task->module ."_cron()'"); 
+      $function();
+    }
+  }
 
-  watchdog("message", "cron: executed '$cron->name'"); 
+  db_query("UPDATE cron SET timestamp = $time WHERE $time - timestamp > scheduled");
 }
 
 ?>
diff --git a/includes/function.inc b/includes/function.inc
index 7c1afc7aed45..87fb0ee37abb 100644
--- a/includes/function.inc
+++ b/includes/function.inc
@@ -61,6 +61,25 @@ function format_plural($count, $singular, $plural) {
   return ($count == 1) ? "$count $singular" : "$count $plural";
 }
 
+function format_interval($timestamp) {
+  if ($timestamp > 86400) {
+    $output .= format_plural(floor($timestamp / 86400), "day ", "days ");
+    $timestamp = $timestamp % 86400;
+  }
+  if ($timestamp > 3600) {
+    $output .= format_plural(floor($timestamp / 3600), "hour ", "hours ");
+    $timestamp = $timestamp % 3600;
+  }
+  if ($timestamp > 60) {
+    $output .= floor($timestamp / 60) ." min ";
+    $timestamp = $timestamp % 60;
+  }
+  if ($timestamp > 0) {
+    $output .= floor($timestamp / 86400) ." sec";
+  }
+  return $output;
+}
+
 function format_date($timestamp, $type = "medium") {
   global $user;
 
diff --git a/includes/config.inc b/includes/hostname.conf
similarity index 85%
rename from includes/config.inc
rename to includes/hostname.conf
index 87726021f574..3afc805172c6 100644
--- a/includes/config.inc
+++ b/includes/hostname.conf
@@ -4,23 +4,12 @@
 # Database settings:
 #
 
-### host: "http://www.drop.org/":
-$db_host = "zind.net";
-$db_name = "droporg";
-$db_pass = "DropIes";
-$db_name = "droporg";
-
-### host: "http://beta.drop.org/":
-#$db_host = "zind.net";
-#$db_name = "dries";
-#$db_pass = "Abc123";
-#$db_name = "dries";
-
 ### host: "http://localhost/":
-#$db_host = "localhost";
-#$db_name = "drop";
-#$db_pass = "drop";
-#$db_name = "drop";
+$db_host = "localhost";
+$db_name = "name";
+$db_pass = "pass";
+$db_name = "name";
+
 
 #
 # Administrative information
@@ -37,7 +26,6 @@
 #   represent the mathematical calculation to be performed 
 #   to update a comment's value.
 #
-
 $comment_votes = array("none" => "none", 
                        "-1"   => "- 1", 
                        "0"    => "+ 0",
@@ -78,18 +66,15 @@
 #   the first theme listed in this associative array will
 #   automatically become the default theme.
 #
-$themes = array("Marvin"  => array(
+$themes = array("UnConeD" => array(
+		   "themes/unconed/unconed.theme",
+		   "modern theme, gray and blue, high coolness factor"),
+                "Marvin"  => array(
                    "themes/marvin/marvin.theme", 
                    "classic theme, white, basic design with a fresh look"),
                 "Zaphod"  => array(
                    "themes/zaphod/zaphod.theme", 
-                   "classic theme, yellow, structured, advanced navigation"),
-                "Trillian"  => array(
-                   "themes/trillian/trillian.theme", 
-                   "simple, easy, black background"),
-		"UnConeD" => array(
-		   "themes/unconed/unconed.theme",
-		   "modern theme, gray and blue, high coolness factor"));
+                   "classic theme, yellow, structured, advanced navigation"));
 
 #
 # Submission moderation votes:
@@ -112,7 +97,7 @@
 #   story is pushed over the threshold and up it goes on the public 
 #   page.  On the other hand, when too many people voted to drop a 
 #   story, the story will get trashed.
-$submission_post_threshold = "3";
+$submission_post_threshold = "5";
 $submission_dump_threshold = "-2";
 
 #
diff --git a/includes/widget.inc b/includes/widget.inc
index b5891a57ea2e..2ea57a2469ed 100644
--- a/includes/widget.inc
+++ b/includes/widget.inc
@@ -80,9 +80,9 @@ function display_new_diaries($theme, $num = 20) {
       $content .= "<P><B>". date("l, M jS", $diary->timestamp) ."</B></P>\n";
       $time = date("F jS", $diary->timestamp);
     }
-    $content .= "<LI><A HREF=\"diary.php?op=view&name=$diary->userid\">$diary->userid</A></LI>\n";
+    $content .= "<LI><A HREF=\"module.php?mod=diary&op=view&name=$diary->userid\">$diary->userid</A></LI>\n";
   }
-  $content .= "<P ALIGN=\"right\">[ <A HREF=\"diary.php\"><FONT COLOR=\"$theme->hlcolor2\">more</FONT></A> ]</P>";
+  $content .= "<P ALIGN=\"right\">[ <A HREF=\"module.php?mod=diary\"><FONT COLOR=\"$theme->hlcolor2\">more</FONT></A> ]</P>";
   $theme->box("Recent diary entries", $content);
 }
 
@@ -96,12 +96,6 @@ function display_new_headlines($theme, $num = 10) {
   $theme->box("Latest headlines", $content);
 }
 
-function display_calendar($theme, $date) {
-  include "includes/calendar.inc";
-  $calendar = new Calendar($date);
-  $theme->box("Browse archives", $calendar->display());
-}
-
 function display_account($theme) {
   global $user, $site_name;
   
diff --git a/modules/account.module b/modules/account.module
index 7169da5ff7ce..11f69fb16a51 100644
--- a/modules/account.module
+++ b/modules/account.module
@@ -1,6 +1,11 @@
 <?
 
-$module = array("admin" => "account_admin");
+$module = array("cron"  => "account_cron",
+                "admin" => "account_admin");
+
+function account_cron() {
+  // clean-up user database
+}
 
 function account_display($order = "username") {
   $sort = array("ID" => "id", "fake e-mail address" => "fake_email", "homepage" => "url", "hostname" => "last_host", "last access date" => "last_access", "real e-mail address" => "real_email", "real name" => "name", "status" => "status", "theme" => "theme", "timezone" => "timezone", "username" => "userid");
@@ -122,4 +127,4 @@ function account_admin() {
   }
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/modules/backend.module b/modules/backend.module
index 003619e58c33..43a2211a6ef6 100644
--- a/modules/backend.module
+++ b/modules/backend.module
@@ -1,6 +1,7 @@
 <?
 
 $module = array("page"  => "backend_page",
+                "cron"  => "backend_cron",
                 "admin" => "backend_admin");
 
 include "includes/theme.inc";
@@ -42,6 +43,9 @@ function backend_page() {
   $theme->footer();
 }
 
+function backend_cron() {
+  // update backends
+}
 
 function backend_admin_main() {
   global $theme;
@@ -50,7 +54,7 @@ function backend_admin_main() {
   $result = db_query("SELECT * FROM channel ORDER BY id");
 
   $output .= "<TABLE BORDER=\"1\" CELLSPADDING=\"2\" CELLSPACING=\"2\">\n";
-  $output .= " <TH>Site</TH><TH>Contact</TH><TH>Last updated</TH><TH COLSPAN=\"2\">Operations</TH></TR>\n";
+  $output .= " <TH>site</TH><TH>contact</TH><TH>last updated</TH><TH COLSPAN=\"2\">operations</TH></TR>\n";
 
   while ($channel = db_fetch_object($result)) {
     // Load backend from database:
diff --git a/modules/cron.module b/modules/cron.module
index b54b1fdcbe37..b242388dbed3 100644
--- a/modules/cron.module
+++ b/modules/cron.module
@@ -2,23 +2,37 @@
 
 $module = array("admin" => "cron_admin");
 
-function cron_admin() {
+include_once "includes/function.inc";
+
+
+function cron_reset($name) {
+  cron_delete($name);
+}
+
+function cron_display() {
   // Perform query:
   $result = db_query("SELECT * FROM cron");
  
   // Generate output:
+  $output .= "<TABLE BORDER=\"1\" CELLPADDING=\"2\" CELLSPACING=\"2\">\n";
+  $output .= " <TR><TH>module</TH><TH>interval</TH><TH>last exection</TH><TH COLSPAN=\"2\">operations</TH></TR>\n";
   while ($cron = db_fetch_object($result)) {
-    $output .= "<TABLE BORDER=\"1\" CELLPADDING=\"2\" CELLSPACING=\"2\">\n";
-    $output .= " <TR><TH VALIGN=\"top\">Name:</TH><TD>". check_output($cron->name) ."</TD></TR>\n";
-    $output .= " <TR><TH VALIGN=\"top\">Help:</TH><TD>". check_output($cron->help) ."</TD></TR>\n";
-    $output .= " <TR><TH VALIGN=\"top\">Code:</TH><TD><CODE>". nl2br($cron->code) ."</CODE></TD></TR>\n";
-    $output .= " <TR><TH VALIGN=\"top\">Last run:</TH><TD>". format_date($cron->timestamp) ."</TD></TR>\n";
-    $output .= " <TR><TH VALIGN=\"top\">Scheduled:</TH><TD>every $cron->scheduled seconds</TD></TR>\n";
-    $output .= "</TABLE>\n";
-    $output .= "<BR><BR>\n";
+    $output .= " <TR><TD>". check_output($cron->module) ."</TD><TD>every ". format_interval($cron->scheduled) ."</TD><TD>". format_interval(time() - $cron->timestamp) ." ago</TD><TD ALIGN=\"center\"><A HREF=\"cron.php\">execute</A></TD><TD><A HREF=\"admin.php?mod=cron&op=reset&name=$cron->module\">reset</A></TD></TR>\n";
   }
-
+  $output .= "</TABLE>\n";
   print $output;
 }
 
+function cron_admin() {
+  global $op, $name;
+
+  switch($op) {
+    case "reset":
+      cron_reset($name);
+      break;
+  }
+  
+  cron_display();
+}
+
 ?>
diff --git a/modules/diary.module b/modules/diary.module
index da92e746d2d7..92c97d5f37b7 100644
--- a/modules/diary.module
+++ b/modules/diary.module
@@ -1,6 +1,197 @@
 <?
 
-function diary_edit($id) {
+$module = array("page"  => "diary_page",
+                "admin" => "diary_admin");
+
+
+include "includes/theme.inc";
+
+function diary_page_overview($num = 20) {
+  global $theme, $user;
+
+  $result = db_query("SELECT d.*, u.userid FROM diaries d LEFT JOIN users u ON d.author = u.id ORDER BY d.timestamp DESC LIMIT $num");
+
+  $output .= "<P>This part of the website is dedicated to providing easy to write and easy to read online diaries or journals filled with daily thoughts, poetry, boneless blather, spiritual theories, intimate details, valuable experiences, cynical rants, semi-coherent comments, writing experiments, artistic babblings, critics on actuality, fresh insights, diverse dreams, chronicles and general madness available for general human consumption.</P>";
+
+  while ($diary = db_fetch_object($result)) {
+    if ($time != date("F jS", $diary->timestamp)) {
+      $output .= "<B>". date("l, F jS", $diary->timestamp) ."</B>\n";
+      $time = date("F jS", $diary->timestamp);
+    }
+    $output .= "<DL>\n";
+    $output .= " <DD><P><B>$diary->userid wrote:</B></P></DD>\n";
+    $output .= " <DL>\n";
+    $output .= "  <DD><P>". check_output($diary->text, 1) ."</P><P>[ <A HREF=\"module.php?mod=diary&op=view&name=$diary->userid\">more</A> ]</P></DD>\n";
+    $output .= " </DL>\n";
+    $output .= "</DL>\n";
+  }
+
+  $theme->header();
+  $theme->box("Online diary", $output);
+  $theme->footer();
+
+}
+
+function diary_page_entry($timestamp, $text, $id = 0) {
+  if ($id) {
+    $output .= "<DL>\n";
+    $output .= " <DT><B>". date("l, F jS", $timestamp) .":</B> </DT>\n";
+    $output .= " <DD><P>[ <A HREF=\"module.php?mod=diary&op=edit&id=$id\">edit</A> ]</P><P>". check_output($text, 1) ."</P></DD>\n";
+    $output .= "</DL>\n";
+  }
+  else {
+    $output .= "<DL>\n";
+    $output .= " <DT><B>". date("l, F jS", $timestamp) .":</B></DT>\n";
+    $output .= " <DD><P>". check_output($text, 1) ."</P></DD>\n";
+    $output .= "</DL>\n";
+  }
+  return $output;
+}
+
+function diary_page_display($username) {
+  global $theme, $user;
+
+  $result = db_query("SELECT d.*, u.userid FROM diaries d LEFT JOIN users u ON d.author = u.id WHERE u.userid = '$username' ORDER BY timestamp DESC");
+
+  if ($username == $user->userid) {
+    $output .= diary_page_entry(time(), "<BIG><A HREF=\"module.php?mod=diary&op=add\">Add new diary entry!</A></BIG><P>");
+    while ($diary = db_fetch_object($result)) $output .= diary_page_entry($diary->timestamp, $diary->text, $diary->id);
+  }
+  else {
+    $output .= "<P>". format_username($username) ."'s diary:</P>\n";
+    while ($diary = db_fetch_object($result)) $output .= diary_page_entry($diary->timestamp, $diary->text);
+  }
+
+  $theme->header();
+  $theme->box("$username's online diary", $output);
+  $theme->footer();
+}
+
+function diary_page_add() {
+  global $theme, $user, $allowed_html;
+  
+  $output .= "<FORM ACTION=\"module.php?mod=diary\" METHOD=\"post\">\n";
+
+  $output .= "<P>\n"; 
+  $output .= " <B>Enter new diary entry:</B><BR>\n";
+  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\" MAXLENGTH=\"20\"></TEXTAREA><BR>\n";
+  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
+  $output .= "</P>\n";
+
+  $output .= "<P>\n";
+  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\">\n";
+  $output .= "</P>\n";
+
+  $output .= "</FORM>\n";
+  
+  $theme->header();
+  $theme->box("Online diary", $output);
+  $theme->footer();
+}
+
+function diary_page_edit($id) {
+  global $theme, $user, $allowed_html;
+
+  $result = db_query("SELECT * FROM diaries WHERE id = $id");
+  $diary = db_fetch_object($result);
+
+  $output .= diary_page_entry($diary->timestamp, $diary->text);
+
+  $output .= "<FORM ACTION=\"module.php?mod=diary\" METHOD=\"post\">\n";
+
+  $output .= "<P>\n";
+  $output .= " <B>Edit diary entry:</B><BR>\n";
+  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\">". check_input($diary->text) ."</TEXTAREA><BR>\n";
+  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
+  $output .= "</P>\n";
+
+  $output .= "<P>\n";
+  $output .= " <INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$diary->id\">\n";
+  $output .= " <INPUT TYPE=\"hidden\" NAME=\"timestamp\" VALUE=\"$diary->timestamp\">\n";
+  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\"> <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Submit diary entry\">\n";
+  $output .= "</P>\n";
+
+  $output .= "</FORM>\n";
+  
+  $theme->header();
+  $theme->box("Online diary", $output);
+  $theme->footer();
+}
+
+function diary_page_preview($text, $timestamp, $id = 0) {
+  global $theme, $user, $allowed_html;
+
+  $output .= diary_page_entry($timestamp, $text);
+
+  $output .= "<FORM ACTION=\"module.php?mod=diary\" METHOD=\"post\">\n";
+
+  $output .= "<P>\n";
+  $output .= " <B>Preview diary entry:</B><BR>\n";
+  $output .= " <TEXTAREA WRAP=\"virtual\" COLS=\"50\" ROWS=\"15\" NAME=\"text\">". check_output($text) ."</TEXTAREA><BR>\n";
+  $output .= " <SMALL><I>Allowed HTML tags: ". htmlspecialchars($allowed_html) .".</I></SMALL>\n";
+  $output .= "</P>\n";
+
+  $output .= "<P>\n";
+  $output .= " <INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$id\">\n";
+  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Preview diary entry\">\n";
+  $output .= " <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Submit diary entry\">\n";
+  $output .= "</P>\n";
+
+  $output .= "</FORM>\n";
+
+  $theme->header();
+  $theme->box("Online diary", $output);
+  $theme->footer();
+}
+
+function diary_page_submit($text, $id = 0) {
+  global $user, $theme;
+
+  if ($id) {
+    watchdog("message", "old diary entry updated");
+    db_query("UPDATE diaries SET text =  '". check_input($text) ."' WHERE id = $id");
+  }
+  else {
+    watchdog("diary", "new diary entry added");
+    db_query("INSERT INTO diaries (author, text, timestamp) VALUES ('$user->id', '". check_input($text) ."', '". time() ."')");
+  }
+
+  header("Location: module.php?mod=diary&op=view&name=$user->userid");
+}
+
+function diary_page() {
+  global $op, $id, $name, $text, $timestamp;
+
+  // Security check:
+  if (strstr($id, " ") || strstr($name, " ")) {
+    watchdog("error", "diary: attempt to provide malicious input through URI");
+    exit();
+  }
+
+  switch($op) {
+    case "add":
+      diary_page_add();
+      break;
+   case "edit":
+      diary_page_edit($id);
+      break;
+    case "view":
+      diary_page_display($name);
+      break;
+    case "Preview diary entry":
+      if ($id) diary_page_preview($text, $timestamp, $id);
+      else diary_page_preview($text, time());
+      break;
+    case "Submit diary entry":
+      if ($id) diary_page_submit($text, $id);
+      else diary_page_submit($text);
+      break;
+    default:
+      diary_page_overview();
+  }
+}
+
+function diary_admin_edit($id) {
   $result = db_query("SELECT d.*, u.userid FROM diaries d LEFT JOIN users u ON d.author = u.id WHERE d.id = $id");
 
   $diary = db_fetch_object($result);
@@ -25,12 +216,12 @@ function diary_edit($id) {
   print $output;
 }
 
-function diary_save($id, $text) {
+function diary_admin_save($id, $text) {
   db_query("UPDATE diaries SET text = '". check_input($text) ."' WHERE id = $id");
   watchdog("message", "modified diary entry #$id.");
 }
 
-function diary_display($order = "date") {
+function diary_admin_display($order = "date") {
   // Initialize variables:
   $fields = array("author" => "author", "date" => "timestamp DESC");
 
@@ -59,7 +250,7 @@ function diary_display($order = "date") {
   $output .= " </TR>\n";
 
   while ($diary = db_fetch_object($result)) {
-    $output .= " <TR><TD><A HREF=\"diary.php?op=view&name=$diary->userid\">$diary->userid on ". format_date($diary->date, "small") ."</A></TD><TD>". format_username($diary->userid, 1) ."</TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=diary&op=edit&id=$diary->id\">edit</A></TD></TR>\n";
+    $output .= " <TR><TD><A HREF=\"module.php?mod=diary&op=view&name=$diary->userid\">$diary->userid on ". format_date($diary->date, "small") ."</A></TD><TD>". format_username($diary->userid, 1) ."</TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=diary&op=edit&id=$diary->id\">edit</A></TD></TR>\n";
   }
 
   $output .= "</TABLE>\n";
@@ -73,17 +264,17 @@ function diary_admin() {
  
   switch ($op) {
     case "edit":
-      diary_edit($id);
+      diary_admin_edit($id);
       break;
     case "Save diary entry":
-      diary_save($id, $text);
-      diary_edit($id);
+      diary_admin_save($id, $text);
+      diary_admin_edit($id);
       break;
     case "Update":
-      diary_display($order);
+      diary_admin_display($order);
       break;
     default:
-      diary_display();
+      diary_admin_display();
   }
 }
 
diff --git a/modules/documentation.module b/modules/documentation.module
index a72f2609b023..268e49347ba8 100644
--- a/modules/documentation.module
+++ b/modules/documentation.module
@@ -32,7 +32,7 @@ function documentation_page() {
    -rw-rw-r--    1 drop    drop        includes/www.yourdomain1.com.conf
    -rw-rw-r--    1 drop    drop        includes/www.yourdomain2.com.conf
   </PRE>
-  <P>The only thing left to be done is to setup the corresponding vhosts in your Apache configuration file.  Note that the <CODE>DocumentRoot</CODE> points to the same source tree:</P>
+  <P>The only thing left to be done is to setup the corresponding vhosts in your Apache configuration file.  Note that the <CODE>DocumentRoot</CODE> points to the same source tree twice:</P>
   <PRE>
    NameVirtualHost 127.0.0.1
 
@@ -70,15 +70,15 @@ function documentation_page() {
    </TR>
    <TR>
     <TD VALIGN="top"><CODE>page</CODE></TD>
-    <TD VALIGN="top">If a module requires it's own page, typically exported as <CODE>http://www.yourdomain.com/module.php?mod=module</CODE>, the engine will invoke <CODE>module_page</CODE> to generate a modules page.</TD>
+    <TD VALIGN="top">If a module requires it's own page it should provide a function named <CODE>module_page</CODE>.  The page can then be publicly accessed via <CODE>http://www.yourdomain.com/module.php?mod=module</CODE> which will cause the engine to invoke <CODE>module_page</CODE> in order to generate the module's page.</TD>
    </TR>
    <TR>
-    <TD VALIGN="top"></TD>
-    <TD VALIGN="top"></TD>
+    <TD VALIGN="top"><CODE>cron</CODE></TD>
+    <TD VALIGN="top">Modules that require to schedule some commands to be executed on regular intervals can implement the <CODE>cron</CODE> interface: the engine will then call <CODE>module_cron</CODE> at the appropriate intervals defined by the administrator.  This interface is particulary handy to implement timers or to automate certain tasks like for instance database maintainance, recalculation of settings or parameters, automatic mailings and so on.</TD>
    </TR>
    <TR>
-    <TD VALIGN="top"></TD>
-    <TD VALIGN="top"></TD>
+    <TD VALIGN="top"><CODE>admin</CODE></TD>
+    <TD VALIGN="top">If a module requires a spot in the administrator section it should implement <CODE>module_admin</CODE>.  The engine will automatically add a link to the administration menus and will call <CODE>module_admin</CODE> when this link is followed.  In order to make virtually any module maintainer's life easier, you don't have to worry about access rights or permissions for that matter.  The engine will only allow priveleged users to call exported <CODE>admin</CODE> functions.</TD>
    </TR>
    <TR>
     <TD VALIGN="top"></TD>
@@ -101,7 +101,7 @@ function documentation_page() {
 
   <P>The site can be almost entirely configured through a web interface by using <I>droplets</I>.  Simply put, droplets are small bit of PHP code which will get plugged into the engine or modules powering the site.  Droplets are typically used to link modules with the mainstream engine or to customize various aspects of the engine itself.</P>
   <P>If you know how to script in PHP, droplets are pretty simple to create.  Don't get your panties in a knot if you are not confident with PHP: simply use the standard droplets (i.e. those available by default) as they are just fine or ask an expert dropletber to help you creating custom droplets that fit your need.</P>
-  <P>Each droplet consist of a key of maximum 255 characters and an associated block of PHP code which can be as long as you want it to be.  You can use any piece of PHP code to make up a droplet.  A droplet's code is stored in the database and the engine or a particular module will use the key to find the associated piece of PHP code which will then be dynamically embedded in the engine or the module just-in-time for execution.</P>
+  <P>Each droplet consists of a key of maximum 255 characters and an associated block of PHP code which can be as long as you want it to be.  You can use any piece of PHP code to make up a droplet.  A droplet's code is stored in the database and the engine or a particular module will use the key to find the associated piece of PHP code which will then be dynamically embedded in the engine or the module just-in-time for execution.</P>
   <P>There are however some factors to keep in mind when using and creating droplets: droplets can be extremly useful and flexible, yet be dangerous and insecure if not properly used.  If you are not confident with PHP, SQL or even with the site engine for that matter, avoid experimenting with droplets because you can - and you probably will - corrupt your database or even break your site!</P>
   <P>Remember that the code within each droplet must be valid PHP code, including things like terminating statements with a semicolon so the parser won't die.  Therefore, it is highly recommended to test your droplets seperatly using a simple test script on top of a test database before migrating to your production environment running your real database.</P>
   <P>Note that you can use any global variables, such as configuration parameters within the scope of a droplet and keep in mind that variables that have been given values in a droplet will retain these values in the engine or module afterwards.</P>
@@ -122,7 +122,22 @@ function documentation_page() {
   </PRE>
   <P>For more in depth example, we recommand you to check any of the available modules and to go from there.</P>
   <P>As said above, you can virtually use any piece of PHP code in a droplet: you can declare and use functions, consult the SQL database, access configuration settings and so on.</P>
-  <?
-}
+  
+  <H3>cron</H3>
+ 
+  <P>Cron (wich stands for chronograph) is a periodic command scheduler: it executes commands at intervals specified in seconds.  It can be used to control the execution of daily, weekly and monthly jobs (or anything with a period of n seconds).   Automating tasks is one of the best ways to keep a system running smoothly, and if most of your administration does not require your direct involvement, cron is an ideal solution.</P>
+  <P>Note that cron does not guarantee that the commands will be executed at the specified interval.  However, the engine will make sure that the commands are run at the specified intervals as closely as possible.</P>
+  <P>When <CODE>http://www.yourdomain.com/cron.php</CODE> is accessed, cron will run: it queries the database for the jobs cron controls, and their periods in seconds.  If a certain task wasn't executed in the last n seconds, where n is the period of that job, it will be executed.  It then records the date in the database so it can know when to run it again.  When all the executed commands terminate, cron is done.</P>
+  <P>Cron is handy to run daily, weekly and monthly tasks that
+take care of various "housekeeping chores" such as database maintainance,
+recalculating settings, periodic mailings, scheduled backups and so on.</P>
+  <P>The recommanded way to setup drop.org's cron job is to setup a real UNIX <CODE>crontab</CODE> that frequently visits <CODE>http://www.yourdomain.com/cron.php</CODE>: the more you visit the <CODE>cron.php</CODE>, the more accurate cron can and will be.  If your hosting company does not allow you to use <CODE>crontab</CODE>, you can always ask someone else to setup a <CODE>crontab</CODE> for you.  Afterall, virtually any host machine with access to the internet can run the <CODE>crontab</CODE> for you.<P>
+  <P>For the <CODE>crontab</CODE>, use a browser like <CODE>lynx</CODE> or <CODE>wget</CODE> but make sure the process terminates: either use <CODE>/usr/bin/lynx -source http://www.yourdomain.com/cron.php</CODE> or <CODE>/usr/bin/wgeti -O /dev/null http://www.yourdomain.com/cron.php</CODE>.  Take a look at the example scripts in the <CODE>scripts</CODE>-directory and make sure to adjust them to your needs.</P>
+  <P>A good <CODE>crontab</CODE>-line to run the <CODE>cron</CODE>-script once every hour would be:</P>
+  <PRE>
+    00 * * * * /home/www/drop/scripts/cron-lynx
+  </PRE>
 
+ <?
+ }
 ?>
diff --git a/modules/watchdog.module b/modules/watchdog.module
index f60e4eaeb4f1..1b10f672eea1 100644
--- a/modules/watchdog.module
+++ b/modules/watchdog.module
@@ -1,6 +1,11 @@
 <?
 
-$module = array("admin" => "watchdog_admin");
+$module = array("cron" => "watchdog_cron", 
+                "admin" => "watchdog_admin");
+
+function watchdog_cron() {
+  watchdog_clean();
+}
 
 function watchdog_display($order = "date") {
   $colors = array("#D8BFD8", "#6495ED", "#6A5ADF", "#FFFFFF", "#FFA500", "#FF3C3C");
@@ -31,7 +36,7 @@ function watchdog_display($order = "date") {
   $output .= " </TR>\n";
 
   while ($watchdog = db_fetch_object($result)) {
-    $output .= " <TR BGCOLOR=\"". $colors[$watchdog->level] ."\"><TD>". format_date($watchdog->timestamp) ."</TD><TD>". substr(check_output($watchdog->message), 0, 44) ."</TD><TD ALIGN=\"center\">". format_username($watchdog->userid, 1) ."</A></TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=watchdog&op=view&id=$watchdog->id\">more</A></TD></TR>\n";
+    $output .= " <TR BGCOLOR=\"". $colors[$watchdog->level] ."\"><TD>". format_date($watchdog->timestamp) ."</TD><TD>". substr(check_output($watchdog->message), 0, 44) ."</TD><TD ALIGN=\"center\">". format_username($watchdog->userid, 1) ."</A></TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=watchdog&op=view&id=$watchdog->id\">details</A></TD></TR>\n";
   }
 
   $output .= "</TABLE>\n";
diff --git a/modules/watchdog/watchdog.module b/modules/watchdog/watchdog.module
index f60e4eaeb4f1..1b10f672eea1 100644
--- a/modules/watchdog/watchdog.module
+++ b/modules/watchdog/watchdog.module
@@ -1,6 +1,11 @@
 <?
 
-$module = array("admin" => "watchdog_admin");
+$module = array("cron" => "watchdog_cron", 
+                "admin" => "watchdog_admin");
+
+function watchdog_cron() {
+  watchdog_clean();
+}
 
 function watchdog_display($order = "date") {
   $colors = array("#D8BFD8", "#6495ED", "#6A5ADF", "#FFFFFF", "#FFA500", "#FF3C3C");
@@ -31,7 +36,7 @@ function watchdog_display($order = "date") {
   $output .= " </TR>\n";
 
   while ($watchdog = db_fetch_object($result)) {
-    $output .= " <TR BGCOLOR=\"". $colors[$watchdog->level] ."\"><TD>". format_date($watchdog->timestamp) ."</TD><TD>". substr(check_output($watchdog->message), 0, 44) ."</TD><TD ALIGN=\"center\">". format_username($watchdog->userid, 1) ."</A></TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=watchdog&op=view&id=$watchdog->id\">more</A></TD></TR>\n";
+    $output .= " <TR BGCOLOR=\"". $colors[$watchdog->level] ."\"><TD>". format_date($watchdog->timestamp) ."</TD><TD>". substr(check_output($watchdog->message), 0, 44) ."</TD><TD ALIGN=\"center\">". format_username($watchdog->userid, 1) ."</A></TD><TD ALIGN=\"center\"><A HREF=\"admin.php?mod=watchdog&op=view&id=$watchdog->id\">details</A></TD></TR>\n";
   }
 
   $output .= "</TABLE>\n";
diff --git a/modules/wishlist.module b/modules/wishlist.module
index cef706ccd0e6..bbc9e31bdb9e 100644
--- a/modules/wishlist.module
+++ b/modules/wishlist.module
@@ -29,13 +29,6 @@ function wishlist_page() {
     <UL>
      <LI>blocks and flexible block placement strategies by means of "layout managers" (cfr. Java) and tightly integrated with the module support</LI>
      <LI>post/edit hash - magic cookie: to prevent malicious external access and to prevent duplicate posts because of hitting the "reload" button</LI>
-     <LI>cron job emulation:</LI>
-      <UL>
-       <LI>auto. set default theme according to popularity</LI>
-       <LI>auto. database clean-up (e.g. history field)</LI>
-       <LI>auto. recalculate adaptive/dynamic settings like thresholds</LI>
-       <LI>auto. mail digests</LI>
-      </UL>
      <LI>URL validator</LI>
      <LI>more adaptive submission queue</LI>
      <LI>archive function</LI>
@@ -60,6 +53,7 @@ function wishlist_page() {
      <LI>messaging system between users</LI>
      <LI>voting polls</LI>
      <LI>daily/weekly e-mail digest - mailing list</LI>
+     <LI>story overview grouped by category</LI>
      <LI>e-commerce/shop</LI> 
     </UL>
  
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 000000000000..1f53798bb4fe
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/scripts/cron-lynx b/scripts/cron-lynx
new file mode 100644
index 000000000000..c17666d59690
--- /dev/null
+++ b/scripts/cron-lynx
@@ -0,0 +1,2 @@
+#!/bin/sh
+/usr/bin/lynx -source http://drop.org/cron.php > /dev/null 2>&1
diff --git a/submit.php b/submit.php
index 6e4512908769..579f010fac72 100644
--- a/submit.php
+++ b/submit.php
@@ -4,7 +4,7 @@ function submit_enter() {
   global $anonymous, $categories, $allowed_html, $theme, $user;
   
   ### Guidlines:
-  $output .= blob_get("submit_information");
+  $output .= droplet_get("submit_information");
 
   ### Submission form:
   $output .= "<FORM ACTION=\"submit.php\" METHOD=\"post\">\n";
@@ -135,7 +135,7 @@ function submit_submit($subject, $abstract, $article, $category) {
   
   ### Display confirmation message:
   $theme->header(); 
-  $theme->box("Thank you for your submission.", blob_get("sumbit_confirmation"));
+  $theme->box("Thank you for your submission.", droplet_get("submit_confirmation"));
   $theme->footer();
 }
 
diff --git a/themes/marvin/marvin.theme b/themes/marvin/marvin.theme
index 0ac8de30a425..076f0083a672 100644
--- a/themes/marvin/marvin.theme
+++ b/themes/marvin/marvin.theme
@@ -46,7 +46,7 @@ function header($title) {
         </TD>
        </TR>
        <TR>
-        <TD ALIGN="right" COLSPAN="2"><SMALL><A HREF="/">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="diary.php">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></SMALL></TD>
+        <TD ALIGN="right" COLSPAN="2"><SMALL><A HREF="/">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="module.php?mod=diary">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></SMALL></TD>
        </TR>
        <TR>
         <TD VALIGN="top" WIDTH="100%">
@@ -269,12 +269,9 @@ function footer() {
            ### Display account:
            display_account($this);
          }
-         elseif (strstr($PHP_SELF, "diary.php")) {
+         elseif (strstr($PHP_SELF, "module.php")) {
            ### Display account:
            display_account($this);
- 
-           ### Display new diary entries:
-           display_new_diaries($this);
          }
          elseif (strstr($PHP_SELF, "submission.php")) {
            ### Display account:
@@ -317,7 +314,7 @@ function footer() {
       </TR>
       <TR>
        <TD ALIGN="center" COLSPAN="3">
-        <SMALL>[ <A HREF="/">home</A> | <A HREF="moduled.php?mod=faq">faq</A> | <A HREF="diary.php">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A> ]</SMALL>
+        <SMALL>[ <A HREF="/">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="module.php?mod=diary">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A> ]</SMALL>
        </TD>
       </TR>
      </TABLE>
diff --git a/themes/unconed/unconed.theme b/themes/unconed/unconed.theme
index e6094b486b30..50c3166192b1 100644
--- a/themes/unconed/unconed.theme
+++ b/themes/unconed/unconed.theme
@@ -57,7 +57,7 @@ function header($title) {
        </TR>
        <TR>
         <TD COLSPAN="2" ALIGN="CENTER">
-         <TABLE BORDER="0" WIDTH="100%" CELLSPACING="0" CELLPADDING="0" BGCOLOR="<? echo $this->brcolor1; ?>"><TR><TD ALIGN="CENTER"><TABLE BORDER="0" WIDTH="100%" CELLSPACING="1" CELLPADDING="4"><TR><TD ALIGN="CENTER" BGCOLOR="<? echo $this->bgcolor2; ?>"><BIG><A HREF="index.php">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="diary.php">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></BIG></TD></TR></TABLE></TD></TR></TABLE>
+         <TABLE BORDER="0" WIDTH="100%" CELLSPACING="0" CELLPADDING="0" BGCOLOR="<? echo $this->brcolor1; ?>"><TR><TD ALIGN="CENTER"><TABLE BORDER="0" WIDTH="100%" CELLSPACING="1" CELLPADDING="4"><TR><TD ALIGN="CENTER" BGCOLOR="<? echo $this->bgcolor2; ?>"><BIG><A HREF="index.php">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="module.php?mod=diary">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></BIG></TD></TR></TABLE></TD></TR></TABLE>
         </TD>
        </TR>
        <TR><TD COLSPAN="2"><?
@@ -316,7 +316,7 @@ function footer() {
            display_account($this);
 
            ### Display calendar:
-           display_calendar($this, $date);
+           $this->box("Browse archives", droplet_get("calendar"));
 
            ### Display calendar:
            display_old_headlines($this);
@@ -383,7 +383,7 @@ function footer() {
        ?></TD></TR>
       <TR>
        <TD COLSPAN="2">
-        <TABLE BORDER="0" WIDTH="100%" CELLSPACING="0" CELLPADDING="0" BGCOLOR="<? echo $this->brcolor1; ?>"><TR><TD ALIGN="CENTER"><TABLE BORDER="0" WIDTH="100%" CELLSPACING="1" CELLPADDING="4"><TR><TD ALIGN="CENTER" BGCOLOR="<? echo $this->bgcolor2; ?>"><BIG><A HREF="index.php">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="diary.php">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></BIG></TD></TR></TABLE></TD></TR></TABLE>
+        <TABLE BORDER="0" WIDTH="100%" CELLSPACING="0" CELLPADDING="0" BGCOLOR="<? echo $this->brcolor1; ?>"><TR><TD ALIGN="CENTER"><TABLE BORDER="0" WIDTH="100%" CELLSPACING="1" CELLPADDING="4"><TR><TD ALIGN="CENTER" BGCOLOR="<? echo $this->bgcolor2; ?>"><BIG><A HREF="index.php">home</A> | <A HREF="module.php?mod=faq">faq</A> | <A HREF="module.php?mod=diary">diary</A> | <A HREF="search.php">search</A> | <A HREF="submit.php">submit news</A> | <A HREF="account.php">user account</A></BIG></TD></TR></TABLE></TD></TR></TABLE>
        </TD>
       </TR>
      </TABLE>
-- 
GitLab