diff -urN drupal-4.4.1/database/database.mysql drupal4blog/database/database.mysql
--- drupal-4.4.1/database/database.mysql	Sun May  2 23:45:29 2004
+++ drupal4blog/database/database.mysql	Tue Jun  8 14:19:14 2004
@@ -128,6 +128,9 @@
   status tinyint(3) unsigned NOT NULL default '0',
   thread varchar(255) NOT NULL,
   users longtext,
+  name varchar(60) default NULL,
+  mail varchar(60) default NULL,
+  homepage varchar(60) default NULL,
   PRIMARY KEY  (cid),
   KEY lid (nid)
 ) TYPE=MyISAM;
@@ -301,6 +304,18 @@
 ) TYPE=MyISAM;
 
 --
+-- Table structure for table 'blog'
+--
+
+CREATE TABLE blog (
+  nid int(10) unsigned NOT NULL default '0',
+  extended longtext NOT NULL,
+  excerpt longtext NOT NULL,
+  format tinyint(2) NOT NULL default '0',
+  PRIMARY KEY  (nid)
+) TYPE=MyISAM;
+
+--
 -- Table structure for table 'page'
 --
 
@@ -518,6 +533,7 @@
   status tinyint(4) NOT NULL default '0',
   timezone varchar(8) default NULL,
   language char(2) NOT NULL default '',
+  picture varchar(255) NOT NULL default '',
   init varchar(64) default '',
   data longtext,
   rid int(10) unsigned NOT NULL default '0',
@@ -570,29 +586,103 @@
 ) TYPE=MyISAM;
 
 --
+-- Table structure for table 'trackback'
+--
+
+CREATE TABLE trackback (
+  nid int(10) unsigned NOT NULL default '0',
+  pinged longtext,
+  toping longtext,
+  PRIMARY KEY  (nid)
+) TYPE=MyISAM;
+
+--
+-- Table structure for table 'trackback_pingme'
+--
+
+CREATE TABLE trackback_pingme (
+  nid int(10) unsigned not null,
+  cid int (10) unsigned not null
+) TYPE=MyISAM;
+ 
+
+--
+-- Table structure for table 'subscriptions'
+--
+
+CREATE TABLE subscriptions(
+  sid      int(10) unsigned not null,
+  uid      int(10) unsigned not null,
+  stype    varchar(4) not null
+) TYPE=MyISAM;
+
+--
+-- Table structure for table 'theme_simple'
+--
+
+CREATE TABLE theme_simple(
+  id        int(10) unsigned not null default '0',
+  name      varchar(32) not null,
+  php       longtext,
+  raw       longtext,
+  PRIMARY KEY  (id, name)
+) TYPE=MyISAM;
+
+--
 -- Insert some default values
 --
 
 INSERT INTO system VALUES ('modules/admin.module','admin','module','',1,0,0);
+INSERT INTO system VALUES ('modules/archive.module','archive','module','',1,0,0);
 INSERT INTO system VALUES ('modules/block.module','block','module','',1,0,0);
 INSERT INTO system VALUES ('modules/comment.module','comment','module','',1,0,0);
 INSERT INTO system VALUES ('modules/help.module','help','module','',1,0,0);
 INSERT INTO system VALUES ('modules/node.module','node','module','',1,0,0);
-INSERT INTO system VALUES ('modules/page.module','page','module','',1,0,0);
-INSERT INTO system VALUES ('modules/story.module','story','module','',1,0,0);
+INSERT INTO system VALUES ('modules/blog.module','blog','module','',1,0,0);
+INSERT INTO system VALUES ('modules/blogadmin.module','blogadmin','module','',1,0,0);
+INSERT INTO system VALUES ('modules/ping.module','ping','module','',1,0,0);
+INSERT INTO system VALUES ('modules/search.module','search','module','',1,0,0);
 INSERT INTO system VALUES ('modules/taxonomy.module','taxonomy','module','',1,0,0);
-INSERT INTO system VALUES ('themes/xtemplate/xtemplate.theme','xtemplate','theme','Internet explorer, Netscape, Opera',1,0,0);
+INSERT INTO system VALUES ('modules/profile.module','profile','module','',1,0,0);
+INSERT INTO system VALUES ('modules/tracker.module','tracker','module','',1,0,0);
+INSERT INTO system VALUES ('modules/trackback.module','trackback','module','',1,0,0);
+INSERT INTO system VALUES ('modules/upload.module', 'upload', 'module','',1,0,0);
+INSERT INTO system VALUES ('modules/blogapi.module', 'blogapi', 'module','',1,0,0);
+INSERT INTO system VALUES ('modules/path.module', 'path', 'module','',1,0,0);
+INSERT INTO system VALUES ('modules/urlfilter.module', 'urlfilter', 'module','',1,0,0);
+INSERT INTO system VALUES ('modules/subscriptions.module', 'subscriptions', 'module','',1,0,0);
+INSERT INTO system VALUES ('modules/tag.module', 'tag', 'module','',1,0,0);
+
+INSERT INTO system VALUES ('themes/simple/simple.theme','simple','theme','Internet explorer, Netscape, Opera',1,0,0);
+
 INSERT INTO users (uid, name, mail, rid) VALUES ('0', '', '', '1');
 
 INSERT INTO role (rid, name) VALUES (1, 'anonymous user');
-INSERT INTO permission VALUES (1,'access content',0);
+INSERT INTO permission VALUES (1,'access comments, access content, access trackback, post trackback, post comments, post comments without approval, download files (simple)',0);
 
 INSERT INTO role (rid, name) VALUES (2, 'authenticated user');
-INSERT INTO permission VALUES (2,'access comments, access content, post comments, post comments without approval',0);
+INSERT INTO permission VALUES (2,'access comments, access content, access trackback, post trackback, post comments, post comments without approval, download files (simple), maintain subscriptions',0);
+
+INSERT INTO role (rid, name) VALUES (3, 'blogger');
+INSERT INTO permission VALUES (3,'access comments, access content, access trackback, administer comments, post trackback, post comments, post comments without approval, create blog entry, edit own blog, download files (simple), upload files (simple), maintain files (simple), maintain subscriptions',0);
 
 REPLACE variable SET name='update_start', value='s:10:"2004-02-21;"';
-REPLACE variable SET name='theme_default', value='s:9:"xtemplate";';
+REPLACE variable SET name='blogapi_engine', value='s:1:"2";';
+REPLACE variable SET name='teaser_length', value='s:4:"2000";';
+
+REPLACE variable SET name='theme_default', value='s:6:"simple";';
+
+REPLACE blocks SET module = 'archive', delta = '0', status = '1', weight='-10';
+REPLACE blocks SET module = 'blogadmin', delta = '0', status = '1', weight='-5';
+REPLACE blocks SET module = 'taxonomy', delta = '0', status = '1', weight='-7';
+REPLACE blocks SET module = 'blog', delta = '0', status = '1';
+REPLACE blocks SET module = 'syndicate', delta = '0', status = '1', weight='5';
+REPLACE blocks SET module = 'user', delta = '0', status = '1', weight='8';
+REPLACE blocks SET module = 'user', delta = '1', status = '1', weight='10';
+
+# enable real name and homepage by default
+INSERT INTO variable VALUES ( 'profile_private_fields', 'a:20:{i:0;s:1:"0";i:1;s:8:"realname";i:2;s:1:"0";i:3;s:1:"0";i:4;s:1:"0";i:5;s:1:"0";i:6;s:1:"0";i:7;s:1:"0";i:8;s:1:"0";i:9;s:1:"0";i:10;s:1:"0";i:11;s:1:"0";i:12;s:1:"0";i:13;s:1:"0";i:14;s:1:"0";i:15;s:8:"homepage";i:16;s:1:"0";i:17;s:1:"0";i:18;s:1:"0";i:19;s:1:"0";}' );
 
-REPLACE blocks SET module = 'user', delta = '0', status = '1';
-REPLACE blocks SET module = 'user', delta = '1', status = '1';
+INSERT INTO variable VALUES ( 'profile_public_fields', 'a:20:{i:0;s:1:"0";i:1;s:8:"realname";i:2;s:1:"0";i:3;s:1:"0";i:4;s:1:"0";i:5;s:1:"0";i:6;s:1:"0";i:7;s:1:"0";i:8;s:1:"0";i:9;s:1:"0";i:10;s:1:"0";i:11;s:1:"0";i:12;s:1:"0";i:13;s:1:"0";i:14;s:1:"0";i:15;s:8:"homepage";i:16;s:1:"0";i:17;s:1:"0";i:18;s:1:"0";i:19;s:1:"0";}' );
 
+INSERT INTO vocabulary VALUES (1, 'Category', 'Categories for Blog Entries', 0, 0, 1, 0, 'blog', 0);
diff -urN drupal-4.4.1/database/database.pgsql drupal4blog/database/database.pgsql
--- drupal-4.4.1/database/database.pgsql	Sun May  2 23:45:29 2004
+++ drupal4blog/database/database.pgsql	Thu Jan  1 07:30:00 1970
@@ -1,615 +0,0 @@
--- PostgreSQL include file 31/10/2002
--- Maintainer: James Arthur, j_a_arthurATyahooDOTcom
-
---
--- Table structure for access
---
-
-CREATE TABLE access (
-  aid SERIAL,
-  mask varchar(255) NOT NULL default '',
-  type varchar(255) NOT NULL default '',
-  status smallint NOT NULL default '0',
-  PRIMARY KEY (aid),
-  UNIQUE (mask)
-);
-
---
--- Table structure for accesslog
---
-
-CREATE TABLE accesslog (
-  nid integer default '0',
-  url varchar(255) default NULL,
-  hostname varchar(128) default NULL,
-  uid integer default '0',
-  timestamp integer NOT NULL default '0'
-);
-CREATE INDEX accesslog_timestamp_idx ON accesslog (timestamp);
-
---
--- Table structure for authmap
---
-
-CREATE TABLE authmap (
-  aid SERIAL,
-  uid integer NOT NULL default '0',
-  authname varchar(128) NOT NULL default '',
-  module varchar(128) NOT NULL default '',
-  PRIMARY KEY (aid),
-  UNIQUE (authname)
-);
-
---
--- Table structure for blocks
---
-
-CREATE TABLE blocks (
-  module varchar(64) NOT NULL default '',
-  delta varchar(32) NOT NULL default '0',
-  status smallint NOT NULL default '0',
-  weight smallint NOT NULL default '0',
-  region smallint NOT NULL default '0',
-  path varchar(255) NOT NULL default '',
-  custom smallint NOT NULL default '0',
-  throttle smallint NOT NULL default '0'
-);
-
---
--- Table structure for book
---
-
-CREATE TABLE book (
-  nid integer NOT NULL default '0',
-  parent integer NOT NULL default '0',
-  weight smallint NOT NULL default '0',
-  format smallint default '0',
-  log text default '',
-  PRIMARY KEY (nid)
-);
-CREATE INDEX book_nid_idx ON book(nid);
-CREATE INDEX book_parent ON book(parent);
-
---
--- Table structure for boxes
---
-
-CREATE TABLE boxes (
-  bid SERIAL,
-  title varchar(64) NOT NULL default '',
-  body text default '',
-  info varchar(128) NOT NULL default '',
-  type smallint NOT NULL default '0',
-  PRIMARY KEY  (bid),
-  UNIQUE (info),
-  UNIQUE (title)
-);
-
---
--- Table structure for bundle
---
-
-CREATE TABLE bundle (
-  bid SERIAL,
-  title varchar(255) NOT NULL default '',
-  attributes varchar(255) NOT NULL default '',
-  PRIMARY KEY  (bid),
-  UNIQUE (title)
-);
-
---
--- Table structure for cache
---
-
-CREATE TABLE cache (
-  cid varchar(255) NOT NULL default '',
-  data text default '',
-  expire integer NOT NULL default '0',
-  created integer NOT NULL default '0',
-  headers text default '',
-  PRIMARY KEY  (cid)
-);
-
---
--- Table structure for comments
---
-
-CREATE TABLE comments (
-  cid SERIAL,
-  pid integer NOT NULL default '0',
-  nid integer NOT NULL default '0',
-  uid integer NOT NULL default '0',
-  subject varchar(64) NOT NULL default '',
-  comment text NOT NULL default '',
-  hostname varchar(128) NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  score integer NOT NULL default '0',
-  status smallint  NOT NULL default '0',
-  thread varchar(255) default '',
-  users text default '',
-  PRIMARY KEY  (cid)
-);
-CREATE INDEX comments_nid_idx ON comments(nid);
-
---
--- Table structure for directory
---
-
-CREATE TABLE directory (
-  link varchar(255) NOT NULL default '',
-  name varchar(128) NOT NULL default '',
-  mail varchar(128) NOT NULL default '',
-  slogan text NOT NULL default '',
-  mission text NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  PRIMARY KEY  (link)
-);
-
---
--- Table structure for feed
---
-
-CREATE TABLE feed (
-  fid SERIAL,
-  title varchar(255) NOT NULL default '',
-  url varchar(255) NOT NULL default '',
-  refresh integer NOT NULL default '0',
-  checked integer NOT NULL default '0',
-  attributes varchar(255) NOT NULL default '',
-  link varchar(255) NOT NULL default '',
-  description text NOT NULL default '',
-  image text NOT NULL default '',
-  etag varchar(255) NOT NULL default '',
-  modified integer NOT NULL default '0',
-  PRIMARY KEY  (fid),
-  UNIQUE (title),
-  UNIQUE (url)
-);
-
---
--- Table structure for table 'filters'
---
-
-CREATE TABLE filters (
-  module varchar(64) NOT NULL default '',
-  weight smallint DEFAULT '0' NOT NULL,
-  PRIMARY KEY (weight)
-);
-
---
--- Table structure for table 'forum'
---
-
-CREATE TABLE forum (
-  nid integer NOT NULL default '0',
-  tid integer NOT NULL default '0',
-  shadow integer NOT NULL default '0',
-  PRIMARY KEY  (nid)
-);
-CREATE INDEX forum_tid_idx ON forum(tid);
-
---
--- Table structure for history
---
-
-CREATE TABLE history (
-  uid integer NOT NULL default '0',
-  nid integer NOT NULL default '0',
-  timestamp integer NOT NULL default '0',
-  PRIMARY KEY  (uid,nid)
-);
-
---
--- Table structure for item
---
-
-CREATE TABLE item (
-  iid SERIAL,
-  fid integer NOT NULL default '0',
-  title varchar(255) NOT NULL default '',
-  link varchar(255) NOT NULL default '',
-  author varchar(255) NOT NULL default '',
-  description text NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  attributes varchar(255) NOT NULL default '',
-  PRIMARY KEY  (iid)
-);
-
---
--- Table structure for locales
---
-
-CREATE TABLE locales (
-  lid SERIAL,
-  location varchar(128) NOT NULL default '',
-  string text NOT NULL default '',
-  da text NOT NULL default '',
-  fi text NOT NULL default '',
-  fr text NOT NULL default '',
-  en text NOT NULL default '',
-  es text NOT NULL default '',
-  nl text NOT NULL default '',
-  no text NOT NULL default '',
-  sw text NOT NULL default '',
-  PRIMARY KEY  (lid)
-);
-
---
--- Table structure for table 'moderation_filters'
---
-
-CREATE TABLE moderation_filters (
-  fid SERIAL,
-  filter varchar(255) NOT NULL default '',
-  minimum smallint NOT NULL default '0',
-  PRIMARY KEY  (fid)
-);
-
---
--- Table structure for table 'moderation_roles'
---
-
-CREATE TABLE moderation_roles (
-  rid integer NOT NULL default '0',
-  mid integer NOT NULL default '0',
-  value smallint NOT NULL default '0'
-);
-CREATE INDEX moderation_roles_rid_idx ON moderation_roles(rid);
-CREATE INDEX moderation_roles_mid_idx ON moderation_roles(mid);
-
---
--- Table structure for table 'moderation_votes'
---
-
-CREATE TABLE moderation_votes (
-  mid SERIAL,
-  vote varchar(255) default NULL,
-  weight smallint NOT NULL default '0',
-  PRIMARY KEY  (mid)
-);
-
---
--- Table structure for node
---
-
-CREATE TABLE node (
-  nid SERIAL,
-  type varchar(16) NOT NULL default '',
-  title varchar(128) NOT NULL default '',
-  score integer NOT NULL default '0',
-  votes integer NOT NULL default '0',
-  uid integer NOT NULL default '0',
-  status integer NOT NULL default '1',
-  created integer NOT NULL default '0',
-  comment integer NOT NULL default '0',
-  promote integer NOT NULL default '0',
-  moderate integer NOT NULL default '0',
-  users text NOT NULL default '',
-  teaser text NOT NULL default '',
-  body text NOT NULL default '',
-  changed integer NOT NULL default '0',
-  revisions text NOT NULL default '',
-  static integer NOT NULL default '0',
-  PRIMARY KEY  (nid)
-);
-CREATE INDEX node_type_idx ON node(type);
-CREATE INDEX node_title_idx ON node(title,type);
-CREATE INDEX node_status_idx ON node(status);
-CREATE INDEX node_uid_idx ON node(uid);
-CREATE INDEX node_moderate_idx ON node (moderate);
-CREATE INDEX node_promote_status_idx ON node (promote, status);
-
---
--- Table structure for table 'node_counter'
---
-
-CREATE TABLE node_counter (
-  nid integer NOT NULL default '0',
-  totalcount integer NOT NULL default '0',
-  daycount integer NOT NULL default '0',
-  timestamp integer NOT NULL default '0',
-  PRIMARY KEY  (nid)
-);
-CREATE INDEX node_counter_totalcount_idx ON node_counter(totalcount);
-CREATE INDEX node_counter_daycount_idx ON node_counter(daycount);
-CREATE INDEX node_counter_timestamp_idx ON node_counter(timestamp);
-
---
--- Table structure for page
---
-
-CREATE TABLE page (
-  nid integer NOT NULL default '0',
-  link varchar(128) NOT NULL default '',
-  format smallint NOT NULL default '0',
-  description varchar(128) NOT NULL default '',
-  PRIMARY KEY  (nid)
-);
-CREATE INDEX page_nid_idx ON page(nid);
-
---
--- Table structure for table 'url_alias'
---
-
-CREATE TABLE url_alias (
-  pid serial,
-  dst varchar(128) NOT NULL default '',
-  src varchar(128) NOT NULL default '',
-  PRIMARY KEY  (pid)
-);
-CREATE INDEX url_alias_src_idx ON url_alias(src);
-CREATE INDEX url_alias_dst_idx ON url_alias(dst);
---
--- Table structure for permission
---
-
-CREATE TABLE permission (
-  rid integer NOT NULL default '0',
-  perm text default '',
-  tid integer NOT NULL default '0'
-);
-CREATE INDEX permission_rid_idx ON permission(rid);
-
---
--- Table structure for poll
---
-
-CREATE TABLE poll (
-  nid integer NOT NULL default '0',
-  runtime integer NOT NULL default '0',
-  voters text NOT NULL default '',
-  active integer NOT NULL default '0',
-  PRIMARY KEY  (nid)
-);
-
---
--- Table structure for poll_choices
---
-
-CREATE TABLE poll_choices (
-  chid SERIAL,
-  nid integer NOT NULL default '0',
-  chtext varchar(128) NOT NULL default '',
-  chvotes integer NOT NULL default '0',
-  chorder integer NOT NULL default '0',
-  PRIMARY KEY  (chid)
-);
-CREATE INDEX poll_choices_nid_idx ON poll_choices(nid);
-
---
--- Table structure for role
---
-
-CREATE TABLE role (
-  rid SERIAL,
-  name varchar(32) NOT NULL default '',
-  PRIMARY KEY  (rid),
-  UNIQUE (name)
-);
-
---
--- Table structure for search_index
---
-
-CREATE TABLE search_index (
-  word varchar(50) NOT NULL default '',
-  lno integer NOT NULL default '0',
-  type varchar(16) default NULL,
-  count integer default NULL
-);
-CREATE INDEX search_index_lno_idx ON search_index(lno);
-CREATE INDEX search_index_word_idx ON search_index(word);
-
---
--- Table structure for sessions
---
-
-CREATE TABLE sessions (
-  uid integer NOT NULL,
-  sid varchar(32) NOT NULL default '',
-  hostname varchar(128) NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  session text,
-  PRIMARY KEY (sid)
-);
-
---
--- Table structure for sequences
--- This is only used under MySQL, co commented out
---
---
--- CREATE TABLE sequences (
---   name varchar(255) NOT NULL,
---   id integer NOT NULL,
---   PRIMARY KEY (name)
--- );
-
---
--- Table structure for system
---
-
-CREATE TABLE system (
-  filename varchar(255) NOT NULL default '',
-  name varchar(255) NOT NULL default '',
-  type varchar(255) NOT NULL default '',
-  description varchar(255) NOT NULL default '',
-  status integer NOT NULL default '0',
-  throttle smallint NOT NULL default '0',
-  bootstrap integer NOT NULL default '0',
-  PRIMARY KEY  (filename)
-);
-
---
--- Table structure for term_data
---
-
-CREATE TABLE term_data (
-  tid SERIAL,
-  vid integer NOT NULL default '0',
-  name varchar(255) NOT NULL default '',
-  description text default '',
-  weight smallint NOT NULL default '0',
-  PRIMARY KEY  (tid)
-);
-CREATE INDEX term_data_vid_idx ON term_data(vid);
-
---
--- Table structure for term_hierarchy
---
-
-CREATE TABLE term_hierarchy (
-  tid integer NOT NULL default '0',
-  parent integer NOT NULL default '0'
-);
-CREATE INDEX term_hierarchy_tid_idx ON term_hierarchy(tid);
-CREATE INDEX term_hierarchy_parent_idx ON term_hierarchy(parent);
-
---
--- Table structure for term_node
---
-
-CREATE TABLE term_node (
-  nid integer NOT NULL default '0',
-  tid integer NOT NULL default '0'
-);
-CREATE INDEX term_node_nid_idx ON term_node(nid);
-CREATE INDEX term_node_tid_idx ON term_node(tid);
-
---
--- Table structure for term_relation
---
-
-CREATE TABLE term_relation (
-  tid1 integer NOT NULL default '0',
-  tid2 integer NOT NULL default '0'
-);
-CREATE INDEX term_relation_tid1_idx ON term_relation(tid1);
-CREATE INDEX term_relation_tid2_idx ON term_relation(tid2);
-
---
--- Table structure for term_synonym
---
-
-CREATE TABLE term_synonym (
-  tid integer NOT NULL default '0',
-  name varchar(255) NOT NULL default ''
-);
-CREATE INDEX term_synonym_tid_idx ON term_synonym(tid);
-CREATE INDEX term_synonym_name_idx ON term_synonym(name);
-
---
--- Table structure for users
---
-
-CREATE TABLE users (
-  uid integer NOT NULL default '0',
-  name varchar(60) NOT NULL default '',
-  pass varchar(32) NOT NULL default '',
-  mail varchar(64) default '',
-  mode smallint NOT NULL default '0',
-  sort smallint default '0',
-  threshold smallint default '0',
-  theme varchar(255) NOT NULL default '',
-  signature varchar(255) NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  status smallint NOT NULL default '0',
-  timezone varchar(8) default NULL,
-  language char(2) NOT NULL default '',
-  init varchar(64) default '',
-  data text default '',
-  rid integer NOT NULL default '0',
-  PRIMARY KEY  (uid),
-  UNIQUE (name)
-);
-CREATE INDEX users_timestamp_idx ON users(timestamp);
-
-CREATE SEQUENCE users_uid_seq INCREMENT 1 START 1;
-
---
--- Table structure for variable
---
-
-CREATE TABLE variable (
-  name varchar(48) NOT NULL default '',
-  value text NOT NULL default '',
-  PRIMARY KEY  (name)
-);
-
---
--- Table structure for vocabulary
---
-
-CREATE TABLE vocabulary (
-  vid SERIAL,
-  name varchar(255) NOT NULL default '',
-  description text default '',
-  relations smallint NOT NULL default '0',
-  hierarchy smallint NOT NULL default '0',
-  multiple smallint NOT NULL default '0',
-  required smallint NOT NULL default '0',
-  nodes text default '',
-  weight smallint NOT NULL default '0',
-  PRIMARY KEY  (vid)
-);
-
---
--- Table structure for watchdog
---
-
-CREATE TABLE watchdog (
-  wid SERIAL,
-  uid integer NOT NULL default '0',
-  type varchar(16) NOT NULL default '',
-  message text NOT NULL default '',
-  link varchar(255) NOT NULL default '',
-  location varchar(128) NOT NULL default '',
-  hostname varchar(128) NOT NULL default '',
-  timestamp integer NOT NULL default '0',
-  PRIMARY KEY  (wid)
-);
-
---
--- Insert some default values
---
-
-INSERT INTO system VALUES ('modules/admin.module','admin','module','',1,0,0);
-INSERT INTO system VALUES ('modules/block.module','block','module','',1,0,0);
-INSERT INTO system VALUES ('modules/comment.module','comment','module','',1,0,0);
-INSERT INTO system VALUES ('modules/help.module','help','module','',1,0,0);
-INSERT INTO system VALUES ('modules/node.module','node','module','',1,0,0);
-INSERT INTO system VALUES ('modules/page.module','page','module','',1,0,0);
-INSERT INTO system VALUES ('modules/story.module','story','module','',1,0,0);
-INSERT INTO system VALUES ('modules/taxonomy.module','taxonomy','module','',1,0,0);
-INSERT INTO system VALUES ('themes/xtemplate/xtemplate.theme','xtemplate','theme','Internet explorer, Netscape, Opera',1,0,0);
-
-INSERT INTO variable(name,value) VALUES('update_start', 's:10:"2004-02-21";');
-INSERT INTO variable(name,value) VALUES('theme_default','s:9:"xtemplate";');
-INSERT INTO users(uid,name,mail,rid) VALUES(0,'','', '1');
-
-INSERT INTO role (rid, name) VALUES (1, 'anonymous user');
-INSERT INTO permission VALUES (1,'access content',0);
-
-INSERT INTO role (rid, name) VALUES (2, 'authenticated user');
-INSERT INTO permission VALUES (2,'access comments, access content, post comments, post comments without approval',0);
-
-INSERT INTO blocks(module,delta,status) VALUES('user', '0', '1');
-INSERT INTO blocks(module,delta,status) VALUES('user', '1', '1');
-
----
---- Functions
----
-
-CREATE FUNCTION "greatest"(integer, integer) RETURNS integer AS '
-BEGIN
-  IF $1 > $2 THEN
-    RETURN $1;
-  END IF;
-  RETURN $2;
-END;
-' LANGUAGE 'plpgsql';
-
-CREATE FUNCTION "rand"() RETURNS float AS '
-BEGIN
-  RETURN random();
-END;
-' LANGUAGE 'plpgsql';
diff -urN drupal-4.4.1/database/database.pgsql.not.working.yet drupal4blog/database/database.pgsql.not.working.yet
--- drupal-4.4.1/database/database.pgsql.not.working.yet	Thu Jan  1 07:30:00 1970
+++ drupal4blog/database/database.pgsql.not.working.yet	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,615 @@
+-- PostgreSQL include file 31/10/2002
+-- Maintainer: James Arthur, j_a_arthurATyahooDOTcom
+
+--
+-- Table structure for access
+--
+
+CREATE TABLE access (
+  aid SERIAL,
+  mask varchar(255) NOT NULL default '',
+  type varchar(255) NOT NULL default '',
+  status smallint NOT NULL default '0',
+  PRIMARY KEY (aid),
+  UNIQUE (mask)
+);
+
+--
+-- Table structure for accesslog
+--
+
+CREATE TABLE accesslog (
+  nid integer default '0',
+  url varchar(255) default NULL,
+  hostname varchar(128) default NULL,
+  uid integer default '0',
+  timestamp integer NOT NULL default '0'
+);
+CREATE INDEX accesslog_timestamp_idx ON accesslog (timestamp);
+
+--
+-- Table structure for authmap
+--
+
+CREATE TABLE authmap (
+  aid SERIAL,
+  uid integer NOT NULL default '0',
+  authname varchar(128) NOT NULL default '',
+  module varchar(128) NOT NULL default '',
+  PRIMARY KEY (aid),
+  UNIQUE (authname)
+);
+
+--
+-- Table structure for blocks
+--
+
+CREATE TABLE blocks (
+  module varchar(64) NOT NULL default '',
+  delta varchar(32) NOT NULL default '0',
+  status smallint NOT NULL default '0',
+  weight smallint NOT NULL default '0',
+  region smallint NOT NULL default '0',
+  path varchar(255) NOT NULL default '',
+  custom smallint NOT NULL default '0',
+  throttle smallint NOT NULL default '0'
+);
+
+--
+-- Table structure for book
+--
+
+CREATE TABLE book (
+  nid integer NOT NULL default '0',
+  parent integer NOT NULL default '0',
+  weight smallint NOT NULL default '0',
+  format smallint default '0',
+  log text default '',
+  PRIMARY KEY (nid)
+);
+CREATE INDEX book_nid_idx ON book(nid);
+CREATE INDEX book_parent ON book(parent);
+
+--
+-- Table structure for boxes
+--
+
+CREATE TABLE boxes (
+  bid SERIAL,
+  title varchar(64) NOT NULL default '',
+  body text default '',
+  info varchar(128) NOT NULL default '',
+  type smallint NOT NULL default '0',
+  PRIMARY KEY  (bid),
+  UNIQUE (info),
+  UNIQUE (title)
+);
+
+--
+-- Table structure for bundle
+--
+
+CREATE TABLE bundle (
+  bid SERIAL,
+  title varchar(255) NOT NULL default '',
+  attributes varchar(255) NOT NULL default '',
+  PRIMARY KEY  (bid),
+  UNIQUE (title)
+);
+
+--
+-- Table structure for cache
+--
+
+CREATE TABLE cache (
+  cid varchar(255) NOT NULL default '',
+  data text default '',
+  expire integer NOT NULL default '0',
+  created integer NOT NULL default '0',
+  headers text default '',
+  PRIMARY KEY  (cid)
+);
+
+--
+-- Table structure for comments
+--
+
+CREATE TABLE comments (
+  cid SERIAL,
+  pid integer NOT NULL default '0',
+  nid integer NOT NULL default '0',
+  uid integer NOT NULL default '0',
+  subject varchar(64) NOT NULL default '',
+  comment text NOT NULL default '',
+  hostname varchar(128) NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  score integer NOT NULL default '0',
+  status smallint  NOT NULL default '0',
+  thread varchar(255) default '',
+  users text default '',
+  PRIMARY KEY  (cid)
+);
+CREATE INDEX comments_nid_idx ON comments(nid);
+
+--
+-- Table structure for directory
+--
+
+CREATE TABLE directory (
+  link varchar(255) NOT NULL default '',
+  name varchar(128) NOT NULL default '',
+  mail varchar(128) NOT NULL default '',
+  slogan text NOT NULL default '',
+  mission text NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  PRIMARY KEY  (link)
+);
+
+--
+-- Table structure for feed
+--
+
+CREATE TABLE feed (
+  fid SERIAL,
+  title varchar(255) NOT NULL default '',
+  url varchar(255) NOT NULL default '',
+  refresh integer NOT NULL default '0',
+  checked integer NOT NULL default '0',
+  attributes varchar(255) NOT NULL default '',
+  link varchar(255) NOT NULL default '',
+  description text NOT NULL default '',
+  image text NOT NULL default '',
+  etag varchar(255) NOT NULL default '',
+  modified integer NOT NULL default '0',
+  PRIMARY KEY  (fid),
+  UNIQUE (title),
+  UNIQUE (url)
+);
+
+--
+-- Table structure for table 'filters'
+--
+
+CREATE TABLE filters (
+  module varchar(64) NOT NULL default '',
+  weight smallint DEFAULT '0' NOT NULL,
+  PRIMARY KEY (weight)
+);
+
+--
+-- Table structure for table 'forum'
+--
+
+CREATE TABLE forum (
+  nid integer NOT NULL default '0',
+  tid integer NOT NULL default '0',
+  shadow integer NOT NULL default '0',
+  PRIMARY KEY  (nid)
+);
+CREATE INDEX forum_tid_idx ON forum(tid);
+
+--
+-- Table structure for history
+--
+
+CREATE TABLE history (
+  uid integer NOT NULL default '0',
+  nid integer NOT NULL default '0',
+  timestamp integer NOT NULL default '0',
+  PRIMARY KEY  (uid,nid)
+);
+
+--
+-- Table structure for item
+--
+
+CREATE TABLE item (
+  iid SERIAL,
+  fid integer NOT NULL default '0',
+  title varchar(255) NOT NULL default '',
+  link varchar(255) NOT NULL default '',
+  author varchar(255) NOT NULL default '',
+  description text NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  attributes varchar(255) NOT NULL default '',
+  PRIMARY KEY  (iid)
+);
+
+--
+-- Table structure for locales
+--
+
+CREATE TABLE locales (
+  lid SERIAL,
+  location varchar(128) NOT NULL default '',
+  string text NOT NULL default '',
+  da text NOT NULL default '',
+  fi text NOT NULL default '',
+  fr text NOT NULL default '',
+  en text NOT NULL default '',
+  es text NOT NULL default '',
+  nl text NOT NULL default '',
+  no text NOT NULL default '',
+  sw text NOT NULL default '',
+  PRIMARY KEY  (lid)
+);
+
+--
+-- Table structure for table 'moderation_filters'
+--
+
+CREATE TABLE moderation_filters (
+  fid SERIAL,
+  filter varchar(255) NOT NULL default '',
+  minimum smallint NOT NULL default '0',
+  PRIMARY KEY  (fid)
+);
+
+--
+-- Table structure for table 'moderation_roles'
+--
+
+CREATE TABLE moderation_roles (
+  rid integer NOT NULL default '0',
+  mid integer NOT NULL default '0',
+  value smallint NOT NULL default '0'
+);
+CREATE INDEX moderation_roles_rid_idx ON moderation_roles(rid);
+CREATE INDEX moderation_roles_mid_idx ON moderation_roles(mid);
+
+--
+-- Table structure for table 'moderation_votes'
+--
+
+CREATE TABLE moderation_votes (
+  mid SERIAL,
+  vote varchar(255) default NULL,
+  weight smallint NOT NULL default '0',
+  PRIMARY KEY  (mid)
+);
+
+--
+-- Table structure for node
+--
+
+CREATE TABLE node (
+  nid SERIAL,
+  type varchar(16) NOT NULL default '',
+  title varchar(128) NOT NULL default '',
+  score integer NOT NULL default '0',
+  votes integer NOT NULL default '0',
+  uid integer NOT NULL default '0',
+  status integer NOT NULL default '1',
+  created integer NOT NULL default '0',
+  comment integer NOT NULL default '0',
+  promote integer NOT NULL default '0',
+  moderate integer NOT NULL default '0',
+  users text NOT NULL default '',
+  teaser text NOT NULL default '',
+  body text NOT NULL default '',
+  changed integer NOT NULL default '0',
+  revisions text NOT NULL default '',
+  static integer NOT NULL default '0',
+  PRIMARY KEY  (nid)
+);
+CREATE INDEX node_type_idx ON node(type);
+CREATE INDEX node_title_idx ON node(title,type);
+CREATE INDEX node_status_idx ON node(status);
+CREATE INDEX node_uid_idx ON node(uid);
+CREATE INDEX node_moderate_idx ON node (moderate);
+CREATE INDEX node_promote_status_idx ON node (promote, status);
+
+--
+-- Table structure for table 'node_counter'
+--
+
+CREATE TABLE node_counter (
+  nid integer NOT NULL default '0',
+  totalcount integer NOT NULL default '0',
+  daycount integer NOT NULL default '0',
+  timestamp integer NOT NULL default '0',
+  PRIMARY KEY  (nid)
+);
+CREATE INDEX node_counter_totalcount_idx ON node_counter(totalcount);
+CREATE INDEX node_counter_daycount_idx ON node_counter(daycount);
+CREATE INDEX node_counter_timestamp_idx ON node_counter(timestamp);
+
+--
+-- Table structure for page
+--
+
+CREATE TABLE page (
+  nid integer NOT NULL default '0',
+  link varchar(128) NOT NULL default '',
+  format smallint NOT NULL default '0',
+  description varchar(128) NOT NULL default '',
+  PRIMARY KEY  (nid)
+);
+CREATE INDEX page_nid_idx ON page(nid);
+
+--
+-- Table structure for table 'url_alias'
+--
+
+CREATE TABLE url_alias (
+  pid serial,
+  dst varchar(128) NOT NULL default '',
+  src varchar(128) NOT NULL default '',
+  PRIMARY KEY  (pid)
+);
+CREATE INDEX url_alias_src_idx ON url_alias(src);
+CREATE INDEX url_alias_dst_idx ON url_alias(dst);
+--
+-- Table structure for permission
+--
+
+CREATE TABLE permission (
+  rid integer NOT NULL default '0',
+  perm text default '',
+  tid integer NOT NULL default '0'
+);
+CREATE INDEX permission_rid_idx ON permission(rid);
+
+--
+-- Table structure for poll
+--
+
+CREATE TABLE poll (
+  nid integer NOT NULL default '0',
+  runtime integer NOT NULL default '0',
+  voters text NOT NULL default '',
+  active integer NOT NULL default '0',
+  PRIMARY KEY  (nid)
+);
+
+--
+-- Table structure for poll_choices
+--
+
+CREATE TABLE poll_choices (
+  chid SERIAL,
+  nid integer NOT NULL default '0',
+  chtext varchar(128) NOT NULL default '',
+  chvotes integer NOT NULL default '0',
+  chorder integer NOT NULL default '0',
+  PRIMARY KEY  (chid)
+);
+CREATE INDEX poll_choices_nid_idx ON poll_choices(nid);
+
+--
+-- Table structure for role
+--
+
+CREATE TABLE role (
+  rid SERIAL,
+  name varchar(32) NOT NULL default '',
+  PRIMARY KEY  (rid),
+  UNIQUE (name)
+);
+
+--
+-- Table structure for search_index
+--
+
+CREATE TABLE search_index (
+  word varchar(50) NOT NULL default '',
+  lno integer NOT NULL default '0',
+  type varchar(16) default NULL,
+  count integer default NULL
+);
+CREATE INDEX search_index_lno_idx ON search_index(lno);
+CREATE INDEX search_index_word_idx ON search_index(word);
+
+--
+-- Table structure for sessions
+--
+
+CREATE TABLE sessions (
+  uid integer NOT NULL,
+  sid varchar(32) NOT NULL default '',
+  hostname varchar(128) NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  session text,
+  PRIMARY KEY (sid)
+);
+
+--
+-- Table structure for sequences
+-- This is only used under MySQL, co commented out
+--
+--
+-- CREATE TABLE sequences (
+--   name varchar(255) NOT NULL,
+--   id integer NOT NULL,
+--   PRIMARY KEY (name)
+-- );
+
+--
+-- Table structure for system
+--
+
+CREATE TABLE system (
+  filename varchar(255) NOT NULL default '',
+  name varchar(255) NOT NULL default '',
+  type varchar(255) NOT NULL default '',
+  description varchar(255) NOT NULL default '',
+  status integer NOT NULL default '0',
+  throttle smallint NOT NULL default '0',
+  bootstrap integer NOT NULL default '0',
+  PRIMARY KEY  (filename)
+);
+
+--
+-- Table structure for term_data
+--
+
+CREATE TABLE term_data (
+  tid SERIAL,
+  vid integer NOT NULL default '0',
+  name varchar(255) NOT NULL default '',
+  description text default '',
+  weight smallint NOT NULL default '0',
+  PRIMARY KEY  (tid)
+);
+CREATE INDEX term_data_vid_idx ON term_data(vid);
+
+--
+-- Table structure for term_hierarchy
+--
+
+CREATE TABLE term_hierarchy (
+  tid integer NOT NULL default '0',
+  parent integer NOT NULL default '0'
+);
+CREATE INDEX term_hierarchy_tid_idx ON term_hierarchy(tid);
+CREATE INDEX term_hierarchy_parent_idx ON term_hierarchy(parent);
+
+--
+-- Table structure for term_node
+--
+
+CREATE TABLE term_node (
+  nid integer NOT NULL default '0',
+  tid integer NOT NULL default '0'
+);
+CREATE INDEX term_node_nid_idx ON term_node(nid);
+CREATE INDEX term_node_tid_idx ON term_node(tid);
+
+--
+-- Table structure for term_relation
+--
+
+CREATE TABLE term_relation (
+  tid1 integer NOT NULL default '0',
+  tid2 integer NOT NULL default '0'
+);
+CREATE INDEX term_relation_tid1_idx ON term_relation(tid1);
+CREATE INDEX term_relation_tid2_idx ON term_relation(tid2);
+
+--
+-- Table structure for term_synonym
+--
+
+CREATE TABLE term_synonym (
+  tid integer NOT NULL default '0',
+  name varchar(255) NOT NULL default ''
+);
+CREATE INDEX term_synonym_tid_idx ON term_synonym(tid);
+CREATE INDEX term_synonym_name_idx ON term_synonym(name);
+
+--
+-- Table structure for users
+--
+
+CREATE TABLE users (
+  uid integer NOT NULL default '0',
+  name varchar(60) NOT NULL default '',
+  pass varchar(32) NOT NULL default '',
+  mail varchar(64) default '',
+  mode smallint NOT NULL default '0',
+  sort smallint default '0',
+  threshold smallint default '0',
+  theme varchar(255) NOT NULL default '',
+  signature varchar(255) NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  status smallint NOT NULL default '0',
+  timezone varchar(8) default NULL,
+  language char(2) NOT NULL default '',
+  init varchar(64) default '',
+  data text default '',
+  rid integer NOT NULL default '0',
+  PRIMARY KEY  (uid),
+  UNIQUE (name)
+);
+CREATE INDEX users_timestamp_idx ON users(timestamp);
+
+CREATE SEQUENCE users_uid_seq INCREMENT 1 START 1;
+
+--
+-- Table structure for variable
+--
+
+CREATE TABLE variable (
+  name varchar(48) NOT NULL default '',
+  value text NOT NULL default '',
+  PRIMARY KEY  (name)
+);
+
+--
+-- Table structure for vocabulary
+--
+
+CREATE TABLE vocabulary (
+  vid SERIAL,
+  name varchar(255) NOT NULL default '',
+  description text default '',
+  relations smallint NOT NULL default '0',
+  hierarchy smallint NOT NULL default '0',
+  multiple smallint NOT NULL default '0',
+  required smallint NOT NULL default '0',
+  nodes text default '',
+  weight smallint NOT NULL default '0',
+  PRIMARY KEY  (vid)
+);
+
+--
+-- Table structure for watchdog
+--
+
+CREATE TABLE watchdog (
+  wid SERIAL,
+  uid integer NOT NULL default '0',
+  type varchar(16) NOT NULL default '',
+  message text NOT NULL default '',
+  link varchar(255) NOT NULL default '',
+  location varchar(128) NOT NULL default '',
+  hostname varchar(128) NOT NULL default '',
+  timestamp integer NOT NULL default '0',
+  PRIMARY KEY  (wid)
+);
+
+--
+-- Insert some default values
+--
+
+INSERT INTO system VALUES ('modules/admin.module','admin','module','',1,0,0);
+INSERT INTO system VALUES ('modules/block.module','block','module','',1,0,0);
+INSERT INTO system VALUES ('modules/comment.module','comment','module','',1,0,0);
+INSERT INTO system VALUES ('modules/help.module','help','module','',1,0,0);
+INSERT INTO system VALUES ('modules/node.module','node','module','',1,0,0);
+INSERT INTO system VALUES ('modules/page.module','page','module','',1,0,0);
+INSERT INTO system VALUES ('modules/story.module','story','module','',1,0,0);
+INSERT INTO system VALUES ('modules/taxonomy.module','taxonomy','module','',1,0,0);
+INSERT INTO system VALUES ('themes/xtemplate/xtemplate.theme','xtemplate','theme','Internet explorer, Netscape, Opera',1,0,0);
+
+INSERT INTO variable(name,value) VALUES('update_start', 's:10:"2004-02-21";');
+INSERT INTO variable(name,value) VALUES('theme_default','s:9:"xtemplate";');
+INSERT INTO users(uid,name,mail,rid) VALUES(0,'','', '1');
+
+INSERT INTO role (rid, name) VALUES (1, 'anonymous user');
+INSERT INTO permission VALUES (1,'access content',0);
+
+INSERT INTO role (rid, name) VALUES (2, 'authenticated user');
+INSERT INTO permission VALUES (2,'access comments, access content, post comments, post comments without approval',0);
+
+INSERT INTO blocks(module,delta,status) VALUES('user', '0', '1');
+INSERT INTO blocks(module,delta,status) VALUES('user', '1', '1');
+
+---
+--- Functions
+---
+
+CREATE FUNCTION "greatest"(integer, integer) RETURNS integer AS '
+BEGIN
+  IF $1 > $2 THEN
+    RETURN $1;
+  END IF;
+  RETURN $2;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE FUNCTION "rand"() RETURNS float AS '
+BEGIN
+  RETURN random();
+END;
+' LANGUAGE 'plpgsql';
diff -urN drupal-4.4.1/db-update.php drupal4blog/db-update.php
--- drupal-4.4.1/db-update.php	Thu Jan  1 07:30:00 1970
+++ drupal4blog/db-update.php	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,153 @@
+<?php
+// $Id$
+/*
+** USAGE:
+**
+** - Point your browser to "http://www.site.com/db-update.php" and follow
+**   the instructions.
+**
+** - If you are not logged in as administrator, you will need to modify the
+**   statement below. Change the 1 into a 0 to disable the access check.
+**   After finishing the upgrade, open this file and change the 0 back into
+**   a 1!
+*/
+
+$dblog_version = 'Drupal for blogger v0.7';
+
+// Disable access checking?
+$access_check = 1;
+
+include_once "includes/bootstrap.inc";
+include_once "includes/common.inc";
+
+if (!ini_get("safe_mode")) {
+  set_time_limit(180);
+}
+
+$update_types = array(
+  'v0.6'    => 'Drupal for Blogger v0.6',
+//  '4.4.1'   => 'Drupal 4.4.1',
+);
+
+function update_page_header($title) {
+  $output = "<html><head><title>$title</title>";
+  $output .= <<<EOF
+      <link rel="stylesheet" type="text/css" media="print" href="misc/print.css" />
+      <style type="text/css" title="layout" media="Screen">
+        @import url("misc/drupal.css");
+      </style>
+EOF;
+  $output .= "</head><body>";
+  $output .= "<div id=\"logo\"><a href=\"http://drupal.org/\"><img src=\"misc/druplicon-small.gif\" alt=\"Druplicon - Drupal logo\" title=\"Druplicon - Drupal logo\" /></a></div>";
+  $output .= "<div id=\"update\"><h1>$title</h1>";
+  return $output;
+}
+
+function update_page_footer() {
+  return "</div></body></html>";
+}
+
+function update_sql($sql) {
+  $result = db_query($sql);
+  if ($result) {
+    return array('1', nl2br(htmlentities($sql)) ." ", "<div style=\"color: green;\">OK</div>\n");
+  }
+  else {
+    return array('0', nl2br(htmlentities($sql)) ." ", "<div style=\"color: red;\">FAILED</div>\n");
+  }
+}
+
+function update_process($title,$result) {
+  print "<strong>$title</strong><br/>\n<pre>\n";
+  foreach ($result as $ret) {
+    print $ret[1];
+    print $ret[2];
+  }
+  print "</pre>\n";
+}
+
+/*** update funcs ***/
+function v06tov07_comment() {
+  $ret = array();
+  $ret[] = update_sql("ALTER TABLE {comments} ADD name varchar(60) default NULL");
+  $ret[] = update_sql("ALTER TABLE {comments} ADD mail varchar(60) default NULL");
+  $ret[] = update_sql("ALTER TABLE {comments} ADD homepage varchar(60) default NULL");
+
+  $results = db_query("SELECT * FROM {comments}");
+  while ($comment = db_fetch_object($results)) {
+    $anon = unserialize($comment->anon);
+    $ret[] = db_query("UPDATE {comments} SET name = '%s', mail = '%s', homepage = '%s' WHERE cid=%d",$anon->anon_name, $anon->anon_mail, $anon->anon_url,$comment->cid);
+  }
+  $ret[] .= update_sql("ALTER TABLE {comments} DROP anon");
+  return $ret;
+}
+
+function v06tov07_trackback() {
+  $ret = array();
+  $ret[] = update_sql("ALTER TABLE {tb} RENAME TO {trackback}");
+  $ret[] = update_sql("CREATE TABLE trackback_pingme (nid int(10) unsigned not null, cid int(10) unsigned not null)");
+  return $ret;
+}
+
+function v06tov07_theme_simple() {
+  $ret = array();
+  $ret[] = update_sql("CREATE TABLE theme_simple (id int(10) unsigned not null default '0', name varchar(32) not null, php longtext, raw longtext, PRIMARY KEY (id, name))");
+  return $ret;
+}
+/*** end update funcs ***/
+
+function update_page() {
+  global $user, $update_types, $dblog_version;
+
+  $edit = $_POST["edit"];
+  if ($_POST["op"] != 'Upgrade' || !$edit['from'])
+    return update_info();
+
+  print update_page_header("$dblog_version database upgrade");
+  switch ($edit['from']) {
+    case 'v0.6':
+      update_process("Updating v0.6 comments tables",v06tov07_comment());
+      update_process("Updating v0.6 trackback tables",v06tov07_comment());
+      update_process("Creating tables for simple theme",v06tov07_theme_simple());
+      break;
+  }
+  print update_page_footer();
+}
+
+function update_info() {
+  global $update_types, $dblog_version;
+
+  print update_page_header("$dblog_version database upgrade");
+  print "<ol>\n";
+  print "<li>Use this script to <strong>upgrade an existing Drupal (or 'Drupal for Blogger') installation</strong> to 'Drupal for Blogger'.  You don't need this script when installing 'Drupal for Blogger' from scratch.</li>";
+  print "<li>Before doing anything, <b>backup your database</b> (e.g. <code>mysqldump -p your_drupal_db</code>). This may alter some fields and your data might be lost!</li>\n";
+  print "<li>Don't upgrade your database twice as it may cause problems.</li>\n";
+  print "</ol>";
+
+  $form .= "<center><table border=0 width=90%><tr><td width=20%>";
+  $form .= form_select(t('Upgrade from'), 'from', 'v0.6', $update_types);
+  $form .= "</td><td align=left>";
+  $form .= form_submit(t('Upgrade'));
+  $form .= "</td></tr></table>";
+  print form($form);
+  print update_page_footer();
+}
+
+if (isset($_POST["op"])) {
+  include_once "includes/bootstrap.inc";
+  include_once "includes/common.inc";
+
+  // Access check:
+  if (($access_check == 0) || ($user->uid == 1)) {
+    update_page();
+  }
+  else {
+    print update_page_header("Access denied");
+    print "Access denied.  You are not authorized to access to this page.  Please log in as the user with user ID #1. If you cannot log-in, you will have to edit <code>update.php</code> to by-pass this access check; in that case, open <code>update.php</code> in a text editor and follow the instructions at the top.";
+    print update_page_footer();
+  }
+}
+else {
+  update_info();
+}
+?>
diff -urN drupal-4.4.1/includes/common.inc drupal4blog/includes/common.inc
--- drupal-4.4.1/includes/common.inc	Sun May  2 23:45:29 2004
+++ drupal4blog/includes/common.inc	Tue Jun  8 14:19:14 2004
@@ -1,5 +1,5 @@
 <?php
-/* $Id: common.inc,v 1.328.2.7 2004/04/27 18:18:24 dries Exp $ */
+/* $Id: common.inc,v 1.355 2004/06/02 19:01:40 dries Exp $ */
 
 /**
  * @defgroup common Core functions
@@ -40,18 +40,24 @@
  * @{
  */
 function drupal_set_message($message = NULL, $type = "status") {
-  static $stored_message = array();
+  if (!isset($_SESSION['messages'])) {
+    $_SESSION['messages'] = array();
+  }
 
   if (isset($message)) {
-    $stored_message[] = array($message, $type);
+    $_SESSION['messages'][] = array($message, $type);
   }
 
-  return $stored_message;
+  return $_SESSION['messages'];
 }
 
 function drupal_get_messages() {
-  return drupal_set_message();
+  $messages = drupal_set_message();
+  $_SESSION['messages'] = array();
+
+  return $messages;
 }
+
 /* @} */
 
 /**
@@ -80,7 +86,6 @@
 
   if (!isset($breadcrumb)) {
     $breadcrumb = menu_get_active_breadcrumb();
-    array_pop($breadcrumb);
   }
 
   return $breadcrumb;
@@ -96,7 +101,7 @@
  */
 
 function drupal_set_html_head($data = NULL) {
-  static $stored_head;
+  static $stored_head = '';
 
   if (!is_null($data)) {
     $stored_head .= "$data\n";
@@ -155,6 +160,10 @@
   elseif (function_exists("conf_url_rewrite")) {
     return conf_url_rewrite($path, 'outgoing');
   }
+  else {
+    // No alias found. Return the normal path.
+    return $path;
+  }
 }
 
 /**
@@ -181,7 +190,7 @@
  * @{
  */
 function drupal_set_header($header = NULL) {
-  static $stored_headers;
+  static $stored_headers = '';
 
   if (!is_null($header)) {
     header($header);
@@ -253,24 +262,40 @@
  * Generates a 404 error if the request can not be handled.
  */
 function drupal_not_found() {
-  header("HTTP/1.0 404 Not Found");
-  watchdog("httpd", "404 error: '". check_query($_GET["q"]) ."' not found");
+  header('HTTP/1.0 404 Not Found');
+  watchdog('httpd', t('404 error: "%page" not found', array('%page' => check_query($_GET["q"]))));
 
   $path = drupal_get_normal_path(variable_get('site_404', ''));
-
+  $status = MENU_FALLTHROUGH;
   if ($path) {
     menu_set_active_item($path);
+    $status = menu_execute_active_handler();
   }
 
-  if ($path && menu_active_handler_exists()) {
-    menu_execute_active_handler();
-  }
-  else {
+  if ($status != MENU_FOUND) {
     print theme('page', '', t('Page not found'));
   }
 }
 
 /**
+ * Generates a 403 error if the request is not allowed.
+ */
+function drupal_access_denied() {
+  header('HTTP/1.0 403 Forbidden');
+
+  $path = drupal_get_normal_path(variable_get('site_403', ''));
+  $status = MENU_FALLTHROUGH;
+  if ($path) {
+    menu_set_active_item($path);
+    $status = menu_execute_active_handler();
+  }
+
+  if ($status != MENU_FOUND) {
+    print theme('page', message_access(), t('Access denied'));
+  }
+}
+
+/**
  * Flexible and powerful HTTP client implementation. Allows to GET, POST, PUT
  * or any other HTTP requests. Handles redirects.
  *
@@ -399,7 +424,7 @@
   $entry = $types[$errno] .": $message in $filename on line $line.";
 
   if ($errno & E_ALL ^ E_NOTICE) {
-    watchdog("error", $types[$errno] .": $message in $filename on line $line.");
+    watchdog('error', t('%error: %message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line)));
     if (error_reporting()) {
       print "<pre>$entry</pre>";
     }
@@ -551,7 +576,7 @@
  */
 function valid_email_address($mail) {
   $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
-  $domain = '(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.?)+';
+  $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
   $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
   $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
 
@@ -561,10 +586,20 @@
 /**
  * Verify the syntax of the given URL.
  *
- * @param $url an URL
+ * @param $url
+ *   an URL
+ * @param $absolute
+ *   is the URL to be verified absolute, ie. of the form \<scheme\>://\<hostname\>/...?
+ * @return
+ *   valid syntax: TRUE; FALSE otherwise
  */
-function valid_url($url) {
-  return preg_match("/^[a-zA-z0-9\/:_\-_\.,]+$/", $url);
+function valid_url($url, $absolute = FALSE) {
+  if ($absolute) {
+    return preg_match("/^(http|https|ftp):\/\/[a-z0-9\/:_\-_\.\?,~=#&]+$/i", $url);
+  }
+  else {
+    return preg_match("/^[a-z0-9\/:_\-_\.,]+$/i", $url);
+  }
 }
 
 function valid_input_data($data) {
@@ -596,7 +631,7 @@
     $match += preg_match("/<\s*(applet|script|object|style|embed|form|blink|meta|html|frame|iframe|layer|ilayer|head|frameset|xml)/i", $data);
 
     if ($match) {
-      watchdog("warning", "terminated request because of suspicious input data: ". drupal_specialchars($data));
+      watchdog('warning', t('terminated request because of suspicious input data: %data', array('%data' => drupal_specialchars($data))));
       return 0;
     }
   }
@@ -628,9 +663,8 @@
     $output = module_invoke($type, "search_item", $item);
   }
   else {
-    $output = " <strong><u><a href=\"". $item["link"] ."\">". $item["title"] ."</a></u></strong><br />";
-    $output .= " <small>" . t($type) . ($item["user"] ? " - ". $item["user"] : "") ."". ($item["date"] ? " - ". format_date($item["date"], "small") : "") ."</small>";
-    $output .= "<br /><br />";
+    $output = " <dt class=\"title\"><a href=\"". $item["link"] ."\">". $item["title"] ."</a></dt>";
+    $output .= " <dd class=\"small\">" . t($type) . ($item["user"] ? " - ". $item["user"] : "") ."". ($item["date"] ? " - ". format_date($item["date"], "small") : "") ."</dd>";
   }
 
   return $output;
@@ -656,7 +690,7 @@
     $action = url("search");
   }
 
-  $output = " <br /><input type=\"text\" class=\"form-text\" size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\" />";
+  $output = " <div class=\"search-form\"><br /><input type=\"text\" class=\"form-text\" size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\" />";
   $output .= " <input type=\"submit\" class=\"form-submit\" value=\"". t("Search") ."\" />\n";
 
   if ($options) {
@@ -668,10 +702,9 @@
         $output .= " <input type=\"checkbox\" name=\"edit[type][$name]\" ". ($edit["type"][$name] ? " checked=\"checked\"" : "") ." /> ". t($name);
       }
     }
+    $output .= "</div>";
   }
 
-  $form .= "<br />";
-
   return form($output, "post", $action);
 }
 
@@ -680,6 +713,7 @@
  */
 function search_data($keys = NULL) {
   $edit = $_POST["edit"];
+  $output = '';
 
   if (isset($keys)) {
     foreach (module_list() as $name) {
@@ -687,9 +721,11 @@
         list($title, $results) = module_invoke($name, "search", $keys);
         if ($results) {
           $output .= "<h2>$title</h2>";
+          $output .= "<dl class=\"search-results\">";
           foreach ($results as $entry) {
             $output .= search_item($entry, $name);
           }
+          $output .= "</dl>";
         }
       }
     }
@@ -737,7 +773,7 @@
   $output = "<channel>\n";
   $output .= " <title>". drupal_specialchars(strip_tags($title)) ."</title>\n";
   $output .= " <link>". drupal_specialchars(strip_tags($link)) ."</link>\n";
-  $output .= " <description>". drupal_specialchars($description) ."</description>\n";
+  $output .= " <description>". drupal_specialchars(strip_tags($description)) ."</description>\n";
   $output .= " <language>". drupal_specialchars(strip_tags($language)) ."</language>\n";
   foreach ($args as $key => $value) {
     $output .= " <$key>". drupal_specialchars(strip_tags($value)) ."</$key>\n";
@@ -809,6 +845,7 @@
  */
 function format_interval($timestamp, $granularity = 2) {
   $units = array("1 year|%count years" => 31536000, "1 week|%count weeks" => 604800, "1 day|%count days" => 86400, "1 hour|%count hours" => 3600, "1 min|%count min" => 60, "1 sec|%count sec" => 1);
+  $output = '';
   foreach ($units as $key => $value) {
     $key = explode("|", $key);
     if ($timestamp >= $value) {
@@ -821,7 +858,7 @@
       break;
     }
   }
-  return ($output) ? $output : t("0 sec");
+  return $output ? $output : t("0 sec");
 }
 
 /**
@@ -864,6 +901,7 @@
   }
 
   $max = strlen($format);
+  $date = '';
   for ($i = 0; $i <= $max; $c = $format{$i++}) {
     if (strpos('AaDFlM', $c)) {
       $date .= t(gmdate($c, $timestamp));
@@ -926,7 +964,14 @@
     ** the true author of the content.
     */
 
-    $output = $object->name;
+    if ($object->homepage) {
+      $output = "<a href=\"$object->homepage\">$object->name</a>";
+    }
+    else {
+      $output = $object->name;
+    }
+
+    $output .= ' ('. t('not verified') .')';
   }
   else {
     $output = t(variable_get("anonymous", "Anonymous"));
@@ -947,64 +992,127 @@
   return "<form action=\"$action\" method=\"$method\"". drupal_attributes($attributes) .">\n$form\n</form>\n";
 }
 
-function form_item($title, $value, $description = NULL, $id = NULL) {
-  return theme("form_element", $title, $value, $description, $id);
+/**
+ * File an error against the form with the specified name.
+ */
+function form_set_error($name, $message) {
+  $GLOBALS['form'][$name] = $message;
+  drupal_set_message($message, 'error');
+}
+
+/**
+ * Return true when errors have been set.
+ */
+function form_has_errors() {
+  return isset($GLOBALS['form']);
+}
+
+/**
+ * Return the error message filed against the form with the specified name.
+ */
+function _form_get_error($name) {
+  return $GLOBALS['form'][$name];
+}
+
+function _form_get_class($name, $required, $error) {
+  return $name. ($required ? ' required' : '') . ($error ? ' error' : '');
+}
+
+function form_item($title, $value, $description = NULL, $id = NULL, $required = FALSE, $error = FALSE) {
+  return theme("form_element", $title, $value, $description, $id, $required, $error);
 }
 
 function form_group($legend, $group, $description = NULL) {
   return "<fieldset>" . ($legend ? "<legend>$legend</legend>" : "") . $group . ($description ? "<div class=\"description\">$description</div>" : "") . "</fieldset>\n";
 }
 
-function form_radio($title, $name, $value = 1, $checked = 0, $description = NULL, $attributes = NULL) {
-  $element = "<input type=\"radio\" class=\"form-radio\" name=\"edit[$name]\" value=\"$value\"". ($checked ? " checked=\"checked\"" : "") . drupal_attributes($attributes) .' />';
+function form_radio($title, $name, $value = 1, $checked = 0, $description = NULL, $attributes = NULL, $required = FALSE) {
+  $element = "<input type=\"radio\" class=\"". _form_get_class('form-radio', $required, _form_get_error($name)) ."\" name=\"edit[$name]\" value=\"$value\"". ($checked ? " checked=\"checked\"" : "") . drupal_attributes($attributes) .' />';
   if (!is_null($title)) {
     $element = "<label class=\"option\">$element $title</label>";
   }
-  return theme('form_element', NULL, $element, $description);
+  return theme('form_element', NULL, $element, $description, $required, _form_get_error($name));
 }
 
-function form_radios($title, $name, $value, $options, $description = NULL) {
+function form_radios($title, $name, $value, $options, $description = NULL, $required = FALSE) {
   if (count($options) > 0) {
+    $choices = '';
     foreach ($options as $key => $choice) {
       $choices .= "<label class=\"option\"><input type=\"radio\" class=\"form-radio\" name=\"edit[$name]\" value=\"$key\"". ($key == $value ? " checked=\"checked\"" : "") ." /> $choice</label><br />";
     }
-    return theme('form_element', $title, $choices, $description);
+    return theme('form_element', $title, $choices, $description, $required, _form_get_error($name));
   }
 }
 
-function form_checkbox($title, $name, $value = 1, $checked = 0, $description = NULL, $attributes = NULL) {
-  $element = "<input type=\"checkbox\" class=\"form-checkbox\" name=\"edit[$name]\" id=\"edit-$name\" value=\"". $value ."\"". ($checked ? " checked=\"checked\"" : "") . drupal_attributes($attributes) .' />';
+function form_checkbox($title, $name, $value = 1, $checked = 0, $description = NULL, $attributes = NULL, $required = FALSE) {
+  $element = "<input type=\"checkbox\" class=\"". _form_get_class('form-checkbox', $required, _form_get_error($name)) ."\" name=\"edit[$name]\" id=\"edit-$name\" value=\"". $value ."\"". ($checked ? " checked=\"checked\"" : "") . drupal_attributes($attributes) .' />';
   if (!is_null($title)) {
     $element = "<label class=\"option\">$element $title</label>";
   }
-  return form_hidden($name, 0) . theme('form_element', NULL, $element, $description);
+  return form_hidden($name, 0) . theme('form_element', NULL, $element, $description, $required, _form_get_error($name));
 }
 
-function form_textfield($title, $name, $value, $size, $maxlength, $description = NULL, $attributes = NULL) {
+function form_checkboxes($title, $name, $values, $options, $description = NULL, $required = FALSE) {
+  if (count($options) > 0) {
+    if (!isset($values)) {
+      $values = array();
+    }
+    $choices = '';
+    foreach ($options as $key => $choice) {
+      $choices .= "<label class=\"option\"><input type=\"checkbox\" class=\"form-checkbox\" name=\"edit[$name][]\" value=\"$key\"". (in_array($key, $values) ? " checked=\"checked\"" : "") ." /> $choice</label><br />";
+    }
+    return theme('form_element', $title, $choices, $description, $required, _form_get_error($name));
+  }
+}
+
+function form_textfield($title, $name, $value, $size, $maxlength, $description = NULL, $attributes = NULL, $required = FALSE) {
   $size = $size ? " size=\"$size\"" : "";
-  return theme("form_element", $title, "<input type=\"text\" maxlength=\"$maxlength\" class=\"form-text\" name=\"edit[$name]\" id=\"$name\"$size value=\"". check_form($value) ."\"". drupal_attributes($attributes) ." />", $description, $name);
+  return theme("form_element", $title, "<input type=\"text\" maxlength=\"$maxlength\" class=\"". _form_get_class('form-text', $required, _form_get_error($name)) ."\" name=\"edit[$name]\" id=\"$name\"$size value=\"". check_form($value) ."\"". drupal_attributes($attributes) ." />", $description, $name, $required, _form_get_error($name));
 }
 
-function form_password($title, $name, $value, $size, $maxlength, $description = NULL, $attributes = NULL) {
+function form_password($title, $name, $value, $size, $maxlength, $description = NULL, $attributes = NULL, $required = FALSE) {
   $size = $size ? " size=\"$size\"" : "";
-  return theme("form_element", $title, "<input type=\"password\" class=\"form-password\" maxlength=\"$maxlength\" name=\"edit[$name]\" id=\"$name\"$size value=\"". check_form($value) ."\"". drupal_attributes($attributes) ." />", $description, $name);
+  return theme("form_element", $title, "<input type=\"password\" class=\"". _form_get_class('form-password', $required, _form_get_error($name)) ."\" maxlength=\"$maxlength\" name=\"edit[$name]\" id=\"$name\"$size value=\"". check_form($value) ."\"". drupal_attributes($attributes) ." />", $description, $name, $required, _form_get_error($name));
 }
 
-function form_textarea($title, $name, $value, $cols, $rows, $description = NULL, $attributes = NULL) {
+function form_textarea($title, $name, $value, $cols, $rows, $description = NULL, $attributes = NULL, $required = FALSE) {
   $cols = $cols ? " cols=\"$cols\"" : "";
   module_invoke_all("textarea", $name);  // eg. optionally plug in a WYSIWYG editor
-  return theme("form_element", $title, "<textarea wrap=\"virtual\"$cols rows=\"$rows\" name=\"edit[$name]\" id=\"$name\"". drupal_attributes($attributes) .">". check_form($value) ."</textarea>", $description, $name);
+  return theme("form_element", $title, "<textarea wrap=\"virtual\"$cols rows=\"$rows\" name=\"edit[$name]\" id=\"$name\"". drupal_attributes($attributes) .">". check_form($value) ."</textarea>", $description, $name, $required, _form_get_error($name));
 }
 
-function form_select($title, $name, $value, $options, $description = NULL, $extra = 0, $multiple = 0) {
+/**
+ * Outputs a form select item.
+ *
+ * @param $options Array containing the options to choose from. The basic format
+ *                 is "value" => "label". If you want to group options together
+ *                 (with <optgroup> tags), the format becomes "group name" =>
+ *                 $options, where $options is an array of "value" => "label"
+ *                 pairs.
+ *
+ *                 Examples:
+ *                 @verbatim $flavor = array(1 => "Vanilla", 2 => "Chocolate", 3 => "Strawberry"); @endverbatim
+ *                 @verbatim $order = array("Food" => array(1 => "Ice cream", 2 => "Hamburger"), "Drink" => array(1 => "Cola", 2 => "Water")); @endverbatim
+ */
+function form_select($title, $name, $value, $options, $description = NULL, $extra = 0, $multiple = 0, $required = FALSE) {
+  $select = '';
   foreach ($options as $key => $choice) {
-    $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($value == $key ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>";
+    if (is_array($choice)) {
+      $select .= "<optgroup label=\"$key\">";
+      foreach ($choice as $key => $choice) {
+        $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($value == $key ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>";
+      }
+      $select .= "</optgroup>";
+    }
+    else {
+      $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($value == $key ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>";
+    }
   }
-  return theme("form_element", $title, "<select name=\"edit[$name]". ($multiple ? "[]" : "") ."\"". ($multiple ? " multiple " : "") . ($extra ? " $extra" : "") ." id=\"$name\">$select</select>", $description, $name);
+  return theme("form_element", $title, "<select name=\"edit[$name]". ($multiple ? "[]" : "") ."\"". ($multiple ? " multiple=\"multiple\" " : "") . ($extra ? " $extra" : "") ." id=\"$name\">$select</select>", $description, $name, $required, _form_get_error($name));
 }
 
-function form_file($title, $name, $size, $description = NULL) {
-  return theme("form_element", $title, "<input type=\"file\" class=\"form-file\" name=\"edit[$name]\" id=\"$name\" size=\"$size\" />\n", $description, $name);
+function form_file($title, $name, $size, $description = NULL, $required = FALSE) {
+  return theme("form_element", $title, "<input type=\"file\" class=\"". _form_get_class('form-file', $required, _form_get_error($name)) ."\" name=\"edit[$name]\" id=\"$name\" size=\"$size\" />\n", $description, $name, $required, _form_get_error($error));
 }
 
 function form_hidden($name, $value) {
@@ -1042,9 +1150,7 @@
     $script = (strpos($_SERVER["SERVER_SOFTWARE"], "Apache") === false) ? "index.php" : "";
   }
 
-  if ($alias = drupal_get_path_alias($url)) {
-    $url = $alias;
-  }
+  $url = drupal_get_path_alias($url);
 
   if (isset($fragment)) {
     $fragment = "#$fragment";
@@ -1102,7 +1208,7 @@
 }
 
 function l($text, $url, $attributes = array(), $query = NULL, $fragment = NULL, $absolute = NULL) {
-  if ($url == $_GET['q']) {
+  if (drupal_get_normal_path($url) == $_GET['q']) {
     if (isset($attributes['class'])) {
       $attributes['class'] .= ' active';
     }
@@ -1257,7 +1363,7 @@
 set_error_handler("error_handler");
 
 // spit out the correct charset http header
-header("Content-Type: text/html; charset=utf-8");
+drupal_set_header("Content-Type: text/html; charset=utf-8");
 
 // initialize the _GET["q"] prior to loading the modules and invoking their 'init' hook:
 if (!empty($_GET["q"])) {
diff -urN drupal-4.4.1/modules/archive.module drupal4blog/modules/archive.module
--- drupal-4.4.1/modules/archive.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/archive.module	Tue Jun  8 14:19:14 2004
@@ -26,28 +26,28 @@
   $end_of_today = mktime(23, 59, 59, date("n", $offset), date("d", $offset), date("Y", $offset)) + $user->timezone;
 
   // Extract the requested date:
-  if ($edit["year"] && $edit["month"] && $edit["day"]) {
+  if ($edit["year"]) {
     $year = $edit["year"];
     $month = $edit["month"];
     $day = $edit["day"];
 
-    $requested = mktime(0, 0, 0, $month, $day, $year) + $user->timezone;
+    $requested = mktime(0, 0, 0, $month, ($day ? $day : 1), $year) + $user->timezone;
   }
-  else if (arg(0) == "archive" && arg(3)) {
+  else if (arg(0) == "archive" && arg(2)) {
     $year = arg(1);
     $month = arg(2);
     $day = arg(3);
 
-    $requested = mktime(0, 0, 0, $month, $day, $year) + $user->timezone;
+    $requested = mktime(0, 0, 0, $month, ($day ? $day : 1), $year) + $user->timezone;
   }
   else {
-    $year = date("Y", time());
-    $month  = date("n", time());
-    $day = date("d", time());
-
     $requested = $end_of_today + $user->timezone;
   }
 
+  # $day = (isset($day) ? $day : date('d'));
+  $month = (isset($month) ? $month : date('n'));
+  $year = (isset($year) ? $year : date('Y'));
+
   $start_of_month = mktime(0, 0, 0, $month, 1, $year);
 
   // Extract first day of the month:
@@ -58,7 +58,10 @@
 
   $end_of_month = mktime(23, 59, 59, $month, $last, $year);
 
-  $cache = cache_get("archive:calendar:$day-$month-$year");
+  if ($day) 
+    $cache = cache_get("archive:calendar:$day-$month-$year");
+  else
+    $cache = cache_get("archive:calendar:$month-$year");
 
   if (!empty($cache)) {
     return $cache->data;
@@ -66,9 +69,9 @@
 
   // Calculate previous and next months dates and check for shorter months (28/30 days)
   $prevmonth = mktime(23, 59, 59, $month - 1, 1, $year);
-  $prev = mktime(23, 59, 59, $month - 1, min(date("t", $prevmonth), $day), $year);
+  $prev = mktime(23, 59, 59, $month - 1, min(date("t", $prevmonth), ($day ? $day : 1)), $year);
   $nextmonth = mktime(23, 59, 59, $month + 1, 1, $year);
-  $next = mktime(23, 59, 59, $month + 1, min(date("t", $nextmonth), $day), $year);
+  $next = mktime(23, 59, 59, $month + 1, min(date("t", $nextmonth), ($day ? $day : 1)), $year);
 
 
   $result = db_query("SELECT created FROM {node} WHERE status = 1 AND created > $start_of_month AND created < $end_of_month");
@@ -83,13 +86,17 @@
   $output .= "\n<!-- calendar -->\n";
   $output .= "<div class=\"calendar\">";
   $output .= "<table summary=\"". t('A calendar to browse the archives') .".\">\n";
-  $output .= " <tr><td colspan=\"7\" class=\"header-month\">". l("&laquo;", "archive/". date("Y/m/d", $prev)) ." ". format_date($requested, "custom", "F") . date(" Y", $requested) ." ". ($nextmonth <= time() ? l("&raquo;", "archive/". date("Y/m/d", $next)) : "&nbsp;") ."</td></tr>\n";
+  if (variable_get("archive_by",1) == 2) {
+    $output .= " <tr><td colspan=\"7\" class=\"header-month\">". l("&laquo;", "archive/". date("Y/m/d", $prev)) ." ". l(format_date($requested, "custom", "F") . date(" Y", $requested), "archive/" . date("Y/m/d", $requested)) ." ". ($nextmonth <= time() ? l("&raquo;", "archive/". date("Y/m/d", $next)) : "&nbsp;") ."</td></tr>\n";
+  } else {
+    $output .= " <tr><td colspan=\"7\" class=\"header-month\">". l("&laquo;", "archive/". date("Y/m", $prev)) ." ". l(format_date($requested, "custom", "F") . date(" Y", $requested), "archive/" . date("Y/m", $requested)) ." ". ($nextmonth <= time() ? l("&raquo;", "archive/". date("Y/m", $next)) : "&nbsp;") ."</td></tr>\n";
+  }
 
   // First day of week (0 => Sunday, 1 => Monday, ...)
   $cday = $weekstart = variable_get("default_firstday", 0);
 
   // Last day of week
-  ($weekstart - 1 == -1) ? $lastday = 6 : $lastday = $weekstart - 1;
+  ($weekstart == 0) ? $lastday = 6 : $lastday = $weekstart - 1;
 
   // Generate the days of the week:
   $firstcolumn = mktime(0, 0, 0, 3, 20 + $weekstart, 1994);
@@ -129,7 +136,7 @@
       $daytext = "<div>$nday</div>";
       $dayclass = "day-normal";
     }
-    if ($date == $requested) {
+    if ($date == $requested && isset($day)) {
       $output .= "  <td class=\"day-selected\">$daytext</td>\n";
     }
     else if ($date == $start_of_today) {
@@ -161,7 +168,10 @@
 
   $output .= "</table></div>\n\n";
 
-  cache_set("archive:calendar:$day-$month-$year", $output, 1);
+  if ($day) 
+    cache_set("archive:calendar:$day-$month-$year", $output, 1);
+  else
+    cache_set("archive:calendar:$month-$year", $output, 1);
 
   return $output;
 }
@@ -216,19 +226,35 @@
     $day = arg(3);
   }
 
+  switch (variable_get("archive_by",1)) {
+    case 2:
+      $day = (isset($day) ? $day : date('d'));
+    case 1:
+      $month = (isset($month) ? $month : date('n'));
+    default:
+      $year = (isset($year) ? $year : date('Y'));
+  }
+
   $date = mktime(0, 0, 0, $month, $day, $year) - $user->timezone;
-  $date_end = mktime(0, 0, 0, $month, $day + 1, $year) - $user->timezone;
+  if ($month == 0) {
+    $date_end = mktime(0, 0, 0, $month, $day, $year + 1) - $user->timezone;
+  } else if ($day == 0) {
+    $date_end = mktime(0, 0, 0, $month + 1, $day, $year) - $user->timezone;
+  } else {
+    $date_end = mktime(0, 0, 0, $month, $day + 1, $year) - $user->timezone;
+  }
 
   /*
   ** Prepare the values of the form fields:
   */
 
-  $years = drupal_map_assoc(range(2000, 2005));
-  $months = array(1 => t("January"), 2 => t("February"), 3 => t("March"), 4 => t("April"), 5 => t("May"), 6 => t("June"), 7 => t("July"), 8 => t("August"), 9 => t("September"), 10 => t("October"), 11 => t("November"), 12 => t("December"));
-  $days = drupal_map_assoc(range(0, 31));
+  $years = drupal_map_assoc(range(2000, 2010));
+  $months = array(0 => t("All"), 1 => t("January"), 2 => t("February"), 3 => t("March"), 4 => t("April"), 5 => t("May"), 6 => t("June"), 7 => t("July"), 8 => t("August"), 9 => t("September"), 10 => t("October"), 11 => t("November"), 12 => t("December"));
+
+  $days = array_merge(array(0 => "All"), drupal_map_assoc(range(1, 31)));
 
   $start = "<div class=\"container-inline\">";
-  $start .= form_select("", "year", ($year ? $year : date("Y")), $years). form_select("", "month", ($month ? $month : date("m")), $months) . form_select("", "day", ($day ? $day : date("d")), $days) . form_submit(t("Show"));
+  $start .= form_select("", "year", $year, $years). form_select("", "month", $month, $months) . form_select("", "day", $day, $days) . form_submit(t("Show"));
   $start .= "</div>";
   $output .= form($start);
 
@@ -237,21 +263,27 @@
   ** selected.
   */
 
-  if ($year && $month && $day) {
-    $result = db_query_range("SELECT nid FROM {node} WHERE status = '1' AND created > %d AND created < %d ORDER BY created", $date, $date_end, 0, 20);
+  $result = db_query_range("SELECT nid FROM {node} WHERE status = '1' AND created > %d AND created < %d ORDER BY created", $date, $date_end, 0, 20);
 
-    while ($nid = db_fetch_object($result)) {
-      $output .= node_view(node_load(array("nid" => $nid->nid)), 1);
-    }
+  while ($nid = db_fetch_object($result)) {
+    $output .= node_view(node_load(array("nid" => $nid->nid)), 1);
   }
+
   print theme("page", $output);
 }
 
 function archive_settings() {
 
   $output .= form_select(t("First day of week"), "default_firstday", variable_get("default_firstday", 0), array(0 => t("Sunday"), 1 => t("Monday"), 2 => t("Tuesday"), 3 => t("Wednesday"), 4 => t("Thursday"), 5 => t("Friday"), 6 => t("Saturday")), t("The first day of the week.  By changing this value you choose how the calendar block is rendered."));
+  $output .= form_select(t("Archive by"), "archive_by", variable_get("archive_by", 1), array(0 => t("Year"), 1 => t("Month"), 2 => t("Day")), t("Default archive by year, day or month."));
 
   return $output;
+}
+
+function archive_blogadmin($type) {
+  if ($type == "options") {
+    return array(l(t("Archive options"),"admin/system/modules/archive") => t("Configure archive options."));
+  }
 }
 
 ?>
diff -urN drupal-4.4.1/modules/blog.module drupal4blog/modules/blog.module
--- drupal-4.4.1/modules/blog.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/blog.module	Tue Jun  8 14:19:14 2004
@@ -2,17 +2,30 @@
 // $Id: blog.module,v 1.168.2.1 2004/03/15 19:41:47 dries Exp $
 
 function blog_settings() {
-  $output = form_textarea(t("Explanation or submission guidelines"), "blog_help", variable_get("blog_help", ""), 70, 4, t("This text is displayed at the top of the blog submission form.  It's useful for helping or instructing your users."));
-  $output .= form_select(t("Minimum number of words in a blog entry"), "minimum_blog_size", variable_get("minimum_blog_size", 0), drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), t("The minimum number of words a personal blog entry should contain.  This is useful to rule out submissions that do not meet the site's standards, such as short test posts."));
+  $group = form_textarea(t("Explanation or submission guidelines"), "blog_help", variable_get("blog_help", ""), 70, 4, t("This text is displayed at the top of the blog submission form.  It's useful for helping or instructing your users."));
+  $group .= form_select(t("Minimum number of words in a blog entry"), "minimum_blog_size", variable_get("minimum_blog_size", 0), drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), t("The minimum number of words a blog entry should contain.  This is useful to rule out submissions that do not meet the site's standards, such as short test posts."));
+  $group .= form_select(t("Display blog link in each entry"), "blog_link", variable_get("blog_link", 1), array( 0 => "No", 1 => "Yes" ), t("Each blog entry has a link which looks like 'user's blog'. Some people may prefer not to display such link."));
+  $output = form_group(t('Blog Entry settings'), $group);
+
+  $group = form_textfield(t("Block title"), "blog_block_title", variable_get("blog_block_title", "Recent Entries"), 70, 180, t("") . " " . t("Title of the Blog block, e.g. Recent Entries"));
+  $group .= form_select(t("Numbers of Entries"), "blog_block_entries", variable_get("blog_block_entries", 10), drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 50)), t("Numbers of latest entries to display in block."));
+  $output .= form_group(t('Blog Block settings'), $group);
+
+  $group = form_select(t("RSS feed"), "blog_syndicate_rss", variable_get("blog_syndicate_rss",0), array(0 => "Full entry in RSS", 1 => "Teaser in RSS"), t("Format of the entries in syndication"));
+  /* 
+  $group .= form_select(t("Number of entries"), "blog_syndicate_entries", variable_get("blog_syndicate_entries", 15), drupal_map_assoc(array(10, 15, 20, 25, 30, 50)), t("Number of last entries to display in syndicate."));
+  */
+  $output .= form_group(t('Syndicate settings'),$group);
+
   return $output;
 }
 
 function blog_node_name($node) {
-  return t("personal blog entry");
+  return t("blog entry");
 }
 
 function blog_perm() {
-  return array("edit own blog");
+  return array("create blog entry", "edit own blog");
 }
 
 function blog_access($op, $node) {
@@ -23,7 +36,7 @@
   }
 
   if ($op == "create") {
-    return user_access("edit own blog") && $user->uid;
+    return user_access("create blog entry");
   }
 
   if ($op == "update") {
@@ -36,13 +49,43 @@
 
 }
 
+/**
+ * Respond to node insertion.
+ */
+function blog_insert($node) {
+  db_query("INSERT INTO {blog} (nid, extended, excerpt, format) VALUES (%d, '%s', '%s', %d)", $node->nid, $node->extended, $node->excerpt, $node->format);
+}
+
+/**
+ * Respond to node updating.
+ */
+function blog_update($node) {
+  db_query("UPDATE {blog} SET extended = '%s', excerpt = '%s', format = %d WHERE nid = %d", $node->extended, $node->excerpt, $node->format, $node->nid);
+}
+
+/**
+ * Respond to node deletion
+ */
+function blog_delete(&$node) {
+  db_query("DELETE FROM {blog} WHERE nid = %d", $node->nid);
+}
+
+/**
+ * Load node-type-specific information.
+ */
+function blog_load($node) {
+  return db_fetch_object(db_query("SELECT extended, excerpt, format FROM {blog} WHERE nid = %d", $node->nid));
+}
+
 function blog_user($type, &$edit, &$user) {
   switch ($type) {
     case "view_public":
     case "view_private":
+      $output = "";
       if (user_access("edit own blog", $user)) {
-        return form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name)))));
+        $output .= form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name)))));
       }
+      return $output;
   }
 }
 
@@ -54,7 +97,7 @@
       $output .= t("
       <p>Drupal's blog module allows registered users to maintain an online weblog (commonly known as a blog), often referred to as an online journal or diary.  These can be filled with daily thoughts, poetry, boneless blabber, spiritual theories, intimate details, valuable experiences, cynical rants, semi-coherent comments, writing experiments, artistic babblings, critics on current facts, fresh insights, diverse dreams, chronicles and mumbling madness available for public consumption.</p>
       <p>Blogs are made up of individual entries (nodes) that are timestamped and are typically viewed by day as you would a diary.  Blogs often contain links to things you've seen and/or agree/disagree with.  A typical example of a long term blog can be seen at %scripting-com.</p>
-      <p>The blog module adds a \"user blogs\" navigation link to the site, which takes any visitor to a page that displays the most recent blog entries from all the users on the site. Personal user menus gain a \"create a blog entry\" link (which takes you to a submission form) and a \"view personal blog\" link (which displays your blog entries as other people will see them).  On the bottom of each of your own blog entries, there is an \"edit this blog entry\" link that lets you edit or delete that entry.</p>
+      <p>The blog module adds a \"user blogs\" navigation link to the site, which takes any visitor to a page that displays the most recent blog entries from all the users on the site. Personal user menus gain a \"create a blog entry\" link (which takes you to a submission form) and a \"view blog\" link (which displays your blog entries as other people will see them).  On the bottom of each of your own blog entries, there is an \"edit this blog entry\" link that lets you edit or delete that entry.</p>
       <p>If a user has the ability to post blogs, then the import module (news aggregator) will display a blog-it link <strong>(b)</strong> next to each news item in its lists.  Click on this and you will be taken to the blog submission form, with the title, a link to the item, and a link to the source into the body text already in the text box, ready for you to add your explanation.  This actively encourages people to add blog entries about things they see and hear elsewhere in the Drupal site and from your syndicated partner sites.</p>", array("%scripting-com" => "<a href=\"http://www.scripting.com/\">http://www.scripting.com/</a>"));
       break;
     case 'admin/system/modules#description':
@@ -84,18 +127,18 @@
     $account = $user;
   }
 
-  $result = db_query_range("SELECT n.nid, n.title, n.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND u.uid = %d AND n.status = 1 ORDER BY n.nid DESC", $uid, 0, 15);
+  $result = db_query_range("SELECT * FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND u.uid = %d AND n.status = 1 ORDER BY n.nid DESC", $uid, 0, variable_get("blog_syndicate_entries", 15));
   $channel["title"] = $account->name ."'s blog";
-  $channel["link"] = url("blog/view/$uid", NULL, NULL, TRUE);
-  $channel["description"] = $term->description;
+  $channel["link"] = url("blog/$uid", NULL, NULL, TRUE);
+  $channel["description"] = variable_get('site_mission','');
   node_feed($result, $channel);
 }
 
 function blog_feed_last() {
-  $result = db_query_range("SELECT n.nid, n.title, n.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.nid DESC", 0, 15);
-  $channel["title"] = variable_get("site_name", "drupal") ." blogs";
-  $channel["link"] = url("blog");
-  $channel["description"] = $term->description;
+  $result = db_query_range("SELECT *  FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.nid DESC", 0, variable_get("blog_syndicate_entries",15));
+  $channel["title"] = variable_get("site_name", "drupal");
+  $channel["link"] = url("", NULL, NULL, TRUE);
+  $channel["description"] = variable_get('site_mission','');
   node_feed($result, $channel);
 }
 
@@ -112,7 +155,7 @@
   $output .= theme('pager', NULL, variable_get("default_nodes_main", 10));
   $output .= theme('xml_icon', url("blog/feed/$account->uid"));
 
-  drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS - '. $title .'" href="'. url("blog/feed/$account->uid") .'" />');
+  drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS - '. $title .'" href="'. url("blog/feed/$account->uid", NULL, NULL, TRUE) .'" />');
   print theme("page", $output, $title);
 }
 
@@ -129,7 +172,7 @@
   $output .= theme('pager', NULL, variable_get("default_nodes_main", 10));
   $output .= theme('xml_icon', url('blog/feed'));
 
-  drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS - blogs" href="'. url('blog/feed') .'" />');
+  drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS - blogs" href="'. url('blog/feed', NULL, NULL, TRUE) .'" />');
   print theme("page", $output);
 }
 
@@ -139,7 +182,7 @@
   ** Validate the size of the blog:
   */
 
-  if (isset($node->body) && count(explode(" ", $node->body)) < variable_get("minimum_blog_size", 0)) {
+  if (isset($node->body) && count(explode(" ", $node->body." ".$node->extended)) < variable_get("minimum_blog_size", 0)) {
     $error["body"] = theme("error", t("The body of your blog is too short."));
   }
 
@@ -170,7 +213,14 @@
     $output .= implode("", taxonomy_node_form("blog", $node));
   }
 
-  $output .= form_textarea(t("Body"), "body", $node->body, 60, 15, $error["body"] ? $error["body"] : filter_tips_short());
+  $output .= form_textarea(t("Entry Body"), "body", $node->body, 60, 10, "");
+
+  $output .= form_textarea(t("Extended Entry"), "extended", $node->extended, 60, 15, "");
+
+  $output .= form_textarea(t("Excerpt"), "excerpt", $node->excerpt, 60, 5, $error["body"] ? $error["body"] : filter_tips_short());
+
+  $format_type = array(-1 => "No formatting", 0 => "Convert Line Breaks", 1 => "Simple Wiki");
+  $output .= form_select(t("Text Formatting"), "format", $node->format, $format_type, t("'Convert Line Breaks' enclose your paragraph in &lt;p&gt; and &lt;/p&gt; tag for you which is what most bloggers are used to. 'No formatting' assume you will do all HTML formatting. 'Simple Wiki' supports very basic Wiki Formatting Rules."));
 
   return $output;
 }
@@ -197,7 +247,7 @@
 }
 
 function blog_content($node, $main = 0) {
-  return node_prepare($node, $main);
+  return _blog_prepare($node, $main);
 }
 
 function blog_view($node, $main = 0, $page = 0) {
@@ -223,7 +273,7 @@
   if ($type == "system") {
     if (user_access("edit own blog")) {
       menu("node/add/blog", t("blog entry"), "node_page", 0);
-      menu("blog/". $user->uid, t("my blog"), "blog_page", 1);
+      menu("blog/". $user->uid, t("blog archives"), "blog_page", 1);
     }
     if (user_access("access content")) {
       menu("blog", t("blogs"), "blog_page", 0, MENU_HIDE);
@@ -235,11 +285,13 @@
   }
 
   if ($type == "node" && $node->type == "blog") {
-    if (blog_access("update", $node)) {
-      $links[] = l(t("edit this blog entry"), "node/edit/$node->nid", array("title" => t("Edit this blog entry.")));
-    }
-    elseif (arg(0) != 'blog' && arg(1) != $node->uid) {
-      $links[] = l(t("%username's blog", array("%username" => $node->name)), "blog/$node->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $node->name))));
+    if (variable_get("blog_link",1)) {
+      if (blog_access("update", $node) && !user_access('administer nodes')) {
+        $links[] = l(t("edit blog"), "node/edit/$node->nid", array("title" => t("Edit this blog entry.")));
+      }
+      elseif (arg(0) != 'blog' && arg(1) != $node->uid) {
+        $links[] = l(t("%username's blog", array("%username" => $node->name)), "blog/$node->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $node->name))));
+      }
     }
   }
 
@@ -254,12 +306,220 @@
   }
   else {
     if (user_access("access content")) {
-      $block["content"] = node_title_list(db_query_range("SELECT n.title, n.nid FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.nid DESC", 0, 10));
+      $block["content"] = node_title_list(db_query_range("SELECT n.title, n.nid FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.nid DESC", 0, variable_get("blog_block_entries",10)));
       $block["content"] .= "<div class=\"more-link\">". l(t("more"), "blog", array("title" => t("Read the latest blog entries."))) ."</div>";
-      $block["subject"] = t("Blogs");
+      $block["subject"] = variable_get("blog_block_title", "Recent Entries");
     }
     return $block;
   }
+}
+
+
+/*
+ * modified from check_output() from filter.module
+ */
+function check_output_without_nl2br($text) {
+  if (isset($text)) {
+    // Filter content on output:
+    $filters = filter_list();
+
+    // Give filters the chance to escape HTML-like data such as code or formulas
+    // (from this point on, the input can be treated as HTML)
+    if (variable_get("filter_html", FILTER_HTML_DONOTHING) != FILTER_HTML_ESCAPE) {
+      foreach ($filters as $module => $filter) {
+        $text = module_invoke($module, "filter", "prepare", $text);
+      }
+    }
+
+    // HTML handling is done before all regular filtering activities
+    $text = filter_default($text);
+
+    // Regular filtering
+    foreach ($filters as $module => $filter) {
+      $text = module_invoke($module, "filter", "process", $text);
+    }
+  }
+  else {
+    $text = message_na();
+  }
+
+  return $text;
+}
+
+function blog_blogadmin($type = "") {
+  $list = array();
+
+  if ($type == "block") {
+    if (user_access("create blog entry"))
+      $list[] = l(t("new blog entry"), "node/add/blog");
+  } else if ($type == "options") {
+    if (user_access("administer site configuration")) {
+      $list[l(t("Blog entries options"),"admin/system/modules/blog")] = t("Define options for your blog entries and RSS syndication.");
+    }
+  }
+
+  return $list;
+}
+
+function _blog_process_format($node,$str) {
+  if ($node->format < 0) {
+    return check_output_without_nl2br($str);
+  }
+
+  // Convert Line Breaks
+  if ($node->format == 0) {
+    $paras = preg_split("/\r?\n\r?\n/",$str);
+    foreach ($paras as $id => $p) {
+      if (!$p) continue;
+      $paras[$id] = "<p>". check_output($p) ."</p>";
+    }
+    return implode("\n\n",$paras);
+  }
+
+  // Simple Wiki
+  if ($node->format == 1) {
+    //include_once "modules/wiki.module";
+    //return _wiki_transform($str);
+    $wiki_search = array(
+      "/('*)'''(.*?)'''/",
+      "/''(.*)''/",
+      "/----+/",
+      "/\[(http|https|ftp|mailto):(\S+)\s+([^\]]+)\]/",
+      "/^(http|https|ftp):\/\/(\S+)(gif|jpg|png|bmp|jpeg)/",
+      "/([^\"\[])(http|https|ftp|mailto):(\S+)/",
+    );
+
+    $wiki_replace = array(
+      "\\1<strong>\\2</strong>",
+      "<em>\\1</em>",
+      "<hr/>",
+      "<a href=\"\\1:\\2\">\\3</a>",
+      "<img src=\"\\1://\\2\\3\" /><br/>",
+      "\\1<a href=\"\\2:\\3\">\\2:\\3</a>",
+    );
+
+    $paras = preg_split("/\r?\n\r?\n/",$str);
+    foreach ($paras as $id => $p) {
+      $paras[$id] = "<p>" .  check_output(
+         preg_replace($wiki_search,$wiki_replace,$p)) . "</p>";
+    }
+    return implode("\n\n",$paras);
+  }
+
+  return $str;
+}
+
+function _blog_prepare($node,$main = 0) {
+    $node->readmore = (strlen($node->extended) > 0 
+                   || strlen($node->excerpt > 0));
+    if ($main == 0) {
+        $str = rtrim($node->body) . "\n\n" . rtrim($node->extended);
+        $node->body = _blog_process_format($node,$str);
+    } else {
+        if (strlen(rtrim($node->excerpt)) > 0) 
+            $node->teaser = _blog_process_format($node,rtrim($node->excerpt));
+        else
+            $node->teaser = _blog_process_format($node,rtrim($node->body));
+    }
+    return $node;
+}
+
+function _blog_format_rss_item($title, $link, $description, $args = array()) {
+  // arbitrary elements may be added using the $args associative array
+  $output = "<item>\n";
+  $output .= " <title>". drupal_specialchars(strip_tags($title)) ."</title>\n";
+  $output .= " <link>". drupal_specialchars(strip_tags($link)) ."</link>\n";
+  $output .= " <description>". $description ."</description>\n";
+  foreach ($args as $key => $value) {
+    $output .= "<$key>". drupal_specialchars(strip_tags($value)) ."</$key>\n";
+  }
+  $output .= "</item>\n";
+
+  return $output;
+}
+
+function blog_feed_item($node) {
+  $link = url("node/view/$node->nid", NULL, NULL, TRUE);
+
+  switch (variable_get("blog_syndicate_rss",0)) {
+    case 0:
+      $node = _blog_prepare($node,0);
+      $desc = drupal_specialchars($node->body);
+      break;
+    case 1:
+      $node = _blog_prepare($node,1);
+      $node->teaser .= "...<a href=\"$base_url/$links\">read more</a>";
+      $desc = drupal_specialchars($node->teaser);
+      break;
+  }
+
+  $terms = array();
+  if (module_exist("taxonomy")) {
+    foreach (taxonomy_node_get_terms($node->nid) as $term) {
+      $terms = array_merge($terms,array("dc:subject" => $term->name));
+      break;
+    }
+  }
+
+  return _blog_format_rss_item($node->title, $link, $desc, 
+    array_merge($terms, array(
+      'dc:date' => encode_iso8601($node->changed),
+      'dc:creator' => $node->name,
+    ))
+  );
+}
+
+// xmlrpc.php already has iso8601_encode but that does not handle
+// timezone which we need for RSS
+function encode_iso8601($time) {
+  $tzd = date('O',$time);
+  $tzd = $tzd[0] . str_pad((int) ($tzd/100),2,"0",STR_PAD_LEFT) . ":" .
+                   str_pad((int) ($tzd%100),2,"0",STR_PAD_LEFT);
+  $date = date('Y-m-d\TG:i:s',$time).$tzd;
+  return $date;
+}
+
+function blog_search($keys) {
+
+  // Return the results of performing a search using the indexed search
+  // for this particular type of node.
+  //
+  // Pass an array to the 'do_search' function which dictates what it
+  // will search through, and what it will search for
+  //
+  // "keys"'s value is the keywords entered by the user
+  //
+  // "type"'s value is used to identify the node type in the search
+  // index.
+  //
+  // "select"'s value is used to relate the data from the specific nodes
+  // table to the data that the search_index table has in it, and the the
+  // do_search functino will rank it.
+  //
+  // The select must always provide the following fields - lno, title,
+  // created, uid, name, count
+  //
+  $find = do_search(array('keys' => $keys, 'type' => 'blog', 'select' => "select s.lno as lno, n.title as title, n.created as created, u.uid as uid, u.name as name, s.count as count FROM {search_index} s, {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE s.lno = n.nid AND s.type = 'blog' AND s.word like '%' AND n.status = 1"));
+
+  return array(t('Matching blog entries ranked in order of relevance'), $find);
+}
+
+function blog_update_index() {
+
+  // Return an array of values to dictate how to update the search index
+  // for this particular type of node.
+  //
+  // "last_update"'s value is used with variable_set to set the
+  // last time this node type had an index update run.
+  //
+  // "node_type"'s value is used to identify the node type in the search
+  // index.
+  //
+  // "select"'s value is used to select the node id and text fields from
+  // the table we are indexing. In this case, we also check against the
+  // last run date for the nodes update.
+
+  return array('last_update' => 'blog_cron_last', 'node_type' => 'blog', 'select' => "SELECT b.nid as lno, n.title as text1, n.body as text2, b.extended as text3, b.excerpt as text4 FROM {node} n, {blog} b WHERE b.nid = n.nid AND n.status = 1 AND n.moderate = 0 and (n.created > " . variable_get('blog_cron_last', 1) . " or n.changed > " .  variable_get('blog_cron_last', 1) . ")");
 }
 
 ?>
diff -urN drupal-4.4.1/modules/blogadmin.module drupal4blog/modules/blogadmin.module
--- drupal-4.4.1/modules/blogadmin.module	Thu Jan  1 07:30:00 1970
+++ drupal4blog/modules/blogadmin.module	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,92 @@
+<?php
+
+function blogadmin_help($section) {
+  switch ($section) {
+    case "admin/system/modules#description":
+      return t("Provides simple blog administration block. (Requires: drupal4blog patches)");
+  }
+}
+
+function blogadmin_link($type) {
+  if ($type == "system")
+    menu("blogadmin", t("blogadmin"), "blogadmin_page", 0, MENU_HIDE);
+}
+
+function blogadmin_page() {
+  $type = strip_tags(arg(1));
+
+  switch (arg(1)) {
+    case "options":
+      $output = blogadmin_default($type);
+      break;
+      
+    default:
+      $output = blogadmin_default("page");
+      break;
+  }
+  print $output;
+}
+
+function blogadmin_default($type = "page") {
+  $menu = module_invoke_all("blogadmin",$type);
+
+  /*
+  ** These should goes into the respective *_blogadmin() but leave it here
+  ** because we do not want to change too many modules
+  */
+  if ($type == "options") {
+  
+    if (user_access("administer site configuration")) {
+      if (module_exist("system"))
+        $menu[l(t("Theme options"),"admin/system/themes/simple")] = 
+          t("Configure drupal look and feel.");
+    }
+  }
+
+  $output = "";
+
+  foreach ($menu as $l => $m) {
+    $output .= "<ul><li>".$l."<ul><li>".$m."</li></ul></li></ul>\n";
+  }
+
+  if ($type == "options") return theme("page",$output, t("Options"));
+
+  return theme("page",$output, t("Blog Admin"));
+}
+
+function blogadmin_block($op = "list", $delta = 0) {
+  if ($op == "list") {
+    $block[0]['info'] = t("Blog Admin");
+    return $block;
+  }
+
+  $block["subject"] = t("Blog Admin");
+
+  $menu = module_invoke_all("blogadmin","block");
+  /*
+  ** These should goes into the respective *_blogadmin() but leave it here
+  ** because we do not want to change too many modules
+  */
+  if (user_access("administer users"))
+    if (module_exist("user"))
+      $menu[] = l(t("users accounts"),"admin/user");
+
+  if (user_access("administer site configuration"))
+    if (module_exist("system"))
+      $menu[] = l(t("general config"),"admin/system");
+
+  if (user_access("administer site configuration"))
+    if (module_exist("block"))
+      $menu[] = l(t("sidebar config"),"admin/system/block");
+
+  if (user_access("administer site configuration"))
+    $menu[] = l(t("features options"),"blogadmin/options");
+
+  
+  if (!empty($menu))
+    $block["content"] = theme_item_list($menu);
+
+  return $block;
+}
+
+?>
diff -urN drupal-4.4.1/modules/blogapi.module drupal4blog/modules/blogapi.module
--- drupal-4.4.1/modules/blogapi.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/blogapi.module	Tue Jun  8 14:19:14 2004
@@ -1,7 +1,9 @@
 <?php
+// $Id: blogapi.module,v 1.8 2004/05/22 21:32:07 dries Exp $
 
-// $Id: blogapi.module,v 1.5.2.2 2004/04/15 21:41:30 dries Exp $
-
+/**
+ * Implementation of hook_help().
+ */
 function blogapi_help($section) {
   switch ($section) {
     case 'admin/help#blogapi':
@@ -11,6 +13,64 @@
   }
 }
 
+function blogapi_settings() {
+  $output = form_select(t("XML-RPC Engine"), "blogapi_engine", variable_get("blogapi_engine",0), array(0 => "Blogger", 1 => "MetaWeblog", 2 => "Movabletype"), t("Select preferred  engine in RSD."));
+  return $output;
+}
+
+function blogapi_link($type, $node = 0, $main) {
+  if ($type == "system") {
+    menu("blogapi", t("RSD"), "blogapi_blogapi", 0, MENU_HIDE);
+  }
+}
+
+function blogapi_blogapi() {
+  switch (arg(1)) {
+    case "rsd":
+      blogapi_rsd();
+      break;
+    default:
+      drupal_not_found();
+      break;
+  }
+}
+
+function blogapi_rsd() {
+  global $base_url;
+
+  $xmlrpc = $base_url . FILE_SEPARATOR . "xmlrpc.php";
+  $base = url("",NULL,NULL,TRUE);
+  $blogid = 1; # until we figure out how to handle multiple bloggers
+
+  $metaweblog = "false"; $blogger = "false"; $mt = "false";
+  if (variable_get("blogapi_engine",0) == 0) {
+    $blogger = "true";
+  } else if (variable_get("blogapi_engine",0) == 1) {
+    $metaweblog = "true";
+  } else if (variable_get("blogapi_engine",0) == 2) {
+    $mt = "true";
+  }
+
+  print <<<__RSD__
+<?xml version="1.0"?> 
+<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
+  <service>
+    <engineName>Drupal-4-Bloggers-0.6</engineName> 
+    <engineLink>http://www.drupal.org/</engineLink>
+    <homePageLink>$base</homePageLink>
+    <apis>
+      <api name="MetaWeblog" preferred="$metaweblog" apiLink="$xmlrpc" blogID="$blogid" />
+      <api name="Blogger" preferred="$blogger" apiLink="$xmlrpc" blogID="$blogid" />
+      <api name="Movabletype" preferred="$mt" apiLink="$xmlrpc" blogID="$blogid" />
+    </apis>
+  </service>
+</rsd>
+__RSD__;
+}
+
+/**
+ * Implementation of hook_xmlrpc().
+ */
 function blogapi_xmlrpc() {
   $methods = array('blogger.getUsersBlogs' => array('function' => 'blogapi_get_users_blogs'),
                    'blogger.getUserInfo' => array('function' => 'blogapi_get_user_info'),
@@ -22,18 +82,24 @@
                    'metaWeblog.editPost' => array('function' => 'blogapi_edit_post'),
                    'metaWeblog.getPost' => array('function' => 'blogapi_get_post'),
                    'metaWeblog.newMediaObject' => array('function' => 'blogapi_new_media_object'),
-                   'metaWeblog.getCategories' => array('function' => 'blogapi_get_categories'),
+                   'metaWeblog.getCategories' => array('function' => 'blogapi_get_category_list'),
                    'metaWeblog.getRecentPosts' => array('function' => 'blogapi_get_recent_posts'),
+                   'mt.getRecentPostTitles' => array('function' => 'blogapi_get_recent_post_titles'),
                    'mt.getCategoryList' => array('function' => 'blogapi_get_category_list'),
                    'mt.getPostCategories' => array('function' => 'blogapi_get_post_categories'),
-                   'mt.setPostCategories' => array('function' => 'blogapi_set_post_categories')
+                   'mt.setPostCategories' => array('function' => 'blogapi_set_post_categories'),
+                   'mt.getTrackbackPings' => array('function' => 'blogapi_get_trackback_pings'),
+                   'mt.supportedTextFilters' => array('function' => 'blogapi_supported_text_filters'),
+                   'mt.supportedMethods' => array('function' => 'blogapi_supported_methods'),
+                   'mt.publishPost' => array('function' => 'blogapi_publish_post')
                    );
 
   return $methods;
 }
 
-/** api functions */
-
+/**
+ * Blogging API callback. Finds the URL of a user's blog.
+ */
 function blogapi_get_users_blogs($req_params) {
   $params = blogapi_convert($req_params);
   // Remove unused appkey from bloggerAPI.
@@ -43,11 +109,11 @@
 
   $user = blogapi_validate_user($params[1], $params[2]);
   if ($user->uid) {
-    $struct = new xmlrpcval(array('url' => new xmlrpcval(url('blog/' . $user->uid)),
+    $struct = new xmlrpcval(array('url' => new xmlrpcval(url('blog/' . $user->uid, NULL, NULL, true)),
                                   'blogid' => new xmlrpcval($user->uid),
                                   'blogName' => new xmlrpcval($user->name . "'s blog")),
                             'struct');
-    $resp = new xmlrpcval(array($struct), "array");
+    $resp = new xmlrpcval(array($struct), 'array');
     return new xmlrpcresp($resp);
   }
   else {
@@ -55,6 +121,9 @@
   }
 }
 
+/**
+ * Blogging API callback. Returns profile information about a user.
+ */
 function blogapi_get_user_info($req_params) {
   $params = blogapi_convert($req_params);
 
@@ -67,7 +136,7 @@
                                   'firstname' => new xmlrpcval($name[0], 'string'),
                                   'nickname' => new xmlrpcval($user->name, 'string'),
                                   'email' => new xmlrpcval($user->mail, 'string'),
-                                  'url' => new xmlrpcval(url('blog/view/' . $user->uid), 'string')),
+                                  'url' => new xmlrpcval(url('blog/view/' . $user->uid, NULL, NULL, true), 'string')),
                             'struct');
     return new xmlrpcresp($struct);
   }
@@ -76,6 +145,9 @@
   }
 }
 
+/**
+ * Blogging API callback. Inserts a new blog post as a node.
+ */
 function blogapi_new_post($req_params) {
   $params = blogapi_convert($req_params);
 
@@ -89,54 +161,85 @@
     return blogapi_error($user);
   }
 
-  $promote = variable_get("node_promote_blog", 0);
-  $comment = variable_get("node_comment_blog", 2);
-  $moderate = variable_get("node_moderate_blog", 0);
-  $revision = variable_get("node_revision_blog", 0);
-
   // check for bloggerAPI vs. metaWeblogAPI
   if (is_array($params[3])) {
     $title = $params[3]['title'];
     $body = $params[3]['description'];
+    $datecreated = iso8601_decode($params[3]['dateCreated'],1);
+    $mt_allow_comments = $params[3]['mt_allow_comments'];
+    $mt_allow_pings = $params[3]['mt_allow_pings'];
+    $mt_convert_breaks = $params[3]['mt_convert_breaks'];
+    $mt_text_more = $params[3]['mt_text_more'];
+    $mt_excerpt = $params[3]['mt_excerpt'];
+    $mt_keywords = $params[3]['mt_keywords'];
+    $mt_tb_ping_urls = $params[3]['mt_tb_ping_urls'];
   }
   else {
     $title = blogapi_blogger_title($params[3]);
     $body = $params[3];
   }
 
+  $promote = variable_get('node_promote_blog', 1);
+  $comment = variable_get('node_comment_blog', 2);
+  $moderate = variable_get('node_moderate_blog', 0);
+  $revision = variable_get('node_revision_blog', 0);
+
+  if (isset($mt_allow_comments)) {
+    switch ($mt_allow_comments) {
+      case 0: 
+        $comment = 0;
+        break;
+      case 1:
+        $comment = 2;
+        break;
+      case 2:
+        $comment = 1;
+        break;
+    }
+  }
+
   if (!valid_input_data($title, $body)) {
-    return blogapi_error(t("Terminated request because of suspicious input data."));
+    return blogapi_error(t('Terminated request because of suspicious input data.'));
   }
 
   $node = node_validate(array('type' => 'blog',
+                              'created' => $datecreated,
                               'uid' => $user->uid,
                               'name' => $user->name,
                               'title' => $title,
                               'body' => $body,
+                              'extended' => $mt_text_more,
+                              'excerpt' => $mt_excerpt,
+                              'format' => $mt_convert_breaks,
+                              'tb_url' => $mt_tb_ping_urls,
                               'status' => $params[4],
                               'promote' => $promote,
                               'comment' => $comment,
                               'moderate' => $moderate,
                               'revision' => $revision
                               ), $error);
-
   if (count($error) > 0) {
     return blogapi_error($error);
   }
 
-  if (!node_access("create", $node)) {
+  if (!node_access('create', $node)) {
     return blogapi_error(message_access());
   }
 
   $nid = node_save($node);
+  if (function_exists("trackback_send"))
+    trackback_send($node);
   if ($nid) {
-    watchdog("special", "$node->type: added '$node->title' using blog API", l(t("view post"), "node/view/$nid"));
+    watchdog('special', "$node->type: added '$node->title' using blog API", l(t('view post'), "node/view/$nid"));
     return new xmlrpcresp(new xmlrpcval($nid, 'string'));
   }
 
   return blogapi_error(t('error storing post'));
 }
 
+/**
+ * Blogging API callback. Modifies the specified blog node.
+ */
 function blogapi_edit_post($req_params) {
   $params = blogapi_convert($req_params);
   if (count($params) == 6) {
@@ -162,19 +265,47 @@
   if (is_array($params[3])) {
     $title = $params[3]['title'];
     $body = $params[3]['description'];
+    $datecreated = iso8601_decode($params[3]['dateCreated'],1);
+    $mt_allow_comments = $params[3]['mt_allow_comments'];
+    $mt_allow_pings = $params[3]['mt_allow_pings'];
+    $mt_convert_breaks = $params[3]['mt_convert_breaks'];
+    $mt_text_more = $params[3]['mt_text_more'];
+    $mt_excerpt = $params[3]['mt_excerpt'];
+    $mt_keywords = $params[3]['mt_keywords'];
+    $mt_tb_ping_urls = $params[3]['mt_tb_ping_urls'];
   }
   else {
     $title = blogapi_blogger_title($params[3]);
     $body = $params[3];
   }
 
+  if (isset($mt_allow_comments)) {
+    switch ($mt_allow_comments) {
+      case 0: 
+        $comment = 0;
+        break;
+      case 1:
+        $comment = 2;
+        break;
+      case 2:
+        $comment = 1;
+        break;
+    }
+  }
+
   if (!valid_input_data($title, $body)) {
-    return blogapi_error(t("Terminated request because of suspicious input data."));
+    return blogapi_error(t('Terminated request because of suspicious input data.'));
   }
 
   $node->title = $title;
   $node->body = $body;
+  $node->extended = $mt_text_more;
+  $node->excerpt = $mt_excerpt;
+  $node->format = $mt_convert_breaks;
+  $node->tb_url = $mt_tb_ping_urls;
   $node->status = $params[4];
+  $node->created = $datecreated;
+  $node->comment = $comment;
   $node = node_validate($node, $error);
 
   if (count($error) > 0) {
@@ -185,15 +316,22 @@
   foreach ($terms as $term) {
     $node->taxonomy[] = $term->tid;
   }
+
   $nid = node_save($node);
+  if (function_exists("trackback_send"))
+    trackback_send($node);
+
   if ($nid) {
-    watchdog("special", "$node->type: updated '$node->title' using blog API", l(t("view post"), "node/view/$nid"));
-    return new xmlrpcresp(new xmlrpcval(true, "boolean"));
+    watchdog('special', "$node->type: updated '$node->title' using blog API", l(t('view post'), "node/view/$nid"));
+    return new xmlrpcresp(new xmlrpcval(true, 'boolean'));
   }
 
   return blogapi_error(t('error storing node'));
 }
 
+/**
+ * Blogging API callback. Returns a specified blog node.
+ */
 function blogapi_get_post($req_params) {
   $params = blogapi_convert($req_params);
   $user = blogapi_validate_user($params[1], $params[2]);
@@ -202,16 +340,35 @@
   }
 
   $node = node_load(array('nid' => $params[0]));
+  switch ($node->comment) {
+    case 0:
+      $comment = 0;
+      break;
+    case 1:
+      $comment = 2;
+      break;
+    default:
+      $comment = 1;
+      break;
+  }
   $blog = new xmlrpcval(array('userid' => new xmlrpcval($node->name, 'string'),
-                              'dateCreated' => new xmlrpcval(iso8601_encode($node->created), "dateTime.iso8601"),
+                              'dateCreated' => new xmlrpcval(iso8601_encode($node->created), 'dateTime.iso8601'),
                               'title' => new xmlrpcval($node->title, 'string'),
+                              'link' => new xmlrpcval(url("node/view/" . $node->nid, NULL, NULL, TRUE), 'string'),
+                              'permaLink' => new xmlrpcval(url("node/view/" . $node->nid, NULL, NULL, TRUE), 'string'),
                               'description' => new xmlrpcval($node->body, 'string'),
+                              'mt_text_more' => new xmlrpcval($node->extended, 'string'),
+                              'mt_excerpt' => new xmlrpcval($node->excerpt, 'string'),
+                              'mt_allow_comments' => new xmlrpcval($comment, 'string'),
                               'postid' => new xmlrpcval($node->nid, 'string')),
                         'struct');
 
   return new xmlrpcresp($blog);
 }
 
+/**
+ * Blogging API callback. Removes the specified blog node.
+ */
 function blogapi_delete_post($req_params) {
   $params = blogapi_convert($req_params);
 
@@ -221,14 +378,54 @@
   }
 
   $ret = node_delete(array('nid' => $params[1], 'confirm' => 1));
-  return new xmlrpcresp(new xmlrpcval(true, "boolean"));
+  return new xmlrpcresp(new xmlrpcval(true, 'boolean'));
 }
 
+/**
+ * Blogging API callback. Inserts a file into Drupal.
+ *
+ * This has yet to be implemented.
+ */
 function blogapi_new_media_object($req_params) {
-  return blogapi_error('not implemented');
-}
+  $params = blogapi_convert($req_params);
+  $user = blogapi_validate_user($params[1], $params[2]);
+  if (!$user->uid) {
+    return blogapi_error($user);
+  }
 
+  if (!is_array($params[3])) {
+    return blogapi_error('no file uploaded!');
+  } 
+
+  $bits = $params[3]['bits'];
+  $name = $params[3]['name'];
+  
+  $name = preg_replace("/\s/","_",$name);
+  $name = preg_replace("/[^A-Za-z0-9\-\_]/",".",$name);
+  $name = preg_replace("/^[\.\/]+/",".",$name);
+
+  $dest = variable_get("upload_path","public") . FILE_SEPARATOR . $name;
+  $fname = file_create_path($dest);
+  $url = file_create_url($dest);
+
+  $fh = fopen($fname,"w");
+  fwrite($fh,$bits);
+  fclose($fh);
+
+  return new xmlrpcresp(new xmlrpcval($url,'string'));
+}
+
+/**
+ * Blogging API callback. Returns a list of the taxonomy terms that can be
+ * associated with a blog node.
+ */
 function blogapi_get_category_list($req_params) {
+  $params = blogapi_convert($req_params);
+  $user = blogapi_validate_user($params[1], $params[2]);
+  if (!$user->uid) {
+    return blogapi_error($user);
+  }
+
   $vocabularies = module_invoke('taxonomy', 'get_vocabularies', 'blog', 'vid');
   $categories = array();
   if ($vocabularies) {
@@ -245,9 +442,13 @@
       }
     }
   }
-  return new xmlrpcresp(new xmlrpcval($categories, "array"));
+  return new xmlrpcresp(new xmlrpcval($categories, 'array'));
 }
 
+/**
+ * Blogging API callback. Returns a list of the taxonomy terms that are
+ * assigned to a particular node.
+ */
 function blogapi_get_post_categories($req_params) {
   $params = blogapi_convert($req_params);
   $user = blogapi_validate_user($params[1], $params[2]);
@@ -267,9 +468,12 @@
                                         'isPrimary' => new xmlrpcval(true, 'boolean')),
                                   'struct');
   }
-  return new xmlrpcresp(new xmlrpcval($categories, "array"));
+  return new xmlrpcresp(new xmlrpcval($categories, 'array'));
 }
 
+/**
+ * Blogging API callback. Assigns taxonomy terms to a particular node.
+ */
 function blogapi_set_post_categories($req_params) {
   $params = blogapi_convert($req_params);
   $user = blogapi_validate_user($params[1], $params[2]);
@@ -286,6 +490,9 @@
   return new xmlrpcresp(new xmlrpcval(true, 'boolean'));
 }
 
+/**
+ * Blogging API callback. Returns the latest few postings in a user's blog.
+ */
 function blogapi_get_recent_posts($req_params) {
   $params = blogapi_convert($req_params);
 
@@ -299,20 +506,102 @@
   }
 
   $result = db_query_range("SELECT n.nid, n.title, n.body, n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC",  $user->uid, 0, $params[3]);
+  $count = 0;
   while ($blog = db_fetch_object($result)) {
+    $blog = node_load(array('nid' => $blog->nid));
     $blogs[] = new xmlrpcval(array('userid' => new xmlrpcval($blog->name, 'string'),
-                                   'dateCreated' => new xmlrpcval(iso8601_encode($blog->created), "dateTime.iso8601"),
+                                   'dateCreated' => new xmlrpcval(iso8601_encode($blog->created), 'dateTime.iso8601'),
                                    'content' => new xmlrpcval("<title>$blog->title</title>$blog->body", 'string'),
                                    'title' => new xmlrpcval($blog->title, 'string'),
                                    'description' => new xmlrpcval($blog->body, 'string'),
+                                   'mt_text_more' => new xmlrpcval($blog->extended, 'string'),
+                                   'mt_excerpt' => new xmlrpcval($blog->excerpt, 'string'),
+                                   'mt_convert_breaks' => new xmlrpcval($blog->format, 'string'),
                                    'postid' => new xmlrpcval($blog->nid, 'string')),
                              'struct');
+    $count++;
+    if ($count>=$params[3]) {
+      break;
+    }
   }
-  return new xmlrpcresp(new xmlrpcval($blogs, "array"));
+  return new xmlrpcresp(new xmlrpcval($blogs, 'array'));
 }
 
-/** helper functions */
+function blogapi_get_recent_post_titles($req_params) {
+  $params = blogapi_convert($req_params);
 
+  // Remove unused appkey (from bloggerAPI).
+  if (count($params) == 5) {
+    $params = array_slice($params, 1);
+  }
+  $user = blogapi_validate_user($params[1], $params[2]);
+  if (!$user->uid) {
+    return blogapi_error($user);
+  }
+
+  $result = db_query_range("SELECT n.nid, n.title, n.body, n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC",  $user->uid, 0, $params[3]);
+  $count = 0;
+  while ($blog = db_fetch_object($result)) {
+    $blogs[] = new xmlrpcval(array('userid' => new xmlrpcval($blog->name, 'string'),
+                                   'dateCreated' => new xmlrpcval(iso8601_encode($blog->created), 'dateTime.iso8601'),
+                                   'content' => new xmlrpcval("<title>$blog->title</title>$blog->body", 'string'),
+                                   'title' => new xmlrpcval($blog->title, 'string'),
+                                   'postid' => new xmlrpcval($blog->nid, 'string')),
+                             'struct');
+    $count++;
+    if ($count>=$params[3]) {
+      break;
+    }
+  }
+  return new xmlrpcresp(new xmlrpcval($blogs, 'array'));
+}
+
+function blogapi_publish_post($message) {
+  $params = blogapi_convert($req_params);
+
+  $user = blogapi_validate_user($params[1], $params[2]);
+  if (!$user->uid) {
+    return blogapi_error($user);
+  }
+
+  // Okay, we don't need the silly rebuild on Drupal so lets return true :-)
+  return new xmlrpcresp(new xmlrpcval(1, 'boolean'));
+}
+
+function blogapi_get_trackback_pings($message) {
+  $params = blogapi_convert($req_params);
+
+  return blogapi_error("not implemented yet because we cannot determine which is trackback, which is not yet...");
+}
+
+function blogapi_supported_text_filters($message) {
+  $filters[] = new xmlrpcval(array('key' => new xmlrpcval('-1', 'string'),
+                                   'label' => new xmlrpcval('No formatting', 'string')), 
+                                   'struct');
+
+  $filters[] = new xmlrpcval(array('key' => new xmlrpcval('0', 'string'),
+                                   'label' => new xmlrpcval('Convert Line Break', 'string')), 
+                                   'struct');
+
+  $filters[] = new xmlrpcval(array('key' => new xmlrpcval('1', 'string'),
+                                   'label' => new xmlrpcval('Simple Wiki', 'string')), 
+                                   'struct');
+
+  return new xmlrpcresp(new xmlrpcval($filters, 'array'));
+}
+
+function blogapi_supported_methods($message) {
+  $xmlrpc = blogapi_xmlrpc();
+  foreach ($methods as $k => $v) {
+    $methods[] = $k;
+  }
+
+  return new xmlrpcresp(new xmlrpcval($methods, 'array'));
+}
+
+/**
+ * Process the parameters to an XMLRPC callback, and return them as an array.
+ */
 function blogapi_convert($params) {
   $cparams = array();
   $num_params= $params->getNumParams();
@@ -325,6 +614,9 @@
   return $cparams;
 }
 
+/**
+ * Prepare an error message for returning to the XMLRPC caller.
+ */
 function blogapi_error($message) {
   global $xmlrpcusererr;
 
@@ -335,13 +627,16 @@
   return new xmlrpcresp(0, $xmlrpcusererr + 1, strip_tags($message));
 }
 
+/**
+ * Ensure that the given user has permission to edit a blog.
+ */
 function blogapi_validate_user($username, $password) {
   global $user;
 
   $user = user_load(array('name' => $username, 'pass' => $password, 'status' => 1));
 
   if ($user->uid) {
-    if (user_access('edit own blog')) {
+    if (user_access('maintain personal blog')) {
       return $user;
     }
     else {
@@ -353,14 +648,22 @@
   }
 }
 
+/**
+ * For the blogger API, extract the node title from the contents field.
+ */
 function blogapi_blogger_title(&$contents) {
-  if (eregi("<title>([^<]*)</title>", $contents, $title)) {
+  if (eregi('<title>([^<]*)</title>', $contents, $title)) {
     $title = strip_tags($title[0]);
-    $contents = ereg_replace("<title>[^<]*</title>", "", $contents);
+    $contents = ereg_replace('<title>[^<]*</title>', '', $contents);
   }
   else {
     list($title, $rest) = explode("\n", $contents, 2);
   }
   return $title;
 }
+
+function blogapi_default_node() {
+  drupal_set_html_head('<link rel="EditURI" type="application/rsd+xml" title="RSD" href="' . url('blogapi/rsd',NULL,NULL,TRUE) . '" />');
+}
+
 ?>
diff -urN drupal-4.4.1/modules/comment.module drupal4blog/modules/comment.module
--- drupal-4.4.1/modules/comment.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/comment.module	Tue Jun  8 14:19:14 2004
@@ -1,5 +1,5 @@
 <?php
-// $Id: comment.module,v 1.224.2.6 2004/05/01 09:25:21 dries Exp $
+// $Id: comment.module,v 1.248 2004/06/02 19:01:40 dries Exp $
 
 function comment_help($section = "admin/help#comment") {
   $output = "";
@@ -9,6 +9,7 @@
       $output = t("
       <p>When enabled, the Drupal comment module creates a discussion board for each Drupal node. Users can post comments to discuss a forum topic, weblog post, collaborative book page, etc.</p>
       <h3>User control of comment display</h3>
+      <p>(<i>Only for Partial or Full Comment Control</i>)</p>
       <p>Attached to each comment board is a control panel for customizing the way that comments are displayed. Users can control the chronological ordering of posts (newest or oldest first) and the number of posts to display on each page. Additional settings include:</p>
       <ul>
       <li><strong>Threaded</strong> -- Displays the posts grouped according to conversations and subconversations, much like the subject view of an e-mail client.</li>
@@ -40,11 +41,13 @@
       </ul>
 
       <h3>Comment moderation</h3>
+      <p>(<i>Only for Full Comment Control</i>)</p>
       <p>On sites with active commenting from users, the administrator can turn over comment moderation to the community. </p>
       <p>With comment moderation, each comment is automatically assigned an initial rating. As users read comments, they can apply a vote which affects the comment rating. At the same time, users have an additional option in the control panel which allows them to set a threshold for the comments they wish to view. Those comments with ratings lower than the set threshold will not be shown.</p>
       <p>To enable moderation, the administrator must grant <a href=\"%permission\">moderate comments</a> permissions. Then, a number of options in <a href=\"%comment-moderation\">administer &raquo; comments &raquo; moderation</a> must be configured.</p>
 
       <h4>Moderation votes</h4>
+      <p>(<i>Only for Full Comment Control</i>)</p>
       <p>The first step is to create moderation labels which allow users to rate a comment.  Go to <a href=\"%comment-votes\">administer &raquo; comments &raquo; moderation &raquo; votes</a>. In the <em>vote</em> field, enter the textual labels which users will see when casting their votes. Some examples are</p>
       <ul>
       <li>Excellent +3</li>
@@ -59,14 +62,17 @@
 
       <h4>Moderator vote/values matrix</h4>
 
+      <p>(<i>Only for Full Comment Control</i>)</p>
       <p>Next go to <a href=\"%comment-matrix\">administer &raquo; comments &raquo; moderation &raquo; matrix</a>. Enter the values for the vote labels for each permission role in the vote matrix. The values entered here will be used to create the rating for each comment.</p>
       <p>NOTE: Comment ratings are calculated by averaging user votes with the initial rating.</p>
       <h4>Creating comment thresholds</h4>
+      <p>(<i>Only for Full Comment Control</i>)</p>
       <p>In <a href=\"%comment-thresholds\">administer &raquo; comments &raquo; moderation &raquo; thresholds</a>, you'll have to create some comment thresholds to make the comment rating system useful. When comment moderation is enabled and the thresholds are created, users will find another comment control panel option for selecting their thresholds. They'll use the thresholds you enter here to filter out comments with low ratings. Consequently, you'll probably want to create more than one threshold to give users some flexibility in filtering comments.</p>
       <p>When creating the thresholds, note that the <em>Minimum score</em> is asking you for the lowest rating that a comment can have in order to be displayed.</p>
       <p>To see a common example of how thresholds work, you might visit <a href=\"%slashdot\">Slashdot</a> and view one of their comment boards associated with a story. You can reset the thresholds in their comment control panel.</p>
 
       <h4>Initial comment scores</h4>
+      <p>(<i>Only for Full Comment Control</i>)</p>
       <p>Finally, you may want to enter some <em>initial comment scores</em>. In <a href=\"%comment-initial\">administer &raquo; comments &raquo; initial comment scores</a> you can assign a beginning rating for all comments posted by a particular permission role. If you do not assign any initial scores, Drupal will assign a rating of <strong>0</strong> as the default.</p>", array("%comment-config" => url("admin/system/modules/comment"), "%site-config" => url("admin/system"), "%user-permissions" => url("admin/user/permission"), "%tracker" => url("tracker"), "%download-notify" => "http://drupal.org/project/releases", "%permission" => url("admin/user/permission"), "%comment-moderation" => url("admin/comment/moderation"), "%comment-votes" => url("admin/comment/moderation/votes"), "%comment-matrix" => url("admin/comment/moderation/matrix"), "%comment-thresholds" => url("admin/comment/moderation/filters"), "%slashdot" => " http://slashdot.org", "%comment-initial" => url("admin/comment/moderation/roles")));
       break;
     case 'admin/system/modules#description':
@@ -110,27 +116,40 @@
 }
 
 function comment_help_page() {
-  print theme("page", comment_help());
+  print theme('page', comment_help());
 }
 
 function comment_settings() {
-  $group  = form_radios(t("Default display mode"), "comment_default_mode", variable_get("comment_default_mode", 4), _comment_get_modes(), t("The default view for comments. Expanded views display the body of the comment. Threaded views keep replies together."));
-  $group .= form_radios(t("Default display order"), "comment_default_order", variable_get("comment_default_order", 1), _comment_get_orders(), t("The default sorting for new users and anonymous users while viewing comments. These users may change their view using the comment control panel. For registered users, this change is remembered as a persistent user preference."));
-  $group .= form_select(t("Default comments per page"), "comment_default_per_page", variable_get("comment_default_per_page", "50"), _comment_per_page(), t("Default number of comments for each page: more comments are distributed in several pages."));
-  $group .= form_radios(t("Comment controls"), "comment_controls", variable_get("comment_controls", 0), array(t("Display above the comments"), t("Display below the comments"), t("Display above and below the comments"), t("Do not display")), t("Position of the comment controls box.  The comment controls let the user change the default display mode and display order of comments."));
-  $output = form_group(t('Comment viewing options'), $group);
+  $group = form_radios(t("Comment Complexity"), "comment_complex", variable_get("comment_complex", 0), array(0 => t("Simple Comment Control"), 1 => t("Partial Comment Control (no moderation)"), 2 => t("Full Comment Control")),t("'Simple Comment Control' is easier to managed and sufficient for most people. 'Partial Comment Control' enables threaded comments but no moderation features. 'Full Comment Control' gives you complete control."));
+  $output .= form_group(t('Comment control'), $group);
 
-  $group  = form_radios(t("Preview comment"), "comment_preview", variable_get("comment_preview", 1), array(t("Optional"), t("Required")), t("Must users preview comments before submitting?"));
-  $group .= form_radios(t("Location of comment submission form"), "comment_form_location", variable_get("comment_form_location", 0), array(t("Display on separate page"), t("Display below post or comments")), t("The location of the comment submission form."));
+  $group = "";
+  if (variable_get("comment_complex",0)) {
+    $group  = form_radios(t("Default display mode"), "comment_default_mode", variable_get("comment_default_mode", 4), _comment_get_modes(), t("The default view for comments. Expanded views display the body of the comment. Threaded views keep replies together."));
+    $group .= form_radios(t("Default display order"), "comment_default_order", variable_get("comment_default_order", 1), _comment_get_orders(), t("The default sorting for new users and anonymous users while viewing comments. These users may change their view using the comment control panel. For registered users, this change is remembered as a persistent user preference."));
+    $group .= form_select(t("Default comments per page"), "comment_default_per_page", variable_get("comment_default_per_page", "50"), _comment_per_page(), t("Default number of comments for each page: more comments are distributed in several pages."));
+    $group .= form_radios(t("Comment controls"), "comment_controls", variable_get("comment_controls", 0), array(t("Display above the comments"), t("Display below the comments"), t("Display above and below the comments"), t("Do not display")), t("Position of the comment controls box.  The comment controls let the user change the default display mode and display order of comments."));
+    $output .= form_group(t('Comment viewing options'), $group);
+  }
+
+  $group  = form_radios(t('Anonymous poster settings'), 'comment_anonymous', variable_get('comment_anonymous', 1), array(t('Anonymous posters may not enter their contact information'), t('Anonymous posters may leave their contact information'), t('Anonymous posters must leave their contact information')), t('This feature is only useful if you allow anonymous users to post comments.  See the <a href="%url">permissions page</a>.'), array('%url' => url('user/admin/permission')));
+
+  if (variable_get("comment_complex",0)) {
+    $group .= form_radios(t("Comment subject field"), "comment_subject_field", variable_get('comment_subject_field', 0), array(t("Disabled"), t("Enabled")), t('Must users provide a subject for their comments?'));
+    $group .= form_radios(t("Preview comment"), 'comment_preview', variable_get('comment_preview', 1), array(t("Optional"), t("Required")), t("Must users preview comments before submitting?"));
+    $group .= form_radios(t("Location of comment submission form"), "comment_form_location", variable_get("comment_form_location", 1), array(t("Display on separate page"), t("Display below post or comments")), t("The location of the comment submission form."));
+  }
   $output .= form_group(t('Comment posting settings'), $group);
 
-  $result = db_query("SELECT fid, filter FROM {moderation_filters} ");
-  while ($filter = db_fetch_object($result)) {
-    $thresholds[$filter->fid] = ($filter->filter);
-  }
-  if ($thresholds) {
-    $group = form_select(t("Default threshold"), "comment_default_threshold", variable_get("comment_default_threshold", 0), $thresholds, t("Thresholds are values below which comments are hidden. These thresholds are useful for busy sites which want to hide poor comments from most users."));
-    $output .= form_group(t('Comment moderation settings'), $group);
+  if (variable_get("comment_complex",0)>1) {
+    $result = db_query("SELECT fid, filter FROM {moderation_filters} ");
+    while ($filter = db_fetch_object($result)) {
+      $thresholds[$filter->fid] = ($filter->filter);
+    }
+    if ($thresholds) {
+      $group = form_select(t("Default threshold"), "comment_default_threshold", variable_get("comment_default_threshold", 0), $thresholds, t("Thresholds are values below which comments are hidden. These thresholds are useful for busy sites which want to hide poor comments from most users."));
+      $output .= form_group(t('Comment moderation settings'), $group);
+    }
   }
 
   return $output;
@@ -144,10 +163,10 @@
         return form_item(t("Signature"), check_output($user->signature));
       }
       break;
-    case "edit_form":
+    case "edit_form": case 'edit':
       // when user tries to edit his own data
-      return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short()));
-    case "edit_validate":
+      return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 64, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short()));
+    case "edit_validate": case 'validate':
       // validate user data editing
       return array("signature" => $edit["signature"]);
   }
@@ -170,13 +189,14 @@
   }
 
 }
+
 function comment_referer_save() {
   $_SESSION["comment_referer"] = arg(0) ."/". arg(1) ."/". arg(2);
 }
 
-/*
-** Restores the referer from a persistent variable:
-*/
+function comment_node_url() {
+  return arg(0) ."/". arg(1) ."/". arg(2);
+}
 
 function comment_referer_load() {
   return $_SESSION["comment_referer"];
@@ -185,8 +205,9 @@
 function comment_edit($cid) {
   global $user;
 
-  $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2", $cid));
+  $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2', $cid));
   $comment = drupal_unpack($comment);
+  $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
   if (comment_access("edit", $comment)) {
     return comment_preview(object2array($comment));
   }
@@ -203,8 +224,9 @@
     */
 
     if ($pid) {
-      $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid));
+      $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0', $pid));
       $comment = drupal_unpack($comment);
+      $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
       $output .= theme("comment_view", $comment);
     }
     else if (user_access("access content")) {
@@ -217,22 +239,69 @@
     */
 
     if (node_comment_mode($nid) != 2) {
-      $output .= theme("box", t("Reply"), t("This discussion is closed: you can't post new comments."));
+      $output .= theme('box', t("Reply"), t("This discussion is closed: you can't post new comments."));
     }
     else if (user_access("post comments")) {
       $output .= theme("comment_form", array("pid" => $pid, "nid" => $nid), t("Reply"));
     }
     else {
-      $output .= theme("box", t("Reply"), t("You are not authorized to post comments."));
+      $output .= theme('box', t("Reply"), t("You are not authorized to post comments."));
     }
   }
   else {
-    $output .= theme("box", t("Reply"), t("You are not authorized to view comments."));
+    $output .= theme('box', t("Reply"), t("You are not authorized to view comments."));
   }
 
   return $output;
 }
 
+function comment_validate_form($edit) {
+  global $user;
+
+  /*
+  ** Validate the comment's body.
+  */
+
+  if ($edit['comment'] == '') {
+    form_set_error('comment', t('The body of your comment is empty.'));
+  }
+
+  /*
+  ** Check validity of name, mail and homepage (if given)
+  */
+
+  if (!$user->uid) {
+    if (variable_get('comment_anonymous', 1) > 0) {
+      if ($edit['name']) {
+        $taken = db_result(db_query("SELECT COUNT(uid) FROM {users} WHERE LOWER(name) = '%s'", strip_tags($edit['name'])), 0);
+
+        if ($taken != 0) {
+          form_set_error('name', t('The name you used belongs to a registered user.'));
+        }
+
+      }
+      else if (variable_get('comment_anonymous', 1) == 2) {
+        form_set_error('name', t('You have to leave your name.'));
+      }
+
+      if ($edit['mail']) {
+        if (!valid_email_address($edit['mail'])) {
+          form_set_error('mail', t('The e-mail address you specifed is not valid.'));
+        }
+      }
+      else if (variable_get('comment_anonymous', 1) == 2) {
+        form_set_error('mail', t('You have to leave an e-mail address.'));
+      }
+
+      if ($edit['homepage']) {
+        if (!valid_url($edit['homepage'], TRUE)) {
+          form_set_error('homepage', t('The URL of your homepage is not valid.  Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.'));
+        }
+      }
+    }
+  }
+}
+
 function comment_preview($edit) {
   global $user;
 
@@ -247,8 +316,8 @@
   */
 
   $comment->uid = $user->uid;
-  $comment->name = $user->name;
   $comment->timestamp = time();
+  $comment->name = $user->name ? $user->name : $comment->name;
 
   /*
   ** Preview the comment:
@@ -258,8 +327,9 @@
   $output .= theme("comment_form", $edit, t("Reply"));
 
   if ($edit["pid"]) {
-    $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"]));
+    $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0', $edit["pid"]));
     $comment = drupal_unpack($comment);
+    $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
     $output .= theme("comment_view", $comment);
   }
   else {
@@ -273,61 +343,50 @@
 function comment_post($edit) {
   global $user;
 
-  if (user_access("post comments") && node_comment_mode($edit["nid"]) == 2) {
+  if (user_access("post comments") && node_comment_mode($edit['nid']) == 2) {
 
     /*
     ** Validate the comment's subject.  If not specified, extract
     ** one from the comment's body.
     */
 
-    $edit["subject"] = strip_tags($edit["subject"]);
+    $edit['subject'] = strip_tags($edit['subject']);
 
-    if ($edit["subject"] == "") {
-      $edit["subject"] = truncate_utf8(strip_tags($edit["comment"]), 29);
+    if ($edit['subject'] == "") {
+      $edit['subject'] = truncate_utf8(strip_tags($edit['comment']), 29);
     }
 
-    /*
-    ** Validate the comment's body.
-    */
-
-    if ($edit["comment"] == "") {
-      return array(t("Empty comment"), t("The comment you submitted is empty."));
-    }
-
-    /*
-    ** Check for duplicate comments.  Note that we have to use the
-    ** validated/filtered data to perform such check.
-    */
-
-    $duplicate = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND nid = %d AND subject = '%s' AND comment = '%s'", $edit["pid"], $edit["nid"], $edit["subject"], $edit["comment"]), 0);
+    if (!form_has_errors()) {
+      /*
+      ** Check for duplicate comments.  Note that we have to use the
+      ** validated/filtered data to perform such check.
+      */
 
-    if ($duplicate != 0) {
-      watchdog("warning", "comment: duplicate '". $edit["subject"] ."'");
-      return array(t("Duplicate comment"), t("The comment you submitted has already been inserted."));
-    }
-    else {
+      $duplicate = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND nid = %d AND subject = '%s' AND comment = '%s'", $edit["pid"], $edit["nid"], $edit['subject'], $edit['comment']), 0);
+      if ($duplicate != 0) {
+        watchdog('warning', t('comment: duplicate "%comment-subject"', array('%comment-subject' => $edit["subject"])));
+      }
 
       if ($edit["cid"]) {
-
         /*
         ** Update the comment in the database.  Note that the update
         ** query will fail if the comment isn't owned by the current
         ** user.
         */
 
-        db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit["subject"], $edit["comment"], $edit["cid"]);
+        db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit['subject'], $edit['comment'], $edit["cid"]);
 
         /*
         ** Fire a hook
         */
 
-        module_invoke_all("comment", "update", $edit);
+        module_invoke_all('comment', "update", $edit);
 
         /*
         ** Add entry to the watchdog log:
         */
 
-        watchdog("special", "comment: updated '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
+        watchdog('special', t('comment: updated "%comment-subject"', array('%comment-subject' => $edit["subject"])), l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
       }
       else {
         /*
@@ -337,6 +396,13 @@
         $status = user_access("post comments without approval") ? 0 : 1;
         $roles = variable_get("comment_roles", array());
         $score = $roles[$user->rid] ? $roles[$user->rid] : 0;
+
+        /* not in 4.4.0
+        foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
+          $score = max($roles[$rid], $score);
+        }
+        */
+
         $users = serialize(array(0 => $score));
 
         /*
@@ -385,13 +451,13 @@
           */
 
           // Get the parent comment:
-          $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = '%d'", $edit["pid"]));
+          $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $edit['pid']));
 
           // Strip the "/" from the end of the parent thread:
           $parent->thread = (string)rtrim((string)$parent->thread, "/");
 
           // Get the max value in _this_ thread:
-          $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = '%d'", $parent->thread, $edit["nid"]));
+          $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
 
           if ($max == "") {
             // First child of this parent
@@ -428,19 +494,20 @@
 
         $edit["cid"] = db_next_id("{comments}_cid");
 
-        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit["subject"], $edit["comment"], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread);
+        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
 
         /*
         ** Tell the other modules a new comment has been submitted:
         */
 
-        module_invoke_all("comment", "insert", $edit);
+        module_invoke_all('comment', "insert", $edit);
 
         /*
         ** Add entry to the watchdog log:
         */
 
-        watchdog("special", "comment: added '". $edit["subject"] ."'", l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
+        watchdog('special', t('comment: added "%comment-subject"', array('%comment-subject' => $edit["subject"])), l(t("view comment"), "node/view/". $edit["nid"], NULL, NULL, "comment-". $edit["cid"]));
+
       }
 
       /*
@@ -449,19 +516,25 @@
       */
 
       cache_clear_all();
+
+      /*
+      ** Redirect the user the node he commented on, or explain queue
+      */
+
+      if ($status == 1) {
+        print theme('page', t('Your comment has been queued for moderation by site administrators and will be published after approval.'));
+      }
+      else {
+        // Redirect the user to his comment:
+        drupal_goto('node/view/'. $edit['nid'] .'#comment-'. $edit['cid']);
+      }
+    }
+    else {
+      print theme('page', comment_preview($edit));
     }
   }
   else {
-    watchdog("error", "comment: unauthorized comment submitted or comment submitted to a closed node '". $edit["subject"] ."'");
-    return array(t("Error"), t("You are not authorized to post comments, or this node doesn't accept new comments."));
-  }
-
-  /*
-  ** Redirect the user the node he commented on, or explain queue
-  */
-
-  if ($status == 1) {
-    return array(t("Comment queued"), t("Your comment has been queued for moderation by site administrators and will be published after approval."));
+    watchdog('error', t('comment: unauthorized comment submitted or comment submitted to a closed node "%comment-subject"', array('%comment-subject' => $edit["subject"])));
   }
 }
 
@@ -475,7 +548,7 @@
   */
 
   if ($return) {
-    $links[] = l(t("parent"), comment_referer_load(), NULL, NULL, "comment-$comment->cid");
+    $links[] = l(t("parent"), comment_node_url(), NULL, NULL, "comment-$comment->cid");
   }
 
   if (node_comment_mode($comment->nid) == 2) {
@@ -514,7 +587,6 @@
   $output = "";
 
   if (user_access("access comments")) {
-
     /*
     ** Save were we come from so we can go back after a reply
     */
@@ -532,6 +604,7 @@
 
     if (empty($mode)) {
       $mode = $user->mode ? $user->mode : ($_SESSION["comment_mode"] ? $_SESSION["comment_mode"] : variable_get("comment_default_mode", 4));
+      if (variable_get("comment_complex",0) == 0) $mode = 2;
     }
 
     if (empty($order)) {
@@ -555,12 +628,13 @@
       ** Single comment view
       */
 
-      $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
+      $output .= "<form method=\"post\" action=\"". url('comment') ."\"><div>\n";
       $output .= form_hidden("nid", $nid);
 
-      $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users", $cid);
+      $result = db_query('SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users', $cid);
 
       if ($comment = db_fetch_object($result)) {
+        $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
         $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1)));
       }
 
@@ -575,9 +649,15 @@
       ** Multiple comments view
       */
 
-      $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0";
+      $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, c.name , c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid);
+
+      if (!user_access("administer comments"))
+          $query .= "' AND c.status = 0";
+      else
+          $query .= "' AND (c.status = 0 OR c.status = 1)";
 
-      $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread";
+
+      $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread";
 
       /*
       ** We want to use the standard pager, but threads would need every
@@ -671,18 +751,20 @@
       */
 
       $result = pager_query($query, $comments_per_page, 0, "SELECT COUNT(*) FROM {comments} WHERE nid = '". check_query($nid) ."'");
-      if (db_num_rows($result) && (variable_get("comment_controls", 0) == 0 || variable_get("comment_controls", 0) == 2)) {
-        $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
+      if (db_num_rows($result) && ((variable_get("comment_controls", 0) == 0 && variable_get("comment_complex",0)) || variable_get("comment_controls", 0) == 2)) {
+        $output .= "<form method=\"post\" action=\"". url('comment') ."\"><div>\n";
         $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
         $output .= form_hidden("nid", $nid);
         $output .= "</div></form>";
       }
 
-      $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
+      $output .= "<form method=\"post\" action=\"". url('comment') ."\"><div>\n";
       $output .= form_hidden("nid", $nid);
 
+      $output .= '<div class="comment-head">Comments '.db_num_rows($result).'</div>';
       while ($comment = db_fetch_object($result)) {
         $comment = drupal_unpack($comment);
+        $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
         $comment->depth = count(explode(".", $comment->thread)) - 1;
 
         if ($mode == 1) {
@@ -714,7 +796,7 @@
       $output .= "</div></form>";
 
       if (db_num_rows($result) && (variable_get("comment_controls", 0) == 1 || variable_get("comment_controls", 0) == 2)) {
-        $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n";
+        $output .= "<form method=\"post\" action=\"". url('comment') ."\"><div>\n";
         $output .= theme("comment_controls", $threshold, $mode, $order, $comments_per_page);
         $output .= form_hidden("nid", $nid);
         $output .= "</div></form>";
@@ -725,7 +807,7 @@
     ** If enabled, show new comment form
     */
 
-    if (user_access("post comments") && node_comment_mode($nid) == 2 && variable_get("comment_form_location", 0)) {
+    if (user_access("post comments") && node_comment_mode($nid) == 2 && variable_get("comment_form_location", 1)) {
       $output .= theme("comment_form", array("nid" => $nid), t("Post new comment"));
     }
   }
@@ -733,13 +815,22 @@
 }
 
 function comment_perm() {
-  return array("access comments", "post comments", "administer comments", "moderate comments", "post comments without approval", "administer moderation");
+  switch (variable_get("comment_complex",0)) {
+    case 0:
+    case 1:
+      return array("access comments", "post comments", "administer comments", "moderate comments", "post comments without approval");
+    default:
+      return array("access comments", "post comments", "administer comments", "moderate comments", "post comments without approval", "administer moderation");
+  }
 }
 
+/**
+ * Implementation of hook_link().
+ */
 function comment_link($type, $node = 0, $main = 0) {
   $links = array();
 
-  if ($type == "node" && $node->comment) {
+  if ($type == 'node' && $node->comment) {
 
     if ($main) {
 
@@ -747,24 +838,32 @@
       ** Main page: display the number of comments that have been posted.
       */
 
-      if (user_access("access comments")) {
+      if (user_access('access comments')) {
         $all = comment_num_all($node->nid);
         $new = comment_num_new($node->nid);
+        $unp = comment_num_unpublish($node->nid);
 
         if ($all) {
-          $links[] = l(format_plural($all, "1 comment", "%count comments"), "node/view/$node->nid", array("title" => t("Jump to the first comment of this posting.")), NULL, "comment");
+          if ($new && $unp)
+              $title = sprintf("%d new and %d unpublish comments", $new, $unp);
+          else if ($new)
+              $title .= format_plural($new, "1 new comment", 
+                            "%count new comments");
+          else if ($unp)
+              $title .= format_plural($unp, "1 unpublish comment", 
+                            "%count unpublish comments");
+          else
+              $title = "Jump to the first comment of this posting.";
 
-          if ($new) {
-            $links[] = l(format_plural($new, "1 new comment", "%count new comments"), "node/view/$node->nid", array("title" => t("Jump to the first new comment of this posting.")), NULL, "new");
-          }
+          $links[] = l(format_plural($all, "1 comment", "%count comments"), "node/view/$node->nid", array("title" => t($title)), NULL, "comment");
         }
         else {
           if ($node->comment == 2) {
-            if (user_access("post comments")) {
-              $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Add a new comment to this page.")));
+            if (user_access('post comments')) {
+              $links[] = l(t('add new comment'), "comment/reply/$node->nid", array('title' => t('Add a new comment to this page.')));
             }
             else {
-              $links[] = theme("comment_post_forbidden");
+              $links[] = theme('comment_post_forbidden');
             }
           }
         }
@@ -777,44 +876,44 @@
       */
 
       if ($node->comment == 2) {
-        if (user_access("post comments")) {
-          $links[] = l(t("add new comment"), "comment/reply/$node->nid", array("title" => t("Share your thoughts and opinions related to this posting.")), NULL, "comment");
+        if (user_access('post comments')) {
+          $links[] = l(t('add new comment'), "comment/reply/$node->nid", array('title' => t('Share your thoughts and opinions related to this posting.')), NULL, 'comment');
         }
         else {
-          $links[] = theme("comment_post_forbidden");
+          $links[] = theme('comment_post_forbidden');
         }
       }
     }
   }
 
-  if ($type == "comment") {
+  if ($type == 'comment') {
     $links = comment_links($node, $main);
   }
 
-  if ($type == "system") {
-    if (user_access("administer comments")) {
-
-      menu("admin/comment", t("comments"), "comment_admin", 1);
-      menu("admin/comment/comments", t("overview"), "comment_admin", 2);
-      menu("admin/comment/comments/0", t("new/updated"), "comment_admin", 1);
-      menu("admin/comment/comments/1", t("approval queue"), "comment_admin", 2);
-      menu("admin/comment/help", t("help"), "comment_help_page", 9);
-      menu("admin/comment/edit", t("edit comment"), "comment_admin", 0, MENU_HIDE);
-      menu("admin/comment/delete", t("delete comment"), "comment_admin", 0, MENU_HIDE);
-      if (module_exist('search')) {
-        menu("admin/comment/search", t("search"), "comment_admin", 8);
-      }
-
-      // comment settings:
-      if (user_access("administer moderation")) {
-        menu("admin/comment/moderation", t("moderation"), "comment_admin", 3);
-        menu("admin/comment/moderation/votes", t("votes"), "comment_admin");
-        menu("admin/comment/moderation/matrix", t("matrix"), "comment_admin");
-        menu("admin/comment/moderation/filters", t("thresholds"), "comment_admin");
-        menu("admin/comment/moderation/roles", t("initial scores"), "comment_admin", 6);
-      }
+  if ($type == 'system') {
+    $access = user_access('administer comments');
+    menu('admin/comment', t('comments'), $access ? 'comment_admin' : MENU_DENIED, 1);
+    menu('admin/comment/comments', t('overview'), $access ? 'comment_admin' : MENU_DENIED, 2);
+    menu('admin/comment/comments/0', t('new/updated'), $access ? 'comment_admin' : MENU_DENIED, 1);
+    menu('admin/comment/comments/1', t('approval queue'), $access ? 'comment_admin' : MENU_DENIED, 2);
+    menu('admin/comment/help', t('help'), $access ? 'comment_help_page' : MENU_DENIED, 9);
+    menu('admin/comment/edit', t('edit comment'), $access ? 'comment_admin' : MENU_DENIED, 0, MENU_HIDE, MENU_LOCKED);
+    menu('admin/comment/delete', t('delete comment'), $access ? 'comment_admin' : MENU_DENIED, 0, MENU_HIDE, MENU_LOCKED);
+    if (module_exist('search')) {
+      menu('admin/comment/search', t('search'), $access ? 'comment_admin' : MENU_DENIED, 8);
+    }
+
+    // comment settings:
+    $access = user_access('administer comments') && user_access('administer moderation');
+    if (variable_get("comment_complex",0)>1) {
+      menu('admin/comment/moderation', t('moderation'), $access ? 'comment_admin' : MENU_DENIED, 3);
+      menu('admin/comment/moderation/votes', t('votes'), $access ? 'comment_admin' : MENU_DENIED);
+      menu('admin/comment/moderation/matrix', t('matrix'), $access ? 'comment_admin' : MENU_DENIED);
+      menu('admin/comment/moderation/filters', t('thresholds'), $access ? 'comment_admin' : MENU_DENIED);
+      menu('admin/comment/moderation/roles', t('initial scores'), $access ? 'comment_admin' : MENU_DENIED, 6);
     }
-    menu("comment", t("comments"), "comment_page", 0, MENU_HIDE);
+
+    menu('comment', t('comments'), 'comment_page', 0, MENU_HIDE, MENU_LOCKED);
   }
 
   return $links;
@@ -830,27 +929,23 @@
 
   switch ($op) {
     case "edit":
-      print theme("page", comment_edit(check_query(arg(2))), t("Edit comment"));;
+      print theme('page', comment_edit(check_query(arg(2))), t("Edit comment"));;
       break;
     case t("Moderate comments"):
     case t("Moderate comment"):
       comment_moderate($edit);
-      drupal_goto(comment_referer_load());
+      drupal_goto(comment_node_url());
       break;
     case "reply":
-      print theme("page", comment_reply(check_query(arg(3)), check_query(arg(2))), t("Add new comment"));
+      print theme('page', comment_reply(check_query(arg(3)), check_query(arg(2))), t("Add new comment"));
       break;
     case t("Preview comment"):
-      print theme("page", comment_preview($edit), t("Preview comment"));
+      comment_validate_form($edit);
+      print theme('page', comment_preview($edit), t("Preview comment"));
       break;
     case t("Post comment"):
-      list($error_title, $error_body) = comment_post($edit);
-      if ($error_body) {
-        print theme("page", $error_body, $error_title);
-      }
-      else {
-        drupal_goto(comment_referer_load());
-      }
+      comment_validate_form($edit);
+      print theme('page', comment_post($edit));
       break;
     case t("Save settings"):
       $mode = $_POST["mode"];
@@ -859,6 +954,7 @@
       $comments_per_page = $_POST["comments_per_page"];
 
       comment_save_settings(check_query($mode), check_query($order), check_query($threshold), check_query($comments_per_page));
+      //drupal_goto(comment_node_url());
       drupal_goto(comment_referer_load());
       break;
   }
@@ -876,12 +972,12 @@
     ** Edit comments:
     */
 
-    $result = db_query("SELECT c.cid, c.subject, u.uid, u.name FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE nid = %d AND c.status = 0 ORDER BY c.timestamp", $node->nid);
-
+    $result = db_query('SELECT c.cid, c.subject, c.name, c.homepage, u.uid, u.name AS registered_name, c.name FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE nid = %d AND c.status = 0 ORDER BY c.timestamp', $node->nid);
 
-    $header = array(t("title"), t("author"), array("data" => t("operations"), "colspan" => 3));
+    $header = array(t("title"), t('author'), array("data" => t("operations"), "colspan" => 3));
 
     while ($comment = db_fetch_object($result)) {
+      $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
       $rows[] = array(l($comment->subject, "node/view/$node->nid", NULL, NULL, "comment-$comment->cid"), format_name($comment), l(t("view comment"), "node/view/$node->nid", NULL, NULL, $comment->cid), l(t("edit comment"), "admin/comment/edit/$comment->cid"), l(t("delete comment"), "admin/comment/delete/$comment->cid"));
     }
 
@@ -896,15 +992,16 @@
 
 function comment_admin_edit($id) {
 
-  $result = db_query("SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2", $id);
+  $result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status != 2', $id);
   $comment = db_fetch_object($result);
+  $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
   $comment = drupal_unpack($comment);
 
   if ($comment) {
     $form .= form_item(t("Author"), format_name($comment));
-    $form .= form_textfield(t("Subject"), "subject", $comment->subject, 70, 128);
-    $form .= form_textarea(t("Comment"), "comment", $comment->comment, 70, 15, filter_tips_short());
-    $form .= form_radios(t("Status"), "status", $comment->status, array("published", "not published"));
+    $form .= form_textfield(t("Subject"), 'subject', $comment->subject, 70, 128);
+    $form .= form_textarea(t("Comment"), 'comment', $comment->comment, 70, 15, filter_tips_short());
+    $form .= form_radios(t("Status"), 'status', $comment->status, array("published", "not published"));
     $form .= form_hidden("cid", $id);
     $form .= form_submit(t("Submit"));
 
@@ -915,7 +1012,7 @@
 function _comment_delete_thread($comment) {
   // Delete the comment:
   db_query("DELETE FROM {comments} WHERE cid = %d", $comment->cid);
-  watchdog("special", "comment: deleted '$comment->subject'");
+  watchdog('special', t('comment: deleted "%comment-subject"', array('%comment-subject' => $comment->subject)));
 
   // Delete the comment's replies:
   $result = db_query("SELECT cid, subject FROM {comments} WHERE pid = %d", $comment->cid);
@@ -925,7 +1022,8 @@
 }
 
 function comment_delete($cid, $confirmed = 0) {
-  $comment = db_fetch_object(db_query("SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d", $cid));
+  $comment = db_fetch_object(db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d', $cid));
+  $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
 
   if ($comment->cid) {
     if ($confirmed) {
@@ -951,7 +1049,7 @@
       ** Print a confirmation screen:
       */
 
-      $output  = theme("comment", $comment);
+      $output  = theme('comment', $comment);
       $output .= form_submit(t("Delete comment"));
 
       return form($output);
@@ -963,27 +1061,35 @@
 }
 
 function comment_save($id, $edit) {
-  db_query("UPDATE {comments} SET subject = '%s', comment = '%s', status = %d WHERE cid = %d", $edit["subject"], $edit["comment"], $edit["status"], $id);
-  watchdog("special", "comment: modified '". $edit["subject"] ."'");
+  db_query("UPDATE {comments} SET subject = '%s', comment = '%s', status = %d WHERE cid = %d", $edit['subject'], $edit['comment'], $edit['status'], $id);
+  watchdog('special', t('comment: modified "%comment-subject"', array('%comment-subject' => $edit["subject"])));
   drupal_set_message(t("the comment has been saved."));
 }
 
 function comment_admin_overview($status = 0) {
 
   $header = array(
-    array("data" => t("subject"), "field" => "subject"),
-    array("data" => t("author"), "field" => "u.name"),
-    array("data" => t("status"), "field" => "status"),
+    array("data" => t('subject'), "field" => 'subject'),
+    array("data" => t('author'), "field" => "u.name"),
+    array("data" => t('status'), "field" => 'status'),
     array("data" => t("time"), "field" => "c.timestamp", "sort" => "desc"),
     array("data" => t("operations"), "colspan" => 2)
   );
 
-  $sql = "SELECT c.*, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.status = ". check_query($status);
+  $sql = 'SELECT c.subject, c.nid, c.cid, c.comment, c.timestamp, c.status, c.name, c.homepage, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.status = '. check_query($status);
   $sql .= tablesort_sql($header);
   $result = pager_query($sql,  50);
 
   while ($comment = db_fetch_object($result)) {
-    $rows[] = array(l($comment->subject, "node/view/$comment->nid/$comment->cid", array("title" => htmlspecialchars(substr($comment->comment, 0, 128))), NULL, "comment-$comment->cid") ." ". (node_is_new($comment->nid, $comment->timestamp) ? theme("mark") : ""), format_name($comment), ($comment->status == 0 ? t("published") : t("not published")) ."</td><td>". format_date($comment->timestamp, "small") ."</td><td>". l(t("edit comment"), "admin/comment/edit/$comment->cid"), l(t("delete comment"), "admin/comment/delete/$comment->cid"));
+    $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name . theme('notmember');
+    $rows[] = array(
+        l($comment->subject, "node/view/$comment->nid/$comment->cid", array("title" => htmlspecialchars(truncate_utf8($comment->comment, 128))), NULL, "comment-$comment->cid") ." ". (node_is_new($comment->nid, $comment->timestamp) ? theme("mark") : ""),
+        format_name($comment),
+        ($comment->status == 0 ? t("published") : t("not published")),
+        format_date($comment->timestamp, "small"),
+        l(t("edit comment"), "admin/comment/edit/$comment->cid"),
+        l(t("delete comment"), "admin/comment/delete/$comment->cid")
+      );
   }
 
   if ($pager = theme("pager", NULL, 50, 0, tablesort_pager())) {
@@ -1001,10 +1107,10 @@
     db_query("DELETE FROM {moderation_roles} ");
     foreach ($edit as $role_id => $votes) {
       foreach ($votes as $mid => $value) {
-        $sql[] = "('$mid', '$role_id', '". ($value ? $value : 0) ."')";
+        $sql = "('$mid', '$role_id', '". ($value ? $value : 0) ."')";
+   db_query("INSERT INTO {moderation_roles} (mid, rid, value) VALUES ". $sql);
       }
     }
-    db_query("INSERT INTO {moderation_roles} (mid, rid, value) VALUES ". implode(", ", $sql));
     drupal_set_message(t("the vote values have been saved."));
   }
 
@@ -1180,25 +1286,28 @@
       $output = comment_admin_edit(arg(3));
       break;
     case "search":
-      $output = search_type("comment", url("admin/comment/search"), $_POST["keys"]);
+      $output = search_type('comment', url("admin/comment/search"), $_POST["keys"]);
       break;
     case "votes":
     case t("Add new vote"):
     case t("Delete vote"):
     case t("Save vote"):
-      if (user_access("administer moderation")) {
+      if (user_access("administer moderation")
+      && (variable_get("comment_complex",0)>1)) {
         $output = comment_mod_votes($edit);
       }
       break;
     case "roles":
     case t("Save scores"):
-      if (user_access("administer moderation")) {
+      if (user_access("administer moderation")
+      && (variable_get("comment_complex",0)>1)) {
         $output = comment_mod_roles($edit);
       }
       break;
     case "matrix":
     case t("Submit votes"):
-      if (user_access("administer moderation")) {
+      if (user_access("administer moderation")
+      && (variable_get("comment_complex",0)>1)) {
         $output = comment_mod_matrix($edit);
       }
       break;
@@ -1220,6 +1329,14 @@
       $output  = comment_save(check_query(arg(3)), $edit);
       $output .= comment_admin_overview(0);
       break;
+    case "unpublish":
+      _comment_unpublish(check_query(arg(3)));
+      drupal_goto(comment_referer_load()."#comment-".check_query(arg(3)));
+      break;
+    case "publish":
+      _comment_publish(check_query(arg(3)));
+      drupal_goto(comment_referer_load()."#comment-".check_query(arg(3)));
+      break;
     default:
       if (arg(3) == 1) {
         $output = comment_admin_overview(1);
@@ -1228,7 +1345,7 @@
         $output = comment_admin_overview(0);
       }
   }
-  print theme("page", $output);
+  print theme('page', $output);
 }
 
 /*
@@ -1241,36 +1358,46 @@
 
   $form .= "<a id=\"comment\"></a>\n";
 
-  // name field:
-  $form .= form_item(t("Your name"), format_name($user));
+  // contact information:
+  if ($user->uid) {
+    $form .= form_item(t('Your name'), format_name($user));
+  }
+  else if (variable_get('comment_anonymous', 1) > 0) {
+    $form .= form_textfield(t('Your name'), 'name', $edit['name'] ? $edit['name'] : variable_get('anonymous', 'Anonymous') , 20, 40);
+    $form .= form_textfield(t('E-mail'), 'mail', $edit['mail'], 20, 40);
+    $form .= form_textfield(t('Homepage'), 'homepage', $edit['homepage'], 20, 40, t("e.g. http://www.example.com/ - Your E-mail will not be display if you entered your homepage."));
+  }
 
   // subject field:
-  $form .= form_textfield(t("Subject"), "subject", $edit["subject"], 50, 64);
+  if (variable_get('comment_subject_field', 0) && variable_get("comment_complex", 0)) {
+    $form .= form_textfield(t('Subject'), 'subject', $edit['subject'], 50, 64);
+  }
 
   // comment field:
-  $form .= form_textarea(t("Comment"), "comment", $edit["comment"] ? $edit["comment"] : $user->signature, 70, 10, filter_tips_short());
+  $form .= form_textarea(t("Comment"), 'comment', $edit['comment'] ? $edit['comment'] : $user->signature, 70, 10, filter_tips_short());
 
   // preview button:
   $form .= form_hidden("cid", $edit["cid"]);
   $form .= form_hidden("pid", $edit["pid"]);
   $form .= form_hidden("nid", $edit["nid"]);
 
-  if (!$edit["comment"] && variable_get("comment_preview", 1)) {
+  if (variable_get('comment_preview', 1)) {
     $form .= form_submit(t("Preview comment"));
   }
-  else {
-    $form .= form_submit(t("Preview comment"));
+
+  if (!form_has_errors()) {
     $form .= form_submit(t("Post comment"));
   }
 
-  return theme("box", $title, form($form, "post", url("comment/reply/". $edit["nid"])));
+  return theme('box', $title, form($form, "post", url("comment/reply/". $edit["nid"])));
 }
 
 function theme_comment_view($comment, $links = "", $visible = 1) {
 
   /*
-  ** Switch to folded/unfolded view of the comment
+  ** Emit selectors:
   */
+
   $output = "";
   if (node_is_new($comment->nid, $comment->timestamp)) {
     $comment->new = 1;
@@ -1279,13 +1406,18 @@
 
   $output .= "<a id=\"comment-$comment->cid\"></a>\n";
 
+  /*
+  ** Switch to folded/unfolded view of the comment
+  */
+
   if ($visible) {
     $comment->comment = check_output($comment->comment);
-    $output .= theme("comment", $comment, $links);
+    $output .= theme('comment', $comment, $links);
   }
   else {
     $output .= theme("comment_folded", $comment);
   }
+
   return $output;
 }
 
@@ -1322,6 +1454,7 @@
   while ($filter = db_fetch_object($result)) {
     $filters .= " <option value=\"$filter->fid\"". ($threshold == $filter->fid ? " selected=\"selected\"" : "") .">". t($filter->filter) ."</option>";
   }
+
   if ($filters) {
     return "<select name=\"threshold\">$filters</select>\n";
   }
@@ -1344,7 +1477,7 @@
     $output = form_item(NULL, $output, t("Select your preferred way to display the comments and click 'Save settings' to activate your changes."));
   }
 
-  return theme("box", t("Comment viewing options"), $output);
+  return theme('box', t("Comment viewing options"), $output);
 }
 
 function theme_comment_moderation_form($comment) {
@@ -1357,9 +1490,33 @@
     // preview comment:
     $output .= "&nbsp;";
   }
-  else if ((comment_user_can_moderate($node)) && $user->uid != $comment->uid && !(comment_already_moderated($user->uid, $comment->users))) {
+
+  /* simple or partial comment control */
+  if (user_access("administer comments") && variable_get("comment_complex", 0) <= 1) {
+    if ($comment->status == 0) 
+        $output .= l(t("unpublish comment"),
+            "admin/comment/unpublish/".$comment->cid);
+    else if ($comment->status == 1)
+        $output .= l(t("publish comment"),
+            "admin/comment/publish/".$comment->cid);
+    return $output;
+  }
+
+  if ((comment_user_can_moderate($node)) && $user->uid != $comment->uid && !(comment_already_moderated($user->uid, $comment->users))) {
     // comment hasn't been moderated yet:
 
+    /* not in 4.4.1
+    if (!isset($votes) && $user->roles) {
+      $result = db_query("SELECT v.mid, v.vote, MAX(r.value) AS value FROM {moderation_votes} v INNER JOIN {moderation_roles} r ON r.mid = v.mid WHERE r.rid IN (%s) GROUP BY v.mid, v.vote ORDER BY weight", implode(", ", array_keys($user->roles)));
+      $votes = array();
+      while ($vote = db_fetch_object($result)) {
+        if ($vote->value != 0) {
+          $votes[] = $vote;
+        }
+      }
+    }
+    */
+
     if (!isset($votes)) {
       $result = db_query("SELECT v.mid, v.vote, r.value FROM {moderation_votes} v, {moderation_roles} r WHERE v.mid = r.mid AND r.rid = %d ORDER BY weight", $user->rid);
       $votes = array();
@@ -1390,10 +1547,10 @@
 
 function theme_comment($comment, $links = 0) {
   $output  = "<div class=\"comment\">\n";
-  $output .= "<div class=\"subject\">$comment->subject". ($comment->new ? ' '. theme('mark') : '') ."</div>\n";
+  $output .= "<div class=\"subject\">". l($comment->subject, $_GET['q'], NULL, NULL, "comment-$comment->cid") . ($comment->new ? ' '. theme('mark') : '') ."</div>\n";
   $output .= "<div class=\"moderation\">". $comment->moderation ."</div>\n";
   $output .= "<div class=\"credit\">". t("by %a on %b", array("%a" => format_name($comment), "%b" => format_date($comment->timestamp))) ."</div>\n";
-  $output .= "<div class=\"body\">". check_output($comment->comment) ."</div>\n";
+  $output .= "<div class=\"body\">$comment->comment</div>\n";
   $output .= "<div class=\"links\">$links</div>\n";
   $output .= "</div>\n";
   return $output;
@@ -1401,7 +1558,7 @@
 
 function theme_comment_folded($comment) {
   $output  = "<div class=\"comment-folded\">\n";
-  $output .= " <span class=\"subject\">". l($comment->subject, comment_referer_load() ."/$comment->cid", NULL, NULL, "comment-$comment->cid") . ($comment->new ? ' '. theme('mark') : '') ."</span> ";
+  $output .= " <span class=\"subject\">". l($comment->subject, comment_node_url() ."/$comment->cid", NULL, NULL, "comment-$comment->cid") . ($comment->new ? ' '. theme('mark') : '') ."</span> ";
   $output .= "<span class=\"credit\">". t("by") ." ". format_name($comment) ."</span>\n";
   $output .= "</div>\n";
   return $output;
@@ -1471,6 +1628,9 @@
   $moderation = $_POST["moderation"];
 
   if ($moderation) {
+    /* not in 4.4.1
+    $result = db_query("SELECT mid, MAX(value) AS value FROM {moderation_roles} WHERE rid IN (%s) GROUP BY mid", implode(", ", array_keys($user->roles)));
+    */
     $result = db_query("SELECT mid, value FROM {moderation_roles} WHERE rid = %d", $user->rid);
     while ($mod = db_fetch_object($result)) {
       $votes[$mod->mid] = $mod->value;
@@ -1502,7 +1662,7 @@
             ** Fire a hook
             */
 
-            module_invoke_all("comment", "moderate", $cid, $vote);
+            module_invoke_all('comment', "moderate", $cid, $vote);
           }
         }
       }
@@ -1528,11 +1688,19 @@
   static $cache;
 
   if (!isset($cache[$nid])) {
-    $cache[$nid] = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0", $nid));
+    if (user_access("administer comments")) {
+      $cache[$nid] = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE nid = %d", $nid));
+    } else {
+      $cache[$nid] = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0", $nid));
+    }
   }
   return $cache[$nid];
 }
 
+function comment_num_unpublish($nid) {
+  return db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 1", $nid));
+}
+
 function comment_num_replies($pid) {
   static $cache;
 
@@ -1567,7 +1735,11 @@
     ** Use the timestamp to retrieve the number of new comments
     */
 
-    $result = db_result(db_query("SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND timestamp > %d AND c.status = 0", $nid, $timestamp));
+    if (user_access("administer comments")) {
+      $result = db_result(db_query("SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND timestamp > %d", $nid, $timestamp));
+    } else {
+      $result = db_result(db_query("SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND timestamp > %d AND c.status = 0", $nid, $timestamp));
+    }
 
     return $result;
   }
@@ -1579,7 +1751,8 @@
 
 function comment_user_can_moderate($node) {
   global $user;
-  return (user_access("moderate comments"));
+  return (user_access("moderate comments") 
+      && (variable_get("comment_complex",0)>1));
   // TODO: || (($user->uid == $node->uid) && user_access("moderate comments in owned node")));
 }
 
@@ -1614,9 +1787,12 @@
   **
   ** The select statement may optionally provide "nid", which is a secondary
   ** identifier which is currently used byt the comment module.
+  **
+  ** Notice this will not search through comments table 'name' field, but will
+  ** do through users table one.
   */
 
-  $find = do_search(array("keys" => $keys, "type" => "comment", "select" => "select s.lno as lno, c.nid as nid, c.subject as title, c.timestamp as created, u.uid as uid, u.name as name, s.count as count FROM {search_index} s, {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE s.lno = c.cid AND s.type = 'comment' AND c.status = 0 AND s.word like '%'"));
+  $find = do_search(array("keys" => $keys, "type" => 'comment', "select" => "select s.lno as lno, c.nid as nid, c.subject as title, c.timestamp as created, u.uid as uid, u.name as name, s.count as count FROM {search_index} s, {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE s.lno = c.cid AND s.type = 'comment' AND c.status = 0 AND s.word like '%'"));
 
   return array(t("Matching comments ranked in order of relevance"), $find);
 }
@@ -1638,22 +1814,20 @@
   ** last run date for the comments update.
   */
 
-  return array("last_update" => "comment_cron_last", "node_type" => "comment", "select" => "SELECT c.cid as lno, c.subject as text1, c.comment as text2 FROM {comments} c WHERE c.status = 0 AND timestamp > ". variable_get("comment_cron_last", 1));
+  return array("last_update" => "comment_cron_last", "node_type" => 'comment', "select" => "SELECT c.cid as lno, c.subject as text1, c.comment as text2 FROM {comments} c WHERE c.status = 0 AND timestamp > ". variable_get("comment_cron_last", 1));
 }
 
 function comment_nodeapi(&$node, $op, $arg = 0) {
   switch ($op) {
     case "settings":
-      $output[t("comment")] = form_select("", "comment_$node->type", variable_get("comment_$node->type", 2), array(t("Disabled"), t("Read only"), t("Read/Write")));
+      $output[t('comment')] = form_select("", "comment_$node->type", variable_get("comment_$node->type", 2), array(t("Disabled"), t("Read only"), t("Read/Write")));
       return $output;
     case "fields":
-      return array("comment");
+      return array('comment');
     case "form admin":
       if (user_access("administer comments")) {
         $selected = isset($node->comment) ? $node->comment : variable_get("comment_$node->type", 2);
-        $output = form_radio(t("Disabled"), "comment", 0, ($selected == 0));
-        $output .= form_radio(t("Read only"), "comment", 1, ($selected == 1));
-        $output .= form_radio(t("Read/write"), "comment", 2, ($selected == 2));
+        $output = form_radios("", 'comment', $selected, array(t("Disabled"), t("Read only"), t("Read/write")));
         return form_group(t("User comments"), $output);
       }
       break;
@@ -1688,6 +1862,37 @@
 
 function _comment_per_page() {
   return drupal_map_assoc(array(10, 30, 50, 70, 90));
+}
+
+function theme_notmember() {
+    return ' <span class="marker">(non-member)</span>';
+}
+
+function _comment_publish($cid) {
+  if (!user_access("administer comments")) return;
+  $comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $cid));
+  if ($comment) {
+    db_query("UPDATE {comments} SET status = %d WHERE cid = %d", 0, $cid);
+    watchdog("special", "comment: published '".$comment->subject ."'");
+    drupal_set_message(t("Comment '".$comment->subject."' has been publish."));
+  }
+}
+
+function _comment_unpublish($cid) {
+  if (!user_access("administer comments")) return;
+  $comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $cid));
+  if ($comment) {
+    db_query("UPDATE {comments} SET status = %d WHERE cid = %d", 1, $cid);
+    watchdog("special", "comment: unpublished '".$comment->subject ."'");
+    drupal_set_message(t("Comment '".$comment->subject."' has been unpublish."));
+  }
+}
+
+function comment_blogadmin($type) {
+  if ($type == "options") {
+    if (user_access("administer site configuration"))
+      return array(l(t("Comment options"),"admin/system/modules/comment") => t("Configure comments options."));
+  } 
 }
 
 ?>
diff -urN drupal-4.4.1/modules/node.module drupal4blog/modules/node.module
--- drupal-4.4.1/modules/node.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/node.module	Tue Jun  8 14:19:14 2004
@@ -577,6 +577,7 @@
 
 function node_settings() {
   $output .= form_select(t('Number of posts on main page'), 'default_nodes_main', variable_get('default_nodes_main', 10), drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)), t('The default maximum number of posts to display per page on overview pages such as the main page.'));
+  $output .= form_select(t('Number of posts on rss feed'), 'default_nodes_feed', variable_get('default_nodes_feed', 15), drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 40, 50)), t('The default maximum number of entries to display per rss feed.'));
   $output .= form_select(t('Length of trimmed posts'), 'teaser_length', variable_get('teaser_length', 600), array(0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'), 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'), 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')), t("The maximum number of characters used in the trimmed version of a post.  Drupal will use this setting to determine at which offset long posts should be trimmed.  The trimmed version of a post is typically used as a teaser when displaying the post on the main page, in XML feeds, etc.  To disable teasers, set to 'Unlimited'. Note that this setting will only affect new or updated content and will not affect existing teasers."));
   $output .= form_radios(t('Preview post'), 'node_preview', variable_get('node_preview', 0), array(t('Optional'), t('Required')), t('Must users preview posts before submitting?'));
 
@@ -976,7 +977,6 @@
 }
 
 function node_block($op = 'list', $delta = 0) {
-
   if ($op == 'list') {
     $blocks[0]['info'] = t('Syndicate');
     return $blocks;
@@ -1001,7 +1001,7 @@
   */
 
   if (!$nodes) {
-    $nodes = db_query_range('SELECT nid FROM {node} WHERE promote = 1 AND status = 1 ORDER BY created DESC', 0, 15);
+    $nodes = db_query_range('SELECT nid FROM {node} WHERE promote = 1 AND status = 1 ORDER BY created DESC', 0, variable_get('default_nodes_feed',15));
   }
 
   while ($node = db_fetch_object($nodes)) {
@@ -1010,13 +1010,21 @@
     */
 
     $item = node_load(array('nid' => $node->nid));
-    $link = url("node/view/$node->nid", NULL, NULL, 1);
-    $items .= format_rss_item($item->title, $link, ($item->teaser ? $item->teaser : $item->body), array('pubDate' => date('r', $item->changed)));
+
+    /*
+    ** allows customized rss on a per node basis
+    */
+    if (node_hook($item, 'feed_item')) {
+      $items .= node_invoke($item, 'feed_item', $item);
+    } else {
+      $link = url("node/view/$node->nid", NULL, NULL, TRUE);
+      $items .= format_rss_item($item->title, $link, ($item->teaser ? $item->teaser : $item->body), array('pubDate' => date('r', $item->changed)));
+    }
   }
 
   $channel_defaults = array(
-    'version'     => '0.92',
-    'title'       => variable_get('site_name', 'drupal') .' - '. variable_get('site_slogan', ''),
+    'version'     => '1.0',
+    'title'       => variable_get('site_name', 'drupal') . (variable_get('site_slogan','') ? ' - '. variable_get('site_slogan', '') : ''),
     'link'        => $base_url,
     'description' => variable_get('site_mission', ''),
     'language'    => (($key = reset(array_keys($languages))) ? $key : 'en')
@@ -1026,7 +1034,7 @@
   $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
   $output .= "<!DOCTYPE rss [<!ENTITY % HTMLlat1 PUBLIC \"-//W3C//ENTITIES Latin 1 for XHTML//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent\">]>\n";
   $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\">\n";
-  $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
+  $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language'], array('dc:creator' => variable_get('site_mail','')));
   $output .= "</rss>\n";
 
   drupal_set_header('Content-Type: text/xml; charset=utf-8');
@@ -1489,7 +1497,12 @@
   $result = pager_query('SELECT nid, type FROM {node} WHERE promote = 1 AND status = 1 ORDER BY static DESC, created DESC', variable_get('default_nodes_main', 10));
 
   if (db_num_rows($result)) {
-    drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS" href="'. url('node/feed') .'" />');
+    drupal_set_html_head('<link rel="alternate" type="application/rss+xml" title="RSS" href="'. url('node/feed', NULL, NULL, TRUE) .'" />');
+
+    /*
+    ** allows module to insert autodiscovery code
+    */
+    module_invoke_all("default_node");
 
     $output = '';
     while ($node = db_fetch_object($result)) {
diff -urN drupal-4.4.1/modules/ping.module drupal4blog/modules/ping.module
--- drupal-4.4.1/modules/ping.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/ping.module	Tue Jun  8 14:19:14 2004
@@ -17,17 +17,39 @@
     case 'admin/system/modules#description':
       $output = t("Alerts other site(s) that your site has been updated.");
       break;
+
+    case 'admin/system/modules/ping':
+      $output = t("
+        <p>There are three different ways to notify different search engine
+        or websphere trackers. If you don't know what these means, then
+        just leave it as-is.</p>
+        <p>Pings are send out once every few hours, depending how frequent
+        you set your cron.php (see Section 6 on CRON TASKS in INSTALL).
+        Pings <b>will not</b> be send unless you configure your the name
+        and 'slogan' of your site. See configuration.</p>", 
+            array("configuration" => l(t("configuration"),"admin/system")));
   }
 
   return $output;
 }
 
+function ping_settings() {
+    $output = "";
+    $output .= form_textarea(t("Sites to alert (ping)"), "ping_ping", variable_get("ping_ping", "http://rpc.weblogs.com/RPC2"), 70, 4, t("Send weblogUpdates.ping to these sites"));
+    $output .= form_textarea(t("Sites to alert (advance ping)"), "ping_extping", variable_get("ping_extping", "http://ping.blo.gs/\nhttp://rpc.technorati.com/rpc/ping/"), 70, 4, t("Send weblogUpdates.extendedPing to these sites"));
+    $output .= form_textarea(t("Sites to alert (rss update)"), "ping_rss", variable_get("ping_rss", "http://rssrpc.weblogs.com/RPC2"), 70, 4, t("Send rssUpdate to these sites"));
+    return $output;
+}
+
 function ping_cron() {
   global $base_url;
 
   if (variable_get("site_name", 0) && variable_get("site_slogan", 0)) {
     if (db_num_rows(db_query("SELECT nid FROM {node} WHERE status = 1 AND moderate = 0 AND (created > '". variable_get("ping_cron_last", time()) ."' OR changed > '". variable_get("ping_cron_last", time()) ."')"), 1)) {
-      _ping_notify(variable_get("site_name", "") ." - ". variable_get("site_slogan", ""), $base_url);
+      $site = variable_get("site_name","drupal");
+      if (variable_get("site_slogan", "")) 
+          $site .= " - " . variable_get("site_slogon", "");
+      _ping_notify($site, $base_url);
     }
 
     variable_set("ping_cron_last", time());
@@ -41,39 +63,68 @@
 function ping_ping($name = "", $url = "") {
   $feed = url("node/feed");
 
-  $client = new xmlrpc_client("/RPC2", "rpc.weblogs.com", 80);
+  $urls = preg_split("/\s+/",
+    variable_get("ping_ping", "http://rpc.weblogs.com/RPC2"));
+  foreach ($urls as $pingurl) {
+      $purl = parse_url($pingurl);
+      $path = $purl['path'];
+      if (strlen($purl['query'])>0)     $path .= "?".$purl['query'];
+      if (strlen($purl['fragment'])>0)   $path .= "#".$purl['fragment'];
+
+      $client = new xmlrpc_client($path,$purl['host'],$purl['port']);
+
+      $message = new xmlrpcmsg("weblogUpdates.ping", array(new xmlrpcval($name), new xmlrpcval($url)));
 
-  $message = new xmlrpcmsg("weblogUpdates.ping", array(new xmlrpcval($name), new xmlrpcval($url)));
+      $result = $client->send($message);
 
-  $result = $client->send($message);
+      if (!$result || $result->faultCode()) {
+        watchdog("error", "weblogUpdates.ping: failed to notify ".$pingurl);
+      } 
 
-  if (!$result || $result->faultCode()) {
-    watchdog("error", "failed to notify 'weblogs.com' (site)");
+      unset($client);
   }
 
-  unset($client);
+  $urls = preg_split("/\s+/",
+    variable_get("ping_extping", "http://ping.blo.gs/\nhttp://rpc.technorati.com/rpc/ping/"));
+  foreach ($urls as $pingurl) {
+      $purl = parse_url($pingurl);
+      $path = $purl['path'];
+      if (strlen($purl['query'])>0)     $path .= "?".$purl['query'];
+      if (strlen($purl['fragment'])>0)   $path .= "#".$purl['fragment'];
 
-  $client = new xmlrpc_client("/RPC2", "rssrpc.weblogs.com", 80);
+      $client = new xmlrpc_client($path,$purl['host'],$purl['port']);
 
-  $message = new xmlrpcmsg("rssUpdate", array(new xmlrpcval($name), new xmlrpcval($feed)));
+      $message = new xmlrpcmsg("weblogUpdates.extendedPing", array(new xmlrpcval($name), new xmlrpcval($url), new xmlrpcval($url), new xmlrpcval($feed)));
 
-  $result = $client->send($message);
+      $result = $client->send($message,0,$purl['scheme']);
 
-  if (!$result || $result->faultCode()) {
-    watchdog("error", "failed to notify 'weblogs.com' (RSS)");
+      if (!$result || $result->faultCode()) {
+        watchdog("error", "weblogUpdates.extendedPing: failed to notify ".$pingurl);
+      } 
+
+      unset($client);
   }
 
-  unset($client);
+  $urls = preg_split("/\s+/",
+    variable_get("ping_rss", "http://rssrpc.weblogs.com/RPC2"));
+  foreach ($urls as $pingurl) {
+      $purl = parse_url($pingurl);
+      $path = $purl['path'];
+      if (strlen($purl['query'])>0)     $path .= "?".$purl['query'];
+      if (strlen($purl['fragment'])>0)   $path .= "#".$purl['fragment'];
 
-  $client = new xmlrpc_client("/", "ping.blo.gs", 80);
+      $client = new xmlrpc_client($path,$purl['host'],$purl['port']);
 
-  $message = new xmlrpcmsg("weblogUpdates.extendedPing", array(new xmlrpcval($name), new xmlrpcval($url), new xmlrpcval($url), new xmlrpcval($feed)));
+      $message = new xmlrpcmsg("rssUpdate", array(new xmlrpcval($name), new xmlrpcval($feed)));
 
-  $result = $client->send($message);
+      $result = $client->send($message,0,$purl['scheme']);
 
-  if (!$result || $result->faultCode()) {
-    watchdog("error", "failed to notify 'blo.gs' ");
-  }
+      if (!$result || $result->faultCode()) {
+        watchdog("error", "rssUpdate: failed to notify ".$pingurl);
+      } 
 
+      unset($client);
+  }
 }
+
 ?>
diff -urN drupal-4.4.1/modules/search.module drupal4blog/modules/search.module
--- drupal-4.4.1/modules/search.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/search.module	Tue Jun  8 14:19:14 2004
@@ -185,6 +185,8 @@
       case "comment":
         $find[$i++] = array("count" => $count, "title" => $title, "link" => (strstr(request_uri(), "admin") ? url("admin/comment/edit/$lno") : url("node/view/$nid", NULL, "comment-$lno")), "user" => $name, "date" => $created, "keywords" => implode("|", $words));
         break;
+      case "blog":
+        $find[$i++] = array("count" => $count, "title" => $title, "link" => (strstr(request_uri(), "admin") ?  url("admin/node/edit/$lno") : url("node/view/$lno")), "user" => $name, "date" => $created, "keywords" => implode("|", $words));
       }
     }
   }
@@ -224,7 +226,7 @@
       ** false count of the number of hits, and doesn't show up
       ** when clicking on a node from the search interface anyway.
       */
-      $wordlist = $node["text1"] ." ". $node["text2"];
+      $wordlist = $node["text1"] ." ". $node["text2"] . " ". $node["text3"] . " ". $node["text4"];
 
       // Strip heaps of stuff out of it
       $wordlist = preg_replace("'<[\/\!]*?[^<>]*?>'si", "", $wordlist);
diff -urN drupal-4.4.1/modules/subscriptions.module drupal4blog/modules/subscriptions.module
--- drupal-4.4.1/modules/subscriptions.module	Thu Jan  1 07:30:00 1970
+++ drupal4blog/modules/subscriptions.module	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,319 @@
+<?php
+// $Id: subscriptions.module,v 1.16 2004/05/17 10:58:57 ax Exp $
+
+// text for the various help pages
+function subscriptions_help($section = "admin/help#subscriptions") {
+  switch ($section) {
+    case 'admin/help#subscriptions':
+      // appears on the admin module help page
+      return t('
+        <p>This module enables users to subscribe to be notified of changes to threads or categories.
+        Once enabled, all nodes will have an additional link that allows the user to change their subscriptions.
+        Additionally, all users will be given an account option to auto-subscribe to any thread to which they post.
+        No configuration is required for this module, although roles must be given permission to
+        use it.</p>
+
+        <p>Note that because this module causes the comment insert to pause while all
+        subscribers are notified, it may not be practical for large sites.</p>');
+    case 'admin/system/modules#description':
+      // appears on the admin module selection page
+      return t('Allows users to subscribe to nodes &amp taxonomy terms.');
+  }
+}
+
+function subscriptions_perm() {
+  return array("maintain subscriptions");
+}
+
+function subscriptions_blogadmin($type) {
+  if ($type == "options") {
+    return array(l(t("Auto notify via email"),"user/edit") => t("You can configure drupal to auto notify you when there are comments automatically by setting 'Auto Subscribe' in your account information."));
+  }
+}
+
+// this section will be commented out until the mailqueue module is completed
+/*
+function subscriptions_settings() {
+  $mailevent = "This variable sets the event that will initiate the sending of update notification.  ";
+  $mailevent .= "By default, subscription noticies are sent out as soon as a subscribed ";
+  $mailevent .= "node recieved a comment.  This may be inefficient for some large sites.";
+  $mailevent .= "Alternativly, if you have the mailqueue module installed, you can send the messages ";
+  $mailevent .= "to that module for handling.";
+  if ( module_exist( 'mailqueue' ) ) {
+    $output = form_radios( t( "Mailer Event" ), "subscriptions_event", variable_get( "subscriptions_event", "insert" ), array( "insert" => t( "On Comment Insert/Update" ), "queue" => t( "Send to mail queue" ) ) , t( $mailevent ) );
+  }
+  else {
+    $output = "Subscription notifications will be sent upon insert/update.  This should be ";
+    $output .= "sufficient for most installations, but you can install the mailqueue module to send on a batched schedule.";
+    $output = t( $output );
+  }
+  return $output;
+}
+*/
+
+function subscriptions_user($type, $edit, &$user) {
+  switch ($type) {
+    case "edit_form":
+      return array(t('Subscription settings') => form_item(t("Auto Subscribe"), form_checkbox(t("Automatically subscribe threads to which you post."), "subscriptions_auto", 1, $user->subscriptions_auto, t("Checking this box allows you to be automatically subscribed to any thread you create or post a comment to."))));
+      break;
+  }
+}
+
+// returns all subscriptions for a given user. uses caching
+function subscriptions_get_user($account = NULL) {
+  global $user;
+  static $subscriptions;
+
+  if (is_null($account)) {
+    $account = $user;
+  }
+  if (is_null($subscriptions[$account->uid])) {
+    $queryn = "SELECT td.tid, td.name, n.nid, n.title FROM";
+    $queryn .= "(({subscriptions} s LEFT JOIN {node} n  ON n.nid = s.sid) ";
+    $queryn .= "LEFT JOIN {term_node} tn ON tn.nid = s.sid) ";
+    $queryn .= "LEFT JOIN {term_data} td ON td.tid = tn.tid ";
+    $queryn .= "WHERE n.status = 1 AND s.uid = $account->uid AND s.stype = \"node\" ";
+    $queryt .= "SELECT td.tid, td.name FROM ";
+    $queryt .= "{subscriptions} s INNER JOIN {term_data} td ON td.tid = s.sid ";
+    $queryt .= "WHERE s.uid = $account->uid AND s.stype = \"taxa\"";
+    $resultn = db_query($queryn);
+    $resultt = db_query($queryt);
+
+    $subscriptions[$account->uid]['node'] = $subscriptions[$account->uid]['taxa'] = array();
+    while ($nsub = db_fetch_object($resultn)) {
+      $subscriptions[$account->uid]['node'][$nsub->nid] = $nsub;
+    }
+    while ($tsub = db_fetch_object($resultn)) {
+      $subscriptions[$account->uid]['taxa'][$tsub->tid] = $tsub;
+    }
+  }
+
+  return $subscriptions[$account->uid];
+}
+
+// formats the mail and sends it.
+function subscriptions_sendmail($to, $subject, $body, $headers) {
+  $mail_success = user_mail($to, $subject, $body, $headers);
+  if ($mail_success) {
+    watchdog("regular", t("subscription notification for  '") . $name . "' &lt;" . $to . "&gt;");
+  }
+  else {
+    watchdog("error", t("error mailing subscription notification: '") . $name . "' &lt;" . $to . "&gt;");
+  }
+}
+// get email vars
+function subscriptions_mailvars ($sid, $ssid, $uid, $stype) {
+  global $base_url;
+  $from = variable_get("site_mail", ini_get("sendmail_from"));
+  // if comment insertion, get vars
+  if ($stype == "node") {
+    $result = db_query("SELECT title FROM {node} WHERE nid = $sid");
+    $subj = db_result($result);
+    $result = db_query("SELECT u.uid, u.name, u.mail FROM {users} u INNER JOIN {subscriptions} s ON u.uid = s.uid WHERE s.sid = $sid AND s.stype = \"node\"");
+    $strtype = "thread";
+    $nid = $sid;
+    $cid = "comment-$ssid";
+  }
+  // if node insert, test if node has a taxonomy else skip
+  if ($stype == "taxa" && ! is_null($sid)) {
+    $result = db_query("SELECT name FROM {term_data} WHERE tid = $sid");
+    $subj = db_result($result);
+    $result = db_query("SELECT u.mail, u.name, u.uid FROM {users} u INNER JOIN {subscriptions} s ON u.uid = s.uid WHERE s.sid = $sid AND stype = \"taxa\"");
+    $strtype = "category";
+    $nid = $ssid;
+    $cid = NULL;
+  }
+  // loop through subscribers and call mail function
+  while ($subscriptions = db_fetch_object($result)) {
+    if ($subscriptions->uid != $uid && ! is_null($sid)) {
+      $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from";
+      $subject = "[" . variable_get("site_name", "drupal") . "] " . t("New comment posted to") . " '$subj'";
+      $body = t("Greetings ") . $subscriptions->name . ",\n\n";
+      $body .= t("A new comment has been posted to '$subj' which you subscribed to. To view the thread, ");
+      $body .= t("navigate to ") . url("node/view/$nid", NULL, $cid, 1). ".";
+      $body .= "\n-- \n";
+      $body .= t("This is an automatic mail from ") . variable_get("site_name", "drupal") . ".\n";
+      $body .= t("To manage your subscriptions, browse to ") . url('subscriptions', NULL, NULL, 1). ".\n";
+      if (variable_get("subscriptions_event", "insert") == "queue" && module_exist('mailqueue')) {
+        mailqueue_insert($uid, $subject, null, $body, 1);
+      }
+      elseif (variable_get("subscriptions_event", "insert") == "insert" || ! module_exist('mailqueue')) {
+        subscriptions_sendmail($subscriptions->mail, $subject, $body, $headers);
+      }
+    }
+  }
+}
+
+function subscriptions_autosubscribe ($uid, $nid) {
+  global $user;
+  // if user has auto subscribe enabled
+  if ($user->subscriptions_auto) {
+    // check to see if already subscribed
+    $result = db_query("SELECT sid FROM {subscriptions} WHERE sid = $nid AND stype = \"node\" AND uid = $uid");
+    if (!db_num_rows($result)) {
+      // if not, subscribe
+      subscriptions_add($nid, $user->uid, 'node');
+    }
+  }
+}
+// hook to comments update event
+function subscriptions_comment ($op, $comment) {
+  global $user;
+  if ($comment->status == 0 && ($op == "insert" || $op == "update")) {
+    $nid = $comment["nid"];
+    $cid = $comment["cid"];
+    subscriptions_mailvars ($nid, $cid, $user->uid, "node");
+    subscriptions_autosubscribe ($user->uid, $nid);
+  }
+}
+// hook to taxonomy update
+function subscriptions_nodeapi (&$node, $op, $arg = 0) {
+  global $user;
+  $tid = $node->taxonomy[0];
+  $nid = $node->nid;
+  switch ($op) {
+    case "insert":
+    case "update":
+      if ($node->status) {
+        subscriptions_mailvars ($tid, $nid, $user->uid, "taxa");
+        subscriptions_mailvars ($nid, 0, $user->uid, "node");
+        if ($node->type == "blog") {
+          subscriptions_mailvars ($node->uid, $nid, $user->uid, "blog");
+        }
+        subscriptions_autosubscribe ($user->uid, $nid);
+      }
+      if (isset($node->subscriptions_subscribe)) {
+        if ($node->subscriptions_subscribe) {
+          subscriptions_add($nid, $user->uid, 'node');
+        }
+        user_save($user, array ('subscriptions_subscribe' => $node->subscriptions_subscribe));
+      }
+      break;
+    case 'form post':
+      if (!$user->subscriptions_auto) {
+        $allsubs = subscriptions_get_user();
+        $val = isset($node->subscriptions_subscribe) ? $node->subscriptions_subscribe : $allsubs['node'][$node->nid] ? 1 : $user->subscriptions_subscribe;
+        return form_item(t("Subscribe"), form_checkbox(t("Receive notification of replies to this %name.", array ('%name' => node_invoke($node, 'node_name'))), "subscriptions_subscribe", 1, $val));
+      }
+      break;
+  }
+}
+// puts "change subscription" link on all nodes
+// and "my subscriptions" link in the account settings block
+function subscriptions_link($type, $node = 0, $main) {
+  if (user_access("maintain subscriptions")) {
+    if ($type == "system") {
+      menu("subscriptions", t("my subscriptions"), "subscriptions_page", 0);
+    }
+    if ($type == "node" && $node->comment == 2) {
+      $subscriptions = subscriptions_get_user();
+      $name = node_invoke($node, 'node_name');
+      if (isset($subscriptions['node'][$node->nid])) {
+        $links[] = l(t("unsubscribe"), "subscriptions/del/node/$node->nid", array("title" => t("Stop receiving an email whenever a new comment is posted to this %n.", array ('%n' => $name))));
+      }
+      else {
+        $links[] = l(t("subscribe"), "subscriptions/add/node/$node->nid", array("title" => t("Receive an email whenever a comment is posted to this %n.", array ('%n' => $name))));
+      }
+    }
+  }
+  return $links ? $links : array();
+}
+
+function subscriptions_get_taxa($uid) {
+  $result = db_query("SELECT sid FROM {subscriptions} WHERE uid = $uid and stype=\"taxa\"");
+  while ($taxasub = db_fetch_object($result)) {
+    $tsubscriptions[] = $taxasub->sid;
+  } 
+  return $tsubscriptions ? $tsubscriptions : array();
+} 
+
+function subscriptions_gen_taxa_links($tid, $taxa) {
+  if (in_array($tid, $taxa)) {
+    $link = l(t("unsubscribe"), "subscriptions/del/taxa/$tid", array("title" => t("Unsubscribe from this category.")));
+  }
+  else {
+    $link = l(t("subscribe"), "subscriptions/add/taxa/$tid", array("title" => t("Subscribe to this category.")));
+  } 
+  return $link;
+}
+
+function subscriptions_add($sid, $uid, $stype) {
+  db_query("INSERT INTO {subscriptions} ( sid , uid, stype )  VALUES ($sid , $uid, \"$stype\")");
+}
+
+// pages where users add and delete their subscriptions to nodes
+function subscriptions_page () {
+  global $user;
+  $subscribed = false;
+  $uid = $user->uid;
+  if (! arg(2)) {
+    $sid = arg(1);
+    $nid = $sid;
+  }
+  else {
+    $op = arg(1);
+    $stype = arg(2);
+    $sid = arg(3);
+    $nid = arg(4);
+    if ($stype == "node") {
+      $nid = $sid;
+    }
+  }
+
+  if (user_access("maintain subscriptions")) {
+    switch ($op) {
+      // inserts a new subscription into the subscriptions_nodes table
+      case "add":
+        subscriptions_add($sid, $uid, $stype);
+        $message = t("<p>Your subscription was activated.<p/>");
+        if (!(arg(2) == "taxa")) {
+          $message .= l(t("Return to the thread"), "node/view/$nid") . " | ";
+        }
+        $message .= l(t("Return to the subscriptions"), "subscriptions") . "<br />";
+        print theme("page", $message, t("Subscription Activated"));
+        break;
+      // removes a subscription from the subscriptions_nodes table
+      case "del":
+        db_query("DELETE FROM {subscriptions} WHERE sid = $sid AND uid = $uid AND stype = '$stype'");
+        $message = t("<p>Your subscription was deactivated.<p/>");
+        if (!(arg(2) == "taxa")) {
+          $message .= l(t("Return to the thread"), "node/view/$nid") . " | ";
+        }
+        $message .= l(t("Return to the subscriptions"), "subscriptions") . "<br />";
+        print theme("page", $message, t("Subscription Deactivated"));
+        break;
+      // determines the user's subscription status and displays the right option to change it
+      default:
+        // get all subscriptions and write to table rows
+        $subscriptions = subscriptions_get_user();
+        // build node rows
+        foreach ($subscriptions['node'] as $nsub) {
+          $subrows .= t("<tr><td>Thread: ") . l($nsub->title, "node/view/$nsub->nid") . "</td><td>[" . l(t("unsubscribe"), "subscriptions/del/node/" . $nsub->nid, array("title" => t("Unsubscribe from this thread."))) . "]</td></tr>\n";
+        }
+        // traverse the taxonomy tree
+        $taxa = subscriptions_get_taxa($uid);
+        foreach (taxonomy_get_vocabularies() as $vocab) { 
+          $tree = taxonomy_get_tree($vocab->vid);        
+          $subrows .= "<tr><td colspan=\"2\"><b>" . $vocab->name . "</b></td></tr>\n";
+          foreach ($tree as $term) {
+            $subrows .= t("<tr><td>Category: ") . l($term->name, "taxonomy/page/or/$term->tid") . "</td><td>[" . subscriptions_gen_taxa_links($term->tid, $taxa) . "]</td></tr>\n";
+          } 
+        }
+
+        if (strlen($subrows) == 0) {
+          $message .= t("<p>You are not currently subscribed to any active threads or categories.</p>");
+        }
+        else {
+          $message .= "<p align=\"center\"><table>" . $subrows . "</table></p>";
+        }
+
+        print theme("page", $message, t("Your Subscriptions"));
+        break;
+    }
+  }
+  else {
+    print theme("page", message_access(), t("Access Denied"));
+  }
+}
+
+?>
diff -urN drupal-4.4.1/modules/tag.module drupal4blog/modules/tag.module
--- drupal-4.4.1/modules/tag.module	Thu Jan  1 07:30:00 1970
+++ drupal4blog/modules/tag.module	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,510 @@
+<?php
+// $Id$
+
+// invoke all tags from all modules
+module_invoke_all('taginit');
+tag_init_glob();
+
+function theme_tag_help($title,$info) {
+  return <<<_TAG_HELP_
+<li><strong>$title</strong><br/>
+$info</li>
+<p></p>
+_TAG_HELP_;
+}
+
+function tag_help($section) {
+  switch ($section) {
+    case 'tag/help':
+      $output .= theme('tag_help','DPPageTitle',t('The title of the current page.'));
+      $output .= theme('tag_help','DPSimpleCSS',t("The URL to the simple theme's CSS. See CSS in %info.", array('%info' => l(t('simple theme settings'), 'admin/system/themes/simple'))));
+      $output .= theme('tag_help','DPHeaders',t('Standard Drupal headers.'));
+      $output .= theme('tag_help','DPOnLoads',t('Insert Onload functions specify by modules.'));
+      $output .= theme('tag_help','DPSiteURL',t('The base URL of the site.'));
+      $output .= theme('tag_help','DPSiteName',t('The name of this web site. See %info.', array('%info' => l(t('system configuration'), 'admin/system'))));
+      break;
+    case 'admin/system/modules#description':
+      $output .= t("Allow simple template tags.");
+      break;
+  }
+
+  return $output;
+}
+
+function tag_link($type, $node = 0, $main) {
+  if ($type  == 'system') {
+    if (user_access("administer site configuration")) {
+      menu('tag/help', t('Tag'), 'tag_info', 0, MENU_HIDE);
+    }
+  }
+}
+
+function tag_info() {
+  print theme('page',$output, t('help'));
+}
+
+function tag_init_glob() {
+  global $taglist, $tag_search, $tag_php;
+
+  if (!$taglist) return;
+
+  // we need to sort the taglists so they dont overlap each another...
+
+  foreach ($taglist as $tag => $taginfo) {
+    // if it is a function, lets make a condition out of it!
+    if ($taglist[$tag]['func']) {
+      $add['If'.$tag] = array('cond' => $taglist[$tag]['func']);
+      $add['IfNot'.$tag] = array('notcond' => $taglist[$tag]['func']);
+      $add['ElseIf'.$tag] = array('elseif' => $taglist[$tag]['func']);
+      $add['ElseIfNot'.$tag] = array('elseifnot' => $taglist[$tag]['func']);
+    }
+    else if ($taglist[$tag]['each'] && $taglist[$tag]['vars']) {
+      $add['If'.$tag] = array('cond' => $taglist[$tag]['each']);
+      $add['IfNot'.$tag] = array('notcond' => $taglist[$tag]['each']);
+      $add['ElseIf'.$tag] = array('elseif' => $taglist[$tag]['each']);
+      $add['ElseIfNot'.$tag] = array('elseifnot' => $taglist[$tag]['each']);
+      $add['IfFirst'.$tag] = array('iffirst' => $taglist[$tag]['vars']);
+      $add['IfNotFirst'.$tag] = array('ifnotfirst' => $taglist[$tag]['vars']);
+    }
+  }
+  $taglist = array_merge($add,$taglist);
+
+  foreach ($taglist as $tag => $taginfo) {
+    $php = tag_tag2php($tag);
+    $nopv = preg_replace("/[PARAMS|VARIABLES]/",'',$php);
+
+    // either in the form of <$DPTag$> or <DPTag>
+    $tag_search[] = "|\<\\\$DP".$tag."\\\$\>|";
+    $tag_php[] = $nopv;
+    $tag_search[] = "|\<DP".$tag."\>|";
+    $tag_php[] = $nopv;
+
+    $pv = preg_replace("/PARAMS/",", \\\\1",$php);
+    if ($taglist[$tag]['cond'] || $taglist[$tag]['notcond'] ||
+        $taglist[$tag]['elseif'] || $taglist[$tag]['elseifnot']) {
+      // only conditions can have evaluation criteria
+      $pv = preg_replace("/VARIABLES/"," \\\\2",$pv);
+      $tag_search[] = "|\<\\\$DP".$tag."\\(([^\\)]*)\\)\s*([^\\\$]*)\\\$\>|";
+      $tag_php[] = $pv;
+      $tag_search[] = "|\<DP".$tag."\\(([^\\)]*)\\)\s*([^\\\$]*)\>|";
+      $tag_php[] = $pv;
+    } else if ($taglist[$tag]['func']) {
+      $pv = preg_replace("/VARIABLES/",'',$pv);
+      $tag_search[] = "|\<\\\$DP".$tag."\\(([^\\)]*)\\)\s*\\\$\>|";
+      $tag_php[] = $pv;
+      $tag_search[] = "|\<DP".$tag."\\(([^\\)]*)\\)\s*\>|";
+      $tag_php[] = $pv;
+    }
+  }
+}
+
+function tag_taginit() {
+  global $taglist;
+
+  // site info
+  $taglist['SiteURL'] = array('func' => 'tag_site_url');
+  $taglist['SiteName'] = array('func' => 'tag_site_name');
+  $taglist['SiteSlogan'] = array('func' => 'tag_site_slogan');
+  $taglist['SiteMission'] = array('func' => 'tag_site_mission');
+  $taglist['SiteLayout'] = array('func' => 'tag_site_layout');
+  $taglist['SiteFooter'] = array('func' => 'tag_site_footer');
+
+  // generic
+  $taglist['Content'] = array('func' => 'tag_generic_content');
+  $taglist['Variable'] = array('func' => 'tag_generic_variable');
+  $taglist['UserAccess'] = array('func' => 'tag_generic_user_access');
+
+  // drupal core
+  $taglist['Headers'] = array('func' => 'tag_drupal_headers');
+  $taglist['Footers'] = array('func' => 'tag_drupal_footers');
+  $taglist['OnLoads'] = array('func' => 'tag_drupal_onload');
+
+  // page info
+  $taglist['Title'] = array('func' => 'tag_page_title');
+  $taglist['Breadcrumbs'] = array('func' => 'tag_page_breadcrumb');
+  $taglist['ForEachBreadcrumb'] = array('foreach' => 'tag_page_breadcrumb',
+                                        'vars' => 'breadcrumb');
+  $taglist['Breadcrumb'] = array('each' => 'tag_page_breadcrumb_each',
+                                 'vars' => 'breadcrumb');
+  $taglist['Help'] = array('func' => 'tag_page_help');
+  $taglist['Messages'] = array('func' => 'tag_page_message');
+  $taglist['ForEachMessage'] = array('foreach' => 'tag_page_message',
+                                     'vars'    => 'message');
+  $taglist['Message'] = array('each' => 'tag_page_message_each',
+                              'vars' => 'message');
+  $taglist['MessageType'] = array('func' => 'tag_page_message_type');
+
+  // links
+  $taglist['Links'] = array('func' => 'tag_links');
+  $taglist['ForEachLink'] = array('foreach' => 'tag_links',
+                                  'vars'    => 'link');
+  $taglist['Link'] = array('each' => 'tag_links_each',
+                           'vars' => 'link');
+
+  // block
+  $taglist['Sidebar'] = array('func' => 'block_tag_sidebar');
+  $taglist['BlockModule'] = array('func' => 'block_tag_module');
+  $taglist['BlockTitle'] = array('func' => 'block_tag_title');
+  $taglist['BlockContent'] = array('func' => 'block_tag_content');
+  $taglist['Block'] = array('func' => 'block_tag_block');
+
+  // node
+  $taglist['NodeStatic'] = array('func' => 'node_tag_static');
+  $taglist['NodeDate'] = array('func' => 'node_tag_date');
+  $taglist['NodeURL'] = array('func' => 'node_tag_url');
+  $taglist['NodeTitle'] = array('func' => 'node_tag_title');
+  $taglist['NodeName'] = array('func' => 'node_tag_name');
+  $taglist['Node'] = array('func' => 'node_tag_node');
+
+  // comment
+  $taglist['CommentNew'] = array('func' => 'comment_tag_comment_new');
+  $taglist['CommentID'] = array('func' => 'comment_tag_comment_id');
+  $taglist['CommentShowTitle'] = array('func' => 'comment_tag_show_title');
+  $taglist['CommentTitleLink'] = array('func' => 'comment_tag_comment_title_link');
+  $taglist['CommentTitle'] = array('func' => 'comment_tag_comment_title');
+  $taglist['CommentName'] = array('func' => 'comment_tag_comment_name');
+  $taglist['CommentDate'] = array('func' => 'comment_tag_comment_date');
+  $taglist['CommentVisible'] = array('func' => 'comment_tag_comment_visible');
+  $taglist['CommentDepth'] = array('func' => 'comment_tag_comment_depth');
+  $taglist['Comment'] = array('func' => 'comment_tag_comment');
+
+  // taxonomy
+  $taglist['Categories'] = array('func' => 'taxonomy_tag_categories');
+  $taglist['ForEachCategory'] = array('foreach' => 'taxonomy_tag_categories',
+                                      'vars' => 'category');
+  $taglist['Category'] = array('each' => 'taxonomy_tag_categories_each',
+                               'vars' => 'category');
+
+  // simple css
+  $taglist['SimpleCSS'] = array('func' => 'simple_tag_css');
+
+  // basic php tags
+  $taglist['Else'] = array('php' => "<?php else : ?>");
+  $taglist['EndIf'] = array('php' => "<?php endif; ?>");
+  $taglist['EndForEach'] = array('php' => "<?php endforeach; ?>");
+}
+
+function tag_tag2php($tag) {
+  global $taglist;
+
+  if (!isset($taglist[$tag])) {
+    watchdog('error', 'Unknown tag DP'.$tag.'!');
+    return '';
+  }
+
+  // if it is php
+  if ($taglist[$tag]['php']) {
+    return $taglist[$tag]['php'];
+  }
+
+  // if it is a function ...
+  if ($taglist[$tag]['func']) {
+    return "<?php print ".$taglist[$tag]['func']."(\$varsPARAMS); ?>";
+  }
+
+  // if it is a condition function ...
+  if ($taglist[$tag]['cond']) {
+    return "<?php if (".$taglist[$tag]['cond']."(\$varsPARAMS)VARIABLES) : ?>";
+  }
+
+  if ($taglist[$tag]['notcond']) {
+    return "<?php if (!".$taglist[$tag]['notcond']."(\$varsPARAMS)VARIABLES) : ?>";
+  }
+
+  if ($taglist[$tag]['elseif']) {
+    return "<?php elseif (".$taglist[$tag]['elseif']."(\$varsPARAMS)VARIABLES) : ?>";
+  }
+
+  if ($taglist[$tag]['elseifnot']) {
+    return "<?php elseif (!".$taglist[$tag]['elseifnot']."(\$varsPARAMS)VARIABLES) : ?>";
+  }
+
+  // if it is a foreach loop ...
+  if ($taglist[$tag]['foreach'] && $taglist[$tag]['vars']) {
+    $output .= "<?php \$".$taglist[$tag]['vars']."_count = 0; ?>";
+    $output .= "<?php foreach (".$taglist[$tag]['foreach']."(\$varsPARAMS) as \$".$taglist[$tag]['vars'].") : ?>";
+    $output .= "<?php \$vars['".$taglist[$tag]['vars']."'] = \$".$taglist[$tag]['vars']."; ?>";
+    return $output;
+  }
+
+  if ($taglist[$tag]['each'] && $taglist[$tag]['vars']) {
+    $output .= "<?php \$".$taglist[$tag]['vars']."_count++; ?>";
+    $output .= "<?php print ".$taglist[$tag]['each']."(\$varsPARAMS); ?>";
+    return $output;
+  }
+
+  if ($taglist[$tag]['iffirst']) {
+    return "<?php if (\$".$taglist[$tag]['iffirst']."_count == 0) : ?>";
+  }
+
+  if ($taglist[$tag]['ifnotfirst']) {
+    return "<?php if (\$".$taglist[$tag]['ifnotfirst']."_count == 0) : ?>";
+  }
+
+  watchdog('error', 'Tag DP'.$tag.' cannot be converted to php!');
+  return '';
+}
+
+function tag_process($raw) {
+  global $taglist, $tag_search, $tag_php;
+  $php = preg_replace($tag_search, $tag_php, $raw);
+
+  preg_match_all("|<\\\${0,1}DP[^>]+\\\${0,1}>|",$php,$errors);
+  $errors = array_unique($errors[0]);
+  foreach ($errors as $error) 
+    form_set_error('simple_theme_edit',"Cannot process $error");
+
+  return $php;
+}
+
+/*
+** customized functions
+*/
+
+// site functions
+function tag_site_url($vars = array(), $v1 = NULL) {
+  return url($v1);
+}
+
+function tag_site_name($vars = array()) {
+  return variable_get('site_name', 'Drupal');
+}
+
+function tag_site_slogan($vars = array()) {
+  return variable_get('site_slogan', '');
+}
+
+function tag_site_mission($vars = array()) {
+  // return mission only on frontpage
+  if ($_GET["q"] == variable_get("site_frontpage", "node"))
+    return variable_get('site_mission', '');
+}
+
+function tag_site_layout($vars = array()) {
+  static $layout;
+
+  if ($layout) return $layout;
+
+  $results = db_query('SELECT * FROM {blocks} WHERE (status = 1 OR custom =1)');
+
+  while ($block = db_fetch_object($results)) 
+    $region[$block->region]++;
+
+  if ($region[0] && $region[1]) {
+    $layout = "both";
+  } else if ($region[0]) {
+    $layout = "left";
+  } else {
+    $layout = "right";
+  }
+  return $layout;
+}
+
+function tag_site_footer($vars = array()) {
+  return variable_get('site_footer', '');
+}
+
+// drupal core
+function tag_drupal_headers($vars = array()) {
+  return drupal_get_html_head();
+}
+
+function tag_drupal_footers($vars = array()) {
+  return theme('closure');
+}
+
+function tag_drupal_onload($vars = array()) {
+  return theme_onload_attribute();
+}
+
+// generic
+function tag_generic_content($vars = array()) {
+  return $vars['content'];
+}
+
+function tag_generic_variable($vars = array(), $v1 = NULL) {
+  if ($v1) return variable_get($v, '');
+  return NULL;
+}
+
+function tag_generic_user_access($vars = array(), $v1 = NULL) {
+  if ($v1) return user_access($v1);
+  return NULL;
+}
+
+// page info
+function tag_page_title($vars = array()) {
+  $title = drupal_get_title();
+  if ($title) return $title;
+  return variable_get('site_name', 'Drupal');
+}
+
+function tag_page_breadcrumb($vars = array()) {
+  return drupal_get_breadcrumb();
+}
+
+function tag_page_breadcrumb_each($vars = array()) {
+  return $vars['breadcrumb'];
+}
+
+function tag_page_help($vars = array()) {
+  return menu_get_active_help();
+}
+
+function tag_page_help_each($vars = array()) {
+  return $vars['help'];
+}
+
+function tag_page_message($vars = array()) {
+  // this seem to be a better way to get message without clearing
+  // what is in $_SESSION['message'] but you must manually reset 
+  // the message afterwards...
+  return drupal_set_message();
+}
+
+function tag_page_message_each($vars = array()) {
+  return $vars['message'][0];
+}
+
+function tag_page_message_type($vars = array()) {
+  return $vars['message'][1];
+}
+
+function tag_links($vars = array()) {
+  return $vars['links'];
+}
+
+function tag_links_each($vars = array()) {
+  return $vars['link'];
+}
+
+// block functions - move to block.module some day...
+function block_tag_block($vars = array(), $v1 = NULL) {
+  if (!$v1) return $vars['block'];
+  return $vars['block']->$v1;
+}
+
+function block_tag_module($vars = array()) {
+  return $vars['block']->module;
+}
+
+function block_tag_title($vars = array()) {
+  return $vars['block']->subject;
+}
+
+function block_tag_content($vars = array()) {
+  return $vars['content'];
+}
+
+function block_tag_sidebar($vars = array(), $v1 = 'all') {
+  static $sidebar;
+
+  if ($sidebar[$v1]) return $sidebar[$v1];
+  $sidebar[$v1] = theme("blocks", $v1);
+
+  return $sidebar[$v1];
+}
+
+// node functions - move to node.module some day...
+function node_tag_node($vars = array(), $v1 = NULL) {
+  if (!$v1) return $vars['node'];
+  return $vars['node']->$v1;
+}
+
+function node_tag_static($vars = array()) {
+  return $vars['node']->static;
+}
+
+function node_tag_date($vars = array(), $v1 = NULL) {
+  if (!$v1) return format_date($vars['node']->created);
+  switch ($v1) {
+    case 'small':
+    case 'large':
+    case 'medium':
+      return format_date($vars['node']->created, $v1);
+    default:
+      return format_date($vars['node']->created, 'custom', $v1);
+  }
+}
+
+function node_tag_url($vars = array()) {
+  return url("node/view/".$vars['node']->nid,NULL,NULL,TRUE);
+}
+
+function node_tag_title($vars = array()) {
+  return $vars['node']->title;
+}
+
+function node_tag_name($vars = array()) {
+  return format_name($vars['node']);
+}
+
+// comment functions - move to comment.module some day...
+function comment_tag_comment($vars = array(), $v1 = NULL) {
+  if (!$v1) return $vars['comment'];
+  return $vars['comment']->$v1;
+}
+
+function comment_tag_comment_new($vars = array()) {
+  return $vars['comment']->new;
+}
+
+function comment_tag_comment_id($vars = array()) {
+  return $vars['comment']->cid;
+}
+
+function comment_tag_show_title($vars = array()) {
+  return (variable_get('comment_subject_field', 0) && variable_get("comment_complex", 0));
+}
+
+function comment_tag_comment_title($vars = array()) {
+  return $vars['comment']->subject;
+}
+
+function comment_tag_comment_name($vars = array()) {
+  return format_name($vars['comment']);
+}
+
+function comment_tag_comment_date($vars = array(), $v1 = NULL) {
+  if (!$v1) return format_date($vars['comment']->timestamp);
+  switch ($v1) {
+    case 'small':
+    case 'large':
+    case 'medium':
+      return format_date($vars['comment']->timestamp, $v1);
+    default:
+      return format_date($vars['comment']->timestamp, 'custom', $v1);
+  }
+}
+
+function comment_tag_comment_visible($vars = array()) {
+  return $vars['comment']->visible;
+}
+
+function comment_tag_comment_title_link($vars = array()) {
+  return l($vars['comment']->subject, comment_referer_load().'/'.$vars['comment']->cid.'#'.$vars['comment']->cid);
+}
+
+function comment_tag_comment_depth($vars = array()) {
+  global $user;
+  $mode = $user->mode ? $user->mode : ($_SESSION["comment_mode"] ? $_SESSION["comment_mode"] : variable_get("comment_default_mode", 4));
+  if (variable_get("comment_complex",0) == 0) $mode = 2;
+  if ($mode == 1 || $mode == 2) return 0;
+  return $vars['comment']->depth;
+}
+
+// taxonomy functions - move to taxonomy.module some day...
+function taxonomy_tag_categories($vars = array()) {
+  return $vars['taxonomy'];
+}
+
+function taxonomy_tag_categories_each($vars = array()) {
+  return $vars['category'];
+}
+
+// simple css
+function simple_tag_css($vars = array()) {
+  return simple_style_path();
+}
+
+?>
diff -urN drupal-4.4.1/modules/taxonomy.module drupal4blog/modules/taxonomy.module
--- drupal-4.4.1/modules/taxonomy.module	Sun May  2 23:45:30 2004
+++ drupal4blog/modules/taxonomy.module	Tue Jun  8 14:19:14 2004
@@ -17,6 +17,12 @@
   }
 }
 
+function taxonomy_settings() {
+  $output = form_radios(t("Taxonomy Complexity"), "taxonomy_complex", variable_get("taxonomy_complex", 0), array(0 => t("Simple Taxonomy"), 1 => t("Nested Taxonomy"), 2 => t("Full Taxonomy Control")),t("Taxonomy is just a fancy word for Category. You can attach 'taxonomy' to your entries which will specify their categories. 'Simple Taxonomy' is easier to managed and sufficient for most people. 'Nested Taxonomy' enables hierarchical categories. 'Full Taxonomy Control' gives you complete control."));
+  $output .= form_textfield(t("Taxonomy title"), "taxonomy_block_title", variable_get("taxonomy_block_title", "Categories"), 70, 180, t("") . " " . t("Title of the Taxonomyblock, e.g. Categories"));
+  return $output;
+}
+
 function taxonomy_perm() {
   return array("administer taxonomy");
 }
@@ -60,6 +66,61 @@
   }
 }
 
+function _taxonomy_list_terms($vid) {
+  $tree = taxonomy_get_tree($vid);
+  if ($tree) {
+    $depth = 0;
+    $complex = variable_get("taxonomy_complex",0);
+    foreach($tree as $term) {
+      if ($complex) {
+        while ($depth < $term->depth) {
+          $output .= "<ul>"; $depth++;
+        }
+        while ($depth > $term->depth) {
+          $output .= "</ul>"; $depth--;
+        }
+      }
+
+      $output .= "<li>".l($term->name,"taxonomy/page/or/$term->tid")."</li>";
+    }
+    if ($complex) {
+      while ($depth) {
+        $output .= "</ul>";
+        $depth--;
+      }
+    }
+  }
+  if ($output) return "<ul>".$output."</ul>";
+  return "";
+}
+
+function taxonomy_block($op = "list", $delta = 0) {
+  if ($op == "list") {
+    $block[0]['info'] = t("Taxonomy");
+    return $block;
+  }
+
+  $block["subject"] = variable_get("taxonomy_block_title","Categories");
+
+  if (variable_get("taxonomy_complex",0)<=1) {
+    $vocabularies = taxonomy_get_vocabulary_by_name("Category");
+    if ($vocabularies[0]) 
+      $block["content"] = _taxonomy_list_terms($vocabularies[0]->vid);
+
+    return $block;
+  }
+
+  $vocabularies = taxonomy_get_vocabularies();
+  foreach ($vocabularies as $vocabulary) {
+    $output .= "<ul><li>".$vocabulary->name."</li>";
+    $output .= _taxonomy_list_terms($vocabulary->vid);
+    $output .= "</ul>";
+    $block["content"] = $output;
+  }
+
+  return $block;
+}
+
 /*
 ** admin pages (form, save, overview)
 */
@@ -149,30 +210,34 @@
   $form = form_textfield(t("Term name"), "name", $edit["name"], 50, 64, t("Required") .". ". t("The name for this term.  Example: 'Linux'."));
   $form .= form_textarea(t("Description"), "description", $edit["description"], 60, 5, t("Optional") .". ". t("A description of the term."));
 
-  if ($vocabulary->hierarchy) {
-    $parent = array_keys(taxonomy_get_parents($edit["tid"]));
-    $children = taxonomy_get_tree($vocabulary_id, $edit["tid"]);
-
-    // you can't be son of yourself nor of your children
-    foreach ($children as $child) {
-      $exclude[] = $child->tid;
-    }
-    $exclude[] = $edit["tid"];
-
-    if ($vocabulary->hierarchy == 1) {
-      $form .= _taxonomy_term_select(t("Parent"), "parent", $parent, $vocabulary_id, t("Required") .". ". l(t("Parent term"), "admin/taxonomy/help", NULL, NULL, "parent") .".", 0, "<". t("root") .">", $exclude);
-    }
-    elseif ($vocabulary->hierarchy == 2) {
-      $form .= _taxonomy_term_select(t("Parents"), "parent", $parent, $vocabulary_id, t("Required") .". ". l(t("Parent terms"), "admin/taxonomy/help", NULL, NULL, "parent") .".", 1, "<". t("root") .">", $exclude);
+  if (variable_get("taxonomy_complex",0)>0) {
+    if ($vocabulary->hierarchy) {
+      $parent = array_keys(taxonomy_get_parents($edit["tid"]));
+      $children = taxonomy_get_tree($vocabulary_id, $edit["tid"]);
+  
+      // you can't be son of yourself nor of your children
+      foreach ($children as $child) {
+        $exclude[] = $child->tid;
+      }
+      $exclude[] = $edit["tid"];
+  
+      if ($vocabulary->hierarchy == 1) {
+        $form .= _taxonomy_term_select(t("Parent"), "parent", $parent, $vocabulary_id, t("Required") .". ". l(t("Parent term"), "admin/taxonomy/help", NULL, NULL, "parent") .". Specify this to define sub-term.", 0, "<". t("root") .">", $exclude);
+      }
+      elseif ($vocabulary->hierarchy == 2) {
+        $form .= _taxonomy_term_select(t("Parents"), "parent", $parent, $vocabulary_id, t("Required") .". ". l(t("Parent terms"), "admin/taxonomy/help", NULL, NULL, "parent") .".", 1, "<". t("root") .">", $exclude);
+      }
+    }
+  
+    if (variable_get("taxonomy_complex",0)>1) {
+      if ($vocabulary->relations) {
+        $form .= _taxonomy_term_select(t("Related terms"), "relations", array_keys(taxonomy_get_related($edit["tid"])), $vocabulary_id, t("Optional") .". ", 1, "<". t("none") .">", array($edit["tid"]));
+      }
+    
+      $form .= form_textarea(t("Synonyms"), "synonyms", implode("\n", taxonomy_get_synonyms($edit["tid"])), 30, 5, t("Optional") . ". ". t("<a href=\"%help-url\">Synonyms</a> of this term, one synonym per line.", array("%help-url" => url("admin/taxonomy/help", NULL, NULL, "synonyms"))));
+      $form .= form_weight(t("Weight"), "weight", $edit["weight"], 10, t("Optional. In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top."));
     }
   }
-
-  if ($vocabulary->relations) {
-    $form .= _taxonomy_term_select(t("Related terms"), "relations", array_keys(taxonomy_get_related($edit["tid"])), $vocabulary_id, t("Optional") .". ", 1, "<". t("none") .">", array($edit["tid"]));
-  }
-
-  $form .= form_textarea(t("Synonyms"), "synonyms", implode("\n", taxonomy_get_synonyms($edit["tid"])), 30, 5, t("Optional") . ". ". t("<a href=\"%help-url\">Synonyms</a> of this term, one synonym per line.", array("%help-url" => url("admin/taxonomy/help", NULL, NULL, "synonyms"))));
-  $form .= form_weight(t("Weight"), "weight", $edit["weight"], 10, t("Optional. In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top."));
   $form .= form_hidden("vid", $vocabulary->vid);
   $form .= form_submit(t("Submit"));
 
@@ -186,21 +251,27 @@
 
 function taxonomy_save_term($edit) {
   if ($edit["tid"] && $edit["name"]) {
-    $data = array("name" => $edit["name"], "description" => $edit["description"], "weight" => $edit["weight"]);
-
-    db_query("UPDATE {term_data} SET ". _prepare_update($data) ." WHERE tid = %d", $edit["tid"]);
-    module_invoke_all("taxonomy", "update", "term", $edit);
-    $message = t("the term '%a' has been updated.", array("%a" => $edit["name"]));
+    if (_taxonomy_dup_term($edit["tid"],$edit["name"])) {
+      $message = t("the term '%a' already exist.",array("%a" => $edit["name"]));
+    } else {
+      $data = array("name" => $edit["name"], "description" => $edit["description"], "weight" => $edit["weight"]);
+      db_query("UPDATE {term_data} SET ". _prepare_update($data) ." WHERE tid = %d", $edit["tid"]);
+      module_invoke_all("taxonomy", "update", "term", $edit);
+      $message = t("the term '%a' has been updated.", array("%a" => $edit["name"]));
+    }
   }
   else if ($edit["tid"]) {
-    return taxonomy_del_term($edit["tid"]);
-  }
-  else {
-    $edit["tid"] = db_next_id("{term_data}_tid");
-    $data = array("tid" => $edit["tid"], "name" => $edit["name"], "description" => $edit["description"], "vid" => $edit["vid"], "weight" => $edit["weight"]);
-    db_query("INSERT INTO {term_data} ". _prepare_insert($data, 1) ." VALUES ". _prepare_insert($data, 2));
-    module_invoke_all("taxonomy", "insert", "term", $edit);
-    $message = t("created new term '%name'.", array("%name" => $edit["name"]));
+    $message = t("Please click 'Delete' if you wish to delete the term.");
+  } else {
+    if (_taxonomy_dup_term($edit["tid"],$edit["name"])) {
+      $message = t("the term '%name' already exist.", array("%name" => $edit["name"]));
+    } else {
+      $edit["tid"] = db_next_id("{term_data}_tid");
+      $data = array("tid" => $edit["tid"], "name" => $edit["name"], "description" => $edit["description"], "vid" => $edit["vid"], "weight" => $edit["weight"]);
+      db_query("INSERT INTO {term_data} ". _prepare_insert($data, 1) ." VALUES ". _prepare_insert($data, 2));
+      module_invoke_all("taxonomy", "insert", "term", $edit);
+      $message = t("created new term '%name'.", array("%name" => $edit["name"]));
+    }
   }
 
   // relations (seem very powerful, but I have to understand it completely)
@@ -255,6 +326,7 @@
 
   cache_clear_all();
 
+  drupal_set_message(t("deleted term '%name'.", array("%name" => $term->name)));
   return t("deleted term '%name'.", array("%name" => $term->name));
 }
 
@@ -271,6 +343,27 @@
 }
 
 function taxonomy_overview() {
+  if (variable_get("taxonomy_complex",0)<=1) {
+    $vocabularies = taxonomy_get_vocabulary_by_name("Category");
+    if (!$vocabularies[0]) {
+      $vid = db_next_id("{vocabulary}_vid");
+      db_query("INSERT INTO {vocabulary} VALUES (%d, '%s', '%s', %d, %d, %d, %d, '%s', %d)", $vid, "Category", "Categories for Blog Entries", 0, 1, 1, 0, 'blog', 0);
+      $vocabularies = taxonomy_get_vocabulary_by_name("Category");
+    }
+    $vid = $vocabularies[0]->vid;
+
+    $headers = array(t("name"), t("description"));
+
+    $tree = taxonomy_get_tree($vid);
+    if ($tree) {
+      foreach($tree as $term) {
+        $rows[] = array(l(_taxonomy_depth($term->depth)." ".$term->name, "admin/taxonomy/edit/term/$term->tid") , $term->description);
+      }
+    }
+    $rows[] = array(array("data" => l(t("add category"), "admin/taxonomy/add/term/$vid"), "colspan" => 2));;
+
+    return theme("table",$headers,$rows);
+  }
 
   $output .= "<h3>". t("Vocabularies overview") ."</h3>";
 
@@ -817,7 +910,7 @@
         taxonomy_save_term($edit);
         if (!$edit["tid"]) {
           // if INSERT show form again
-          $output .= taxonomy_form_term();
+          $output .= taxonomy_overview(); // taxonomy_form_term();
           break;
         }
         // else (UPDATE or DELETE) fall through
@@ -882,4 +975,26 @@
 
   return $output;
 }
+
+function taxonomy_blogadmin($type) {
+  $list = array();
+
+  if ($type == "block") {
+    if (user_access("administer taxonomy"))
+      $list[] = l(t("categories"), "admin/taxonomy");
+  } else if ($type == "options") {
+    if (user_access("administer site configuration")) {
+      $list[l(t("Category options"),"admin/system/modules/taxonomy")] = t("Configure category/taxonomy options");
+    }
+  }
+
+  return $list;
+}
+
+function _taxonomy_dup_term($tid,$name) {
+  $term = db_fetch_object(db_query("SELECT * FROM {term_data} WHERE tid=%d",$tid));
+  $result = db_fetch_object(db_query("SELECT * FROM {term_data} WHERE LOWER(name)=LOWER('%s') AND vid=%d",trim($name),$term->vid));
+  return $result;
+}
+
 ?>
diff -urN drupal-4.4.1/modules/trackback.module drupal4blog/modules/trackback.module
--- drupal-4.4.1/modules/trackback.module	Fri May 21 13:31:28 2004
+++ drupal4blog/modules/trackback.module	Tue Jun  8 14:19:14 2004
@@ -5,27 +5,101 @@
   switch ($section) {
     case 'admin/system/modules#description':
       return t("Allow for sending and receiving TrackBacks, which is a way for sites to notify another that they have commented on a post.");
+    case 'admin/system/modules/trackback':
+      return t("Trackback can be attached to any node and is treated like comments for all intent and purpose.");
   }
 }
 
+function trackback_settings() {
+    $output = "";
+
+    $array_moderate = array(0 => t('No'), 1 => t('Yes'));
+    $output .= form_radios(t('Display Trackback only after approval'), 'trackback_moderation', variable_get('trackback_moderation', 0), $array_moderate, t('If yes, all trackback must be moderated before it can be seen by others.'));
+
+    $output .= form_radios(t('Trackback autodiscovery'), 'trackback_autodiscovery', variable_get('trackback_autodiscovery', 1), $array_moderate, t('If yes, any external links will be extracted and the appropriate sites will be sent a Trackback ping. Take note it might slowdown your posting esp. if there is a lot of external links.'));
+
+    return $output;
+}
+
 function trackback_send($node) {
   $params = array(
-    "title" => $node->title,
-    "excerpt" => substr(check_output($node->teaser), 0, 255),
-    "blog_name" => variable_get("site_name", ""),
-    "url" => url("node/view/$node->nid", NULL, NULL, 1)
+    'title' => $node->title,
+    'excerpt' => substr(check_output($node->teaser), 0, 255),
+    'blog_name' => variable_get('site_name', ''),
+    'url' => url("node/view/$node->nid", NULL, NULL, 1)
   );
 
   foreach ($params as $key => $value) {
     $str[] = "$key=". urlencode($value);
   }
 
-  return drupal_http(
-    $node->tb_url,
-    $headers = array("Content-Type" => "application/x-www-form-urlencoded"),
-    "POST",
-    implode("&", $str)
-  );
+  $node->toping = ""; # Start from scratch
+
+  $tb_urls = $node->tb_url;
+  if (variable_get('trackback_autodiscovery',1)) {
+    $text = $node->body; 
+    if ($node->type == 'blog')
+      $text .= ' '.$node->extended.' '.$node->excerpt;
+    preg_match_all("|http://[\w\.\-\+\&\+\=\/\#\%]+|",$text,$murl);
+    $links = array_unique($murl[0]);
+    foreach ($links as $url) {
+      /*
+      ** Yes, it is double query but this is to prevent from loading a 100mb
+      ** avi file only to realize we cant trackback to it
+      **
+      ** Should we check for the size of the text/html before we do a GET?
+      */
+      $result = drupal_http($url, array(), 'HEAD');
+
+      // see http://www.w3.org/TR/2002/NOTE-xhtml-media-types-20020801/
+      if (preg_match("/[text\/html|application\/xhtml\+xml|application\/xml|text\/xml]/", $result->headers['Content-Type'])) {
+        $result = drupal_http($url);
+        $result->data = preg_replace("/[\n\r]/",' ',$result->data);
+        if (preg_match("/<rdf\:RDF.*trackback\:ping.*<\/rdf\:RDF>/i",$result->data)) {
+          $tb = preg_replace("/.*<rdf\:RDF.*trackback\:ping\=\"([^\"]+)\".*<\/rdf\:RDF>.*/i","\\1",$result->data);
+
+          // if it isn't http or https or it is too long, we ignore
+          if (preg_match("/^[http|https]/",$tb) && strlen($tb)<128) {
+            $tb_urls .= ' '.$tb;
+          }
+        }
+      } 
+    }
+  }
+
+  $urls = preg_split("/\s+/",$tb_urls);
+  foreach($urls as $url) {
+    # No need to ping empty URL
+    if (strlen($url)<=0) continue;
+
+    # Do not ping those already ping'ed unless explicitly specified 
+    if (strstr($node->pinged,$url) && !strstr($node->tb_url,$url)) continue;
+
+    $result = drupal_http(
+        $url,
+        $headers = array('Content-Type' => 'application/x-www-form-urlencoded'),
+        'POST',
+        implode("&", $str)
+    );
+
+    if (strlen(trim($result->error))>0) {
+      drupal_set_message(t('Error! Unable to send Trackback ping to ').$url);
+      if (!strstr($node->toping,$url))
+        $node->toping .= "\n".$url; $node->toping = trim($node->toping);
+    } else {
+      drupal_set_message(t('Success! Trackback ping send to ').$url);
+      if (!strstr($node->pinged,$url))
+        $node->pinged .= "\n".$url; $node->pinged = trim($node->pinged);
+    }
+  }
+
+  $c = db_result(
+    db_query("SELECT COUNT(nid) FROM {trackback} WHERE nid = %d", $node->nid));
+
+  if ($c>0) 
+      trackback_update($node);
+  else 
+      trackback_insert($node);
 }
 
 function trackback_receive($node) {
@@ -40,10 +114,13 @@
     $subject = ($_REQUEST["title"]) ? $_REQUEST["title"] : $trackback->url;
     $comment = "<strong>". t("TrackBack from %url", array("%url" => "<a href=\"$trackback->url\">$trackback->name</a>")) .":</strong><br />";
     $comment .= "<blockquote>$trackback->excerpt</blockquote>";
+    $anon_name = trim(strip_tags($trackback->name));
+    $anon_url = trim(strip_tags($trackback->url));
     $cid = db_next_id("comments_cid");
     $thread = rtrim(db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid = %d", $node->nid)), "/") + 1;
-    db_query("INSERT INTO comments (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%d/')",
-             $cid, $node->nid, 0, 0, $subject, $comment, getenv("REMOTE_ADDR"), time(), 0, 0, '', $thread);
+    db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, name, homepage, thread) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s')",
+             $cid, $node->nid, 0, 0, $subject, $comment, getenv("REMOTE_ADDR"), time(), variable_get("trackback_moderation", 0), 0, $anon_name, $anon_url, $thread);
+    db_query("INSERT INTO {trackback_pingme} (nid, cid) VALUES (%d, %d)", $node->nid, $cid);
     watchdog("special", t("trackback: added '%subject'", array("%subject" => $subject)), l(t("view comment"), "node/view/". $node->nid ."#". $cid));
     $error = 0;
   }
@@ -62,37 +139,109 @@
   return $output;
 }
 
-function trackback_page() {
-  if (is_numeric(arg(1)) && $node = node_load(array("nid" => arg(1)))) {
-    header("Content-Type: text/xml");
-    print trackback_receive($node);
+function _trackback_info($node) {
+  $output = t("<p>The Trackback URL for node is:<br/><blockquote>url</blockquote></p>", array("node" => l("'$node->title'","node/view/$node->nid"), "url" => l(url("trackback/$node->nid", NULL, NULL, TRUE), "trackback/$node->nid")));
+
+  if (user_access("administer nodes")) {
+    $node = trackback_load_node($node);
+    $urls = preg_split("/\s+/",$node->pinged);
+    $head = 0;
+    foreach ($urls as $url) {
+      $url = strip_tags($url);
+      if (strlen(trim($url))<=0) continue;
+      if ($head == 0) {
+        $output .=  "<p>".
+          t("Trackback pings have been sent to the following URLs:");
+        $head = 1;
+      }
+      $output .= "<li>$url</li>\n";
+    }
+    if ($head) $output .= "</p>";
   }
-  else {
-    drupal_goto();
+
+  $query = db_query("SELECT cid from {trackback_pingme} WHERE nid = %d", $node->nid);
+
+  if (db_num_rows($query) && module_exist('comment')) {
+    $output .= t("<p>Trackbacks:</p>");
+    // okay, stupid hack to make sure we dont display the comment
+    // a better solution is to have a way to pass this info to
+    // comment_render()
+    $comment_form_location = variable_get("comment_form_location",0);
+    variable_set("comment_form_location",0);
+
+    while ($result = db_fetch_object($query)) {
+      $output .= comment_render($node,$result->cid);
+    }
+
+    variable_set("comment_form_location",$comment_form_location);
   }
+
+  return $output;
+}
+
+function trackback_page() {
+    if (arg(1) == "url" && is_numeric(arg(2)) 
+        && $node = node_load(array("nid" => arg(2)))) {
+        $output = _trackback_info($node);
+        print theme('page', $output, t("Trackback URL"));
+    } elseif (is_numeric(arg(1)) && $node = node_load(array("nid" => arg(1)))) {
+        header("Content-Type: text/xml");
+        print trackback_receive($node);
+    } else {
+        drupal_goto();
+    }
 }
 
 function trackback_nodeapi(&$node, $op, $arg = 0) {
+  $node = trackback_load_node($node);
   switch ($op) {
     case "form post":
-      return form_textfield(t("Trackback URL"), "tb_url", $node->tb_url, 40, 255, t("Enter a URL to send the trackback to."));
+      $node->tb_url = $node->toping;
+      return form_textarea(t("Trackback URL"), "tb_url", $node->tb_url, 70, 3, t("Enter URLs to send the trackback to."));
     case "insert":
     case "update":
-      if ($node->tb_url && $node->nid) {
+      # send trackback only for published post
+      if ($node->nid && $node->status == 1
+        && ($node->tb_url || variable_get("trackback_autodiscovery",1)))
         trackback_send($node);
-      }
   }
 }
 
+function trackback_insert($node) {
+  db_query("INSERT INTO {trackback} (pinged, toping, nid) VALUES ('%s', '%s', %d)",
+    $node->pinged, $node->toping, $node->nid);
+}
+
+function trackback_update($node) {
+  db_query("UPDATE {trackback} SET pinged = '%s', toping = '%s' WHERE nid = %d",
+    $node->pinged, $node->toping, $node->nid);
+}
+
+function trackback_delete(&$node) {
+  db_query("DELETE FROM {trackback} WHERE nid = %d", $node->nid);
+}
+
+function trackback_load($node) {
+  return db_fetch_object(db_query("SELECT pinged, toping FROM {trackback} WHERE nid = %d", $node->nid));
+}
+
+function trackback_load_node($node) {
+  if ($result = trackback_load($node)) {
+    foreach ($result as $key => $value)
+      $node->$key = $value;
+  }
+  return $node;
+}
+
 function trackback_link($type, $node = 0, $main = 0) {
   if ($type == 'system') {
     menu('trackback', t('trackback'), trackback_page, 0, MENU_HIDE);
   }
   elseif ($type == "node") {
-    $link = l(t("trackback url"), "trackback/$node->nid");
+    $link = l(t("trackback url"), "trackback/url/$node->nid", array("title" => t("Display Trackback URL")));
     if (!$main) {
-      $url = url("node/view/$node->nid");
-      $tb_url = url("trackback/$node->nid");
+      $url = url("node/view/$node->nid", NULL, NULL, 1);
+      $tb_url =  url("trackback/$node->nid", NULL, NULL, 1);
       $link .= "\n<!--\n";
       $link .= "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\">\n";
       $link .= "<rdf:Description rdf:about=\"$url\" dc:identifier=\"$url\" dc:title=\"$node->title\" trackback:ping=\"$tb_url\" />\n";
@@ -221,4 +370,17 @@
   }
   return $result;
 }
-?>
+
+function trackback_blogadmin($type = "") {
+  $list = array();
+
+  if ($type == "options") {
+    if (user_access("administer site configuration")) {
+      $list[l(t("Trackback options"),"admin/system/modules/trackback")] = t("Configure trackback options.");
+    }
+  }
+
+  return $list;
+}
+
+?>
diff -urN drupal-4.4.1/modules/upload.module drupal4blog/modules/upload.module
--- drupal-4.4.1/modules/upload.module	Thu Jan  1 07:30:00 1970
+++ drupal4blog/modules/upload.module	Tue Jun  8 14:19:14 2004
@@ -0,0 +1,326 @@
+<?php
+// $Id$
+
+function upload_help($section) {
+  $output = "";
+
+  switch ($section) {
+    case 'admin/system/modules#description':
+      $output = t("Enables quick and easy file upload to drupal.");
+      break;
+    case 'admin/upload/help':
+    case 'admin/system/modules/upload':
+      $output = t("Allows quick and easy file upload to drupal. You can define permissions to upload and download file in the %userperm configuration. The permission 'download files' only works if 'Download Method' is set to Private in %sysconf. You also need to make sure your File System path is not accessible from the web.",
+        array("%userperm" => l(t("user permission"),"admin/user/permission"),
+              "%sysconf"  => l(t("system configuration"), "admin/system")));
+      break;
+  }
+  return $output;
+}
+
+function upload_perm() {
+  return array("upload files (simple)", 
+               "maintain files (simple)", 
+               "download files (simple)");
+}
+
+function upload_settings() {
+  if (!file_check_directory(file_create_path(variable_get('upload_path', 'public')))) 
+    $error['upload_path'] = theme('error', t('Directory does not exists, or is not writable.'));
+
+  $output = "";
+
+  $output .= form_textfield(t("Upload path"), "upload_path", variable_get("upload_path", "public"), 30, 255, t("Subdirectory in the directory '%dir' where upload files will be stored.", array('%dir' => variable_get("file_directory_path", "files") . FILE_SEPARATOR)) . $error['upload_path']);
+
+  $output .= form_textfield(t("Maximum file size"), "upload_filesize", variable_get("upload_filesize", 1024), 10, 10, t("Maximum file size for file upload, in kB. Default 1024kb or 1Mb."));
+
+  return $output;
+}
+
+function upload_link($type, $node = 0, $main) {
+  if ($type  == 'system') {
+    if (user_access('upload files (simple)'))
+      menu('upload', t('quick upload'), 'upload_page', 0, MENU_HIDE);
+    if (user_access('maintain files (simple)'))
+      menu('admin/upload', t('manage files'), 'upload_admin', 0);
+  }
+}
+
+function upload_page() {
+  $op = $_POST['op'] ? $_POST['op'] : arg(1);
+
+  if (!user_access("upload files (simple)"))
+    drupal_goto();
+
+  switch ($op) {
+    case 'Replace':
+      print theme('popup', _upload_upload(1), '');
+      break;
+    case 'Upload':
+      print theme('popup', _upload_upload(0), '');
+      break;
+    default:
+      print theme('popup', _upload_form(), '');
+  }
+}
+
+function _upload_form() {
+  drupal_set_title(t('Quick upload file'));
+  $output .= "<div class=\"standard\">";
+  $output .= form_file(t("Upload file"), "upload_file", 30, 
+              "You need to specify a file to upload.");
+  $output .= form_submit(t('Upload'));
+  $output .= form_submit(t('Replace'));
+  $output .= "<p>".t("'Upload' will not replace existing file. Use 'Replace' if you wish to override existing file.")."</p>";
+  $output .= "</div>";
+
+  # construct the form
+  $param["method"] = "post";
+  $param["options"]["enctype"] = "multipart/form-data";
+  return form($output, ($param['method'] ? $param['method'] : 'post'), $param['action'], $param['options']);
+}
+
+function _upload_upload($replace = 0) {
+  $edit = $_POST['edit'];
+  $upload_file = file_check_upload('upload_file');
+
+  if ((!$upload_file) || !file_exists($upload_file->path)) {
+    drupal_set_message(theme("error",t("You need to specify a file to upload.")));
+    return _upload_form();
+  }
+
+  if (filesize($upload_file->path)/1024 > variable_get("upload_filesize",1024)) {
+    drupal_set_message(theme('error',t('Maximum upload size is ').variable_get("upload_filesize", 1024)."kb.".filesize($upload_file->path)));
+    return _upload_form();
+  }
+
+  $upload_file->name = preg_replace("/\s/","_",$upload_file->name);
+  $upload_file->name = preg_replace("/[^A-Za-z0-9\-\_]/",".",$upload_file->name);
+  $upload_file->name = preg_replace("/^[\.\/]+/",".",$upload_file->name);
+  $dest = variable_get("upload_path","public").FILE_SEPARATOR.$upload_file->name;
+
+  if (!$replace && file_exists(file_create_path($dest))) {
+    drupal_set_message("File already existed!");
+    return _upload_form();
+  } 
+
+  file_save_upload('upload_file',$dest,1);
+
+  $url = file_create_url($dest);
+  if (preg_match("/\.(gif|jpg|png)$/i",$upload_file->name)) {
+    $size = getimagesize(file_create_path($dest));
+    $html = "<img src=\"$url\" alt=\"$upload_file->name\" border=\"0\"";
+    if ($size[0] && $size[1])
+     $html .= " width=\"$size[0]\" height=\"$size[1]\"";
+    $html .= "/>";
+  } else {
+    $html = "<a href=\"$url\">Download file</a>";
+  }
+
+  $output = "";
+  $output .= form_textarea(t("HTML code"),"html",$html,35,3, t("This is the HTML code which you can paste into your entry."));
+  $output .= form_button(t('Close'), "op", "button",
+    array("onClick" => "window.close()"));
+
+  watchdog("special", "upload file ".$upload_file->name, l("view file",$url));
+
+  return form($output);
+}
+
+function upload_admin() {
+  $op = $_POST["op"];
+  $edit = $_POST["edit"];
+
+  if (!user_access("maintain files (simple)"))
+    drupal_goto();
+
+  if (arg(2)) {
+    $output = _upload_admin_file(escapeshellcmd(arg(2)));
+  } else {
+    $output = _upload_admin_overview();
+  }
+
+  print theme("page",$output);
+}
+
+function _upload_filerow($dir,$file) {
+  $ffile = $dir.FILE_SEPARATOR.$file;
+  $info = stat($ffile);
+  if ($info['size'] > 1024*1024) 
+    $size = (int) ($info['size']/(1024*1024))."Mb";
+  else if ($info['size'] > 1024)
+    $size = (int) ($info['size']/1024)."kb";
+  else
+    $size = $info['size']."b";
+  return array(
+    array("data" => l($file,"admin/upload/$file") ),
+    array("data" => $size ),
+    array("data" => format_date($info['mtime']) ),
+  );
+}
+
+function _upload_admin_overview() {
+  $dir = file_create_path(variable_get('upload_path', 'public'));
+  if (!file_check_directory($dir)) {
+    drupal_set_message(t("The upload directory isn't configured properly."));
+    return drupal_goto("admin/system/modules/upload");
+  }
+
+  $output = "";
+  if ($dh = opendir($dir)) {
+    $headers = array( 
+      array("data" => t("filename") ),
+      array("data" => t("size")  ),
+      array("data" => t("date") ),
+    );
+
+    while ($file = readdir($dh)) {
+      if (is_dir($dir.FILE_SEPARATOR.$file)) continue;
+      $rows[] = _upload_filerow($dir,$file);
+    }
+    closedir($dh);
+
+    $url = url("upload");
+    $rows[] = array(array("data" => "<a href=\"javascript:void(window.open('$url','popup','width=350,height=250,resizable=yes'))\">Upload files</a>", "colspan" => 3));
+
+    $output .= theme("table",$headers, $rows);
+  }
+  return $output;
+}
+
+function _upload_admin_file($file) {
+  $op = $_POST['op'];
+  $edit = $_POST['edit'];
+
+  $dir = file_create_path(variable_get('upload_path', 'public'));
+  if (!file_exists($dir.FILE_SEPARATOR.$file)) {
+    drupal_set_message(t("No such file!"));
+    return drupal_goto("admin/upload");
+  }
+
+  switch ($op) {
+    case t('Delete'):
+      $ffile = $dir.FILE_SEPARATOR.$file;
+      @unlink($ffile);
+      watchdog("special", "delete file ".$file, "deleted");
+      drupal_goto("admin/upload");
+      break;
+
+    case t('Rename'):
+      $edit['rename'] = escapeshellcmd($edit['rename']);
+      $edit['rename'] = preg_replace("/\s/","_",$edit['rename']);
+      $edit['rename'] = preg_replace("/[^A-Za-z0-9\-\_]/",".",$edit['rename']);
+      $edit['rename'] = preg_replace("/^[\.\/]+/","",$edit['rename']);
+
+      $ffile = $dir.FILE_SEPARATOR.$file;
+      $dest  = $dir.FILE_SEPARATOR.$edit['rename'];
+
+      if (file_exists($dest)) {
+        drupal_set_message(t("There is already another file by that name."));
+        return drupal_goto("admin/upload/$file");
+      }
+
+      if (copy($ffile,$dest)) {
+        @unlink($ffile);
+        $url = file_create_url(variable_get("upload_path","public").FILE_SEPARATOR.$edit['rename']);
+        watchdog("special", "rename file '$file' to '".$edit['rename']."'", l("view file",$url));
+        return drupal_goto("admin/upload/".$edit['rename']);
+      } else {
+        watchdog("error", "unable to rename file '$file' to '".$edit['rename']."'");
+        drupal_set_message(t("Error renaming file!"));
+        return drupal_goto("admin/upload/$file");
+      }
+      break;
+
+    default:
+      $headers = array( 
+        array("data" => t("filename") ),
+        array("data" => t("size")  ),
+        array("data" => t("date") ),
+      );
+    
+      $rows[] = _upload_filerow($dir,$file);
+      $output = theme("table", $headers, $rows);
+    
+      $form .= form_textfield(t("Rename"), "rename", $file, 30, 128);
+      $form .= form_submit(t("Rename"));
+      $form .= form_submit(t("Delete"));
+      $output .= form($form);
+      break;
+  }
+
+  return $output;
+}
+
+function upload_file_download($file) {
+  if (user_access("download files (simple)")) {
+    $file = file_create_path($file);
+    return array('Content-type: '.mime_content_type($file));
+  }
+}
+
+/*
+** goes into theme.inc?
+**/
+function theme_popup($content, $title = NULL, $breadcrumb = NULL) {
+  if (isset($title)) {
+    drupal_set_title($title);
+  }
+  if (isset($breadcrumb)) {
+    drupal_set_breadcrumb($breadcrumb);
+  }
+
+  $output = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
+  $output .= "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
+  $output .= "<head>";
+  $output .= " <title>". (drupal_get_title() ? drupal_get_title() : variable_get('site_name', "drupal")) ."</title>";
+  $output .= drupal_get_html_head();
+
+  $output .= " </head>";
+  $output .= " <body style=\"background-color: #fff; color: #000;\"". theme("onload_attribute"). "\">";
+  $output .= "<table border=\"0\" cellspacing=\"4\" cellpadding=\"4\"><tr>";
+  $output .= "<td style=\"vertical-align: top;\">";
+
+  $output .= "<h1>" . drupal_get_title() . "</h1>";
+  if ($help = menu_get_active_help()) {
+    $output .= "<small>$help</small><hr />";
+  }
+
+  $output .= "\n<!-- begin content -->\n";
+  $output .= $content;
+  $output .= "\n<!-- end content -->\n";
+
+  $output .= "</td></tr></table>";
+  $output .= theme_closure();
+  $output .= "</body></html>";
+
+  return $output;
+}
+
+function upload_blogadmin($type) {
+  $list = array();
+  
+  if ($type == "block") {
+    if (user_access('upload files (simple)')) {
+      $url = url("upload");
+      $list[] = "<a href=\"javascript:void(window.open('$url','popup','width=350,height=250,resizable=yes'))\">quick upload</a>";
+    }
+
+    if (user_access('maintain files (simple)'))
+      $list[] = l(t("file manager"), "admin/upload");
+  } else if ($type == "options") {
+      $list[l(t("Upload options"),"admin/system/modules/upload")] = t("Configure upload options.");
+  }
+
+  return $list;
+}
+
+if (!function_exists ("mime_content_type")) {
+  function mime_content_type ($file) {
+    $mime = exec ("file -bi " . escapeshellcmd($file));
+    if ($mime) return $mime;
+    return "application/octet-stream";
+  }
+}
+
+?>
