diff --git a/INSTALL b/INSTALL index 45664c8..f006063 100644 --- a/INSTALL +++ b/INSTALL @@ -5,8 +5,8 @@ Software requirements: You must install the following software on your system: * Java 2 SDK version 1.2.2. Venice is normally run with the Blackdown - JDK port, available at http://www.blackdown.org. Version 1.3.x has - not been tested. + JDK port, available at http://www.blackdown.org. Version 1.3.0 is + being tested, and will likely be required sometime in the future. * Java API for XML Parsing (JAXP) version 1.0.1, available from http://java.sun.com. * Java Secure Socket Extension (JSSE) version 1.0.2, available from @@ -15,7 +15,8 @@ Software requirements: * JavaMail API version 1.2, available from http://java.sun.com. * JavaBeans Activation Framework (JAF) version 1.0.1, available from http://java.sun.com. (Required by JavaMail.) - * Apache Jakarta LOG4J library, available from http://www.log4j.org. + * Apache Jakarta LOG4J library 1.0.x, available from + http://jakarta.apache.org. * MySQL 3.23.x (get the most recent 3.23 version), available from http://www.mysql.com. * The MM.MySQL JDBC drivers version 2.0.3, available from XXXX. diff --git a/TODO b/TODO index 83df3c7..131437a 100644 --- a/TODO +++ b/TODO @@ -2,11 +2,13 @@ Lots! - Unimplemented functions on the SIG Administration page: Set SIG Features (sigadmin, command=F) + View Audit Records (needs to be added) - Unimplemented functions in the system admin menu: Set Global Parameters View/Edit Banned Users User Account Management + View Audit Records (needs to be added) (More stuff needs to be added, I'm just not sure what.) - Should we provide the sysadmin the ability to disable SIG creation for diff --git a/etc/render-config.xml b/etc/render-config.xml index 0c0689b..24a4642 100644 --- a/etc/render-config.xml +++ b/etc/render-config.xml @@ -37,33 +37,53 @@ - - http://delenn:8080/venice/ - - http://delenn:8080/venice/images/ + /venice/images/ - http://delenn:8080/venice/static/ + /venice/static/ - - http://delenn:8080/venice/images/powered-by-venice.gif + + /venice/images/powered-by-venice.gif - + -Welcome to the Venice Web Communities System. To get the most out of this site, you should log in or create -an account, using one of the links above. + Venice Web Communities System. To get the most out of this site, you should log in +or create an account, using one of the links above. + ]]> - + Welcome to Venice - + Venice Currents + + + +All messages posted by users on this page are owned by those users.
+The rest: Copyright © 2001 Silverwrist Design Studios, +All Rights Reserved.
+See our Policy Page for our copyright and privacy policies. + ]]> +
+ + + + + + + + Venice User Agreement +
diff --git a/etc/venice-config.xml b/etc/venice-config.xml index 7a41ec9..89a327d 100644 --- a/etc/venice-config.xml +++ b/etc/venice-config.xml @@ -77,12 +77,15 @@ -Venice - community services, conferencing and more. http://venice.sourceforge.net + + ]]> + + + and click the "Create Account" link at the top of the page, or click the "Log In" link if you already have a Venice account. Once you have completed the process, click the "Join Now" button. You will then be able to take part in the conferences that are going on in the SIG. @@ -124,6 +131,7 @@ $PERSONAL Hope to see you in "$SIGNAME" soon! -- $FULLNAME (Venice user ID: $USERNAME) + ]]> @@ -131,9 +139,10 @@ Hope to see you in "$SIGNAME" soon! $PERSONAL = personal message, $FULLNAME = name of inviter, $USERNAME = user name of inviter --> + and click the "Create Account" link at the top of the page, or click the "Log In" link if you already have a Venice account. Once you have completed the process, click the "Join Now" button. You will be prompted for the "password" for this SIG, which is "$JOINKEY". You will then be able to take part in the conferences that are going on in the SIG. @@ -143,6 +152,7 @@ $PERSONAL Hope to see you in "$SIGNAME" soon! -- $FULLNAME (Venice user ID: $USERNAME) + ]]> diff --git a/etc/web.xml b/etc/web.xml index 5084b58..0270d9f 100644 --- a/etc/web.xml +++ b/etc/web.xml @@ -205,6 +205,11 @@ com.silverwrist.util.test.FormDataTest + + dumpall + com.silverwrist.util.test.DumpAll + + top @@ -297,10 +302,19 @@ /testformdata + + dumpall + /dumpall + + + + dumpall + /dump/* + + 60 - diff --git a/setup/database.sql b/setup/database.sql index 3fbbb26..4750e40 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -39,7 +39,8 @@ CREATE TABLE globals ( max_search_page INT NOT NULL, max_sig_mbr_page INT NOT NULL, max_conf_mbr_page INT NOT NULL, - fp_posts INT NOT NULL + fp_posts INT NOT NULL, + num_audit_page INT NOT NULL ); # The audit records table. Most "major" events add a record to this table. @@ -1324,8 +1325,8 @@ INSERT INTO refsigftr (ftr_code, is_default, is_locked, is_hidden, require_read, # Initialize the system globals table. INSERT INTO globals (posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page, - fp_posts) - VALUES (20, 2, 20, 50, 50, 10); + fp_posts, num_audit_page) + VALUES (20, 2, 20, 50, 50, 10, 100); # Add the 'Anonymous Honyak' user to the users table. # (Do 'SELECT * FROM users WHERE is_anon = 1' to retrieve the AC user details.) diff --git a/src/com/silverwrist/util/test/DumpAll.java b/src/com/silverwrist/util/test/DumpAll.java new file mode 100644 index 0000000..f9895ff --- /dev/null +++ b/src/com/silverwrist/util/test/DumpAll.java @@ -0,0 +1,33 @@ +package com.silverwrist.util.test; + +import java.io.*; +import javax.servlet.*; +import javax.servlet.http.*; + +public class DumpAll extends HttpServlet +{ + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + PrintWriter out = response.getWriter(); + out.println("DumpAll
");
+    out.println("Auth type: " + request.getAuthType());
+    out.println("Content length: " + request.getContentLength());
+    out.println("Content type: " + request.getContentType());
+    out.println("Path info: " + request.getPathInfo());
+    out.println("Query string: " + request.getQueryString());
+    out.println("Remote address: " + request.getRemoteAddr());
+    out.println("Remote host: " + request.getRemoteHost());
+    out.println("Remote user: " + request.getRemoteUser());
+    out.println("Request method: " + request.getMethod());
+    out.println("Servlet path: " + request.getServletPath());
+    out.println("Server name: " + request.getServerName());
+    out.println("Server port: " + request.getServerPort());
+    out.println("Server protocol: " + request.getProtocol());
+    out.println("Scheme: " + request.getScheme());
+    out.println("Context path: " + request.getContextPath());
+    out.println("Request URI: " + request.getRequestURI());
+    out.println("
"); + + } +} diff --git a/src/com/silverwrist/venice/core/AdminOperations.java b/src/com/silverwrist/venice/core/AdminOperations.java index 7f6ff53..5b57d3d 100644 --- a/src/com/silverwrist/venice/core/AdminOperations.java +++ b/src/com/silverwrist/venice/core/AdminOperations.java @@ -17,8 +17,12 @@ */ package com.silverwrist.venice.core; +import java.util.List; + public interface AdminOperations { - // TODO: fill this in + public abstract List getAuditRecords(int offset, int count) throws DataException; + + public abstract int getAuditRecordCount() throws DataException; } // end interface AdminOperations diff --git a/src/com/silverwrist/venice/core/AuditData.java b/src/com/silverwrist/venice/core/AuditData.java new file mode 100644 index 0000000..e983fb9 --- /dev/null +++ b/src/com/silverwrist/venice/core/AuditData.java @@ -0,0 +1,47 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core; + +import java.util.Date; +import com.silverwrist.venice.security.Audit; + +public interface AuditData extends Audit +{ + public static final int DATA_COUNT = 4; + + public abstract long getRecord(); + + public abstract Date getDateTime(); + + public abstract int getType(); + + public abstract int getUID(); + + public abstract int getSIGID(); + + public abstract String getIPAddress(); + + public abstract String getData(int ndx); + + public abstract String getDescription(); + + public abstract String getUserName(); + + public abstract String getSIGName(); + +} // end interface AuditData diff --git a/src/com/silverwrist/venice/core/SIGContext.java b/src/com/silverwrist/venice/core/SIGContext.java index 7687a4a..92b468d 100644 --- a/src/com/silverwrist/venice/core/SIGContext.java +++ b/src/com/silverwrist/venice/core/SIGContext.java @@ -167,4 +167,8 @@ public interface SIGContext extends SearchMode public abstract boolean canSendInvitation(); + public abstract List getAuditRecords(int offset, int count) throws AccessError, DataException; + + public abstract int getAuditRecordCount() throws AccessError, DataException; + } // end interface SIGContext diff --git a/src/com/silverwrist/venice/core/VeniceEngine.java b/src/com/silverwrist/venice/core/VeniceEngine.java index e6fe9ed..38ac41a 100644 --- a/src/com/silverwrist/venice/core/VeniceEngine.java +++ b/src/com/silverwrist/venice/core/VeniceEngine.java @@ -75,4 +75,6 @@ public interface VeniceEngine extends SearchMode public abstract List getPublishedMessages(boolean all) throws DataException; + public abstract int getNumAuditRecordsPerPage(); + } // end interface VeniceEngine diff --git a/src/com/silverwrist/venice/core/impl/AdminOperationsImpl.java b/src/com/silverwrist/venice/core/impl/AdminOperationsImpl.java index 991e031..49756c2 100644 --- a/src/com/silverwrist/venice/core/impl/AdminOperationsImpl.java +++ b/src/com/silverwrist/venice/core/impl/AdminOperationsImpl.java @@ -18,9 +18,11 @@ package com.silverwrist.venice.core.impl; import java.sql.*; +import java.util.*; import org.apache.log4j.*; import com.silverwrist.venice.core.*; import com.silverwrist.venice.db.*; +import com.silverwrist.venice.security.AuditRecord; class AdminOperationsImpl implements AdminOperations { @@ -58,4 +60,60 @@ class AdminOperationsImpl implements AdminOperations *-------------------------------------------------------------------------------- */ + public List getAuditRecords(int offset, int count) throws DataException + { + Connection conn = null; + List rc = null; + + try + { // retrieve a connection from the data pool and get the audit records + conn = datapool.getConnection(); + rc = AuditRecord.getAuditRecords(conn,-1,offset,count); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("error loading audit records: " + e.getMessage(),e); + throw new DataException("unable to load audit records: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return rc; + + } // end getAuditRecords + + public int getAuditRecordCount() throws DataException + { + Connection conn = null; + int rc = -1; + + try + { // retrieve a connection from the data pool and get the audit records + conn = datapool.getConnection(); + rc = AuditRecord.getAuditRecordCount(conn,-1); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("error loading audit record count: " + e.getMessage(),e); + throw new DataException("unable to load audit record count: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return rc; + + } // end getAuditRecordCount + } // end class AdminOperationsImpl diff --git a/src/com/silverwrist/venice/core/impl/EngineBackend.java b/src/com/silverwrist/venice/core/impl/EngineBackend.java index 76033f7..8b8b23a 100644 --- a/src/com/silverwrist/venice/core/impl/EngineBackend.java +++ b/src/com/silverwrist/venice/core/impl/EngineBackend.java @@ -37,6 +37,7 @@ public interface EngineBackend public static final int IP_MAXSIGMEMBERDISPLAY = 3; public static final int IP_MAXCONFMEMBERDISPLAY = 4; public static final int IP_NUMFRONTPAGEPOSTS = 5; + public static final int IP_NUMAUDITRECSPERPAGE = 6; public abstract SimpleEmailer createEmailer(); diff --git a/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java b/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java index 947a34c..ab3f488 100644 --- a/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java @@ -22,6 +22,7 @@ import java.util.*; import org.apache.log4j.*; import com.silverwrist.util.StringUtil; import com.silverwrist.venice.db.*; +import com.silverwrist.venice.security.AuditRecord; import com.silverwrist.venice.security.Capability; import com.silverwrist.venice.security.DefaultLevels; import com.silverwrist.venice.core.*; @@ -1271,6 +1272,78 @@ class SIGUserContextImpl implements SIGContext, SIGBackend } // end canSendInvitation + public List getAuditRecords(int offset, int count) throws AccessError, DataException + { + SIGData sd = getSIGData(); + if (!sd.canDeleteSIG(level) && !sd.canModifySIGProfile(level) && !sd.canCreateSIGSubObjects(level)) + { // user not permitted to retrieve audit records - naughty naughty + logger.error("user not permitted to retrieve audit records for SIG"); + throw new AccessError("You are not permitted to retrieve audit records for this SIG."); + + } // end if + + Connection conn = null; + List rc = null; + + try + { // retrieve a connection from the data pool and get the audit records + conn = datapool.getConnection(); + rc = AuditRecord.getAuditRecords(conn,sigid,offset,count); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("error loading audit records: " + e.getMessage(),e); + throw new DataException("unable to load audit records: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return rc; + + } // end getAuditRecords + + public int getAuditRecordCount() throws AccessError, DataException + { + SIGData sd = getSIGData(); + if (!sd.canDeleteSIG(level) && !sd.canModifySIGProfile(level) && !sd.canCreateSIGSubObjects(level)) + { // user not permitted to retrieve audit records - naughty naughty + logger.error("user not permitted to retrieve audit records for SIG"); + throw new AccessError("You are not permitted to retrieve audit records for this SIG."); + + } // end if + + Connection conn = null; + int rc = -1; + + try + { // retrieve a connection from the data pool and get the audit records + conn = datapool.getConnection(); + rc = AuditRecord.getAuditRecordCount(conn,sigid); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("error loading audit record count: " + e.getMessage(),e); + throw new DataException("unable to load audit record count: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return rc; + + } // end getAuditRecordCount + /*-------------------------------------------------------------------------------- * Implementations from interface UserBackend *-------------------------------------------------------------------------------- diff --git a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java index 615a2ae..e2ae127 100644 --- a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java +++ b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java @@ -336,7 +336,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend { final String query = "SELECT posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page, " - + "fp_posts FROM globals;"; + + "fp_posts, num_audit_page FROM globals;"; ResultSet rs = stmt.executeQuery(query); if (!(rs.next())) throw new DataException("Globals table does not appear to be loaded!"); @@ -348,6 +348,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend gp_ints[IP_MAXSIGMEMBERDISPLAY] = rs.getInt(4); gp_ints[IP_MAXCONFMEMBERDISPLAY] = rs.getInt(5); gp_ints[IP_NUMFRONTPAGEPOSTS] = rs.getInt(6); + gp_ints[IP_NUMAUDITRECSPERPAGE] = rs.getInt(7); } // end loadDefaults @@ -492,7 +493,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end catch // Allocate the global parameter arrays. - gp_ints = new int[6]; + gp_ints = new int[7]; // initialize anything that requires us to pull from the database Connection conn = null; @@ -1436,6 +1437,12 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end getPublishedMessages + public int getNumAuditRecordsPerPage() + { + return gp_ints[IP_NUMAUDITRECSPERPAGE]; + + } // end getNumAuditRecordsPerPage + /*-------------------------------------------------------------------------------- * Implementations from interface EngineBackend *-------------------------------------------------------------------------------- diff --git a/src/com/silverwrist/venice/htmlcheck/dict/TernaryLexicon.java b/src/com/silverwrist/venice/htmlcheck/dict/TernaryLexicon.java new file mode 100644 index 0000000..8d6aba5 --- /dev/null +++ b/src/com/silverwrist/venice/htmlcheck/dict/TernaryLexicon.java @@ -0,0 +1,366 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Community System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.htmlcheck.dict; + +import com.silverwrist.venice.htmlcheck.ModSpellingDictionary; + +public class TernaryLexicon implements ModSpellingDictionary +{ + /*-------------------------------------------------------------------------------- + * Inner class representing the nodes in the ternary search tree + *-------------------------------------------------------------------------------- + */ + + protected class TLNode + { + protected static final int PARENT = 0; + protected static final int LT_CHILD = 1; + protected static final int EQ_CHILD = 2; + protected static final int GT_CHILD = 3; + + protected char splitchar; + protected TLNode[] relatives = new TLNode[4]; + protected boolean term = false; + + protected TLNode(char splitchar, TLNode parent) + { + this.splitchar = splitchar; + relatives[PARENT] = parent; + + } // end constructor + + } // end class TLNode + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private TLNode root_node; + private int num_entries; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public TernaryLexicon() + { + root_node = null; + num_entries = 0; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + + private TLNode getNode(String key, TLNode start) + { + if ((key==null) || (start==null) || (key.length()==0)) + return null; // no node can be found + + TLNode current = start; + int ndx = 0; + char c = key.charAt(0); + for (;;) + { // look for + if (current==null) + break; // fell off the tree... + + if (c==current.splitchar) + { // found an equal character - may be at end of string + if (++ndx==key.length()) + return current; // found the node! + current = current.relatives[TLNode.EQ_CHILD]; + c = key.charAt(ndx); + + } // end if + else if (cdelta_lo) + { // move the low child into the slot + ndx_move = TLNode.GT_CHILD; + target = node.relatives[TLNode.LT_CHILD]; + + } // end if + else + { // move the high child into the slot + ndx_move = TLNode.LT_CHILD; + target = node.relatives[TLNode.GT_CHILD]; + + } // end else + + // find the edge of the subtree that doesn't get moved, and put the other subtree there + while (target.relatives[ndx_move]!=null) + target = target.relatives[ndx_move]; + target.relatives[ndx_move] = node.relatives[ndx_move]; + node.relatives[ndx_move].relatives[TLNode.PARENT] = target; + + // put the target node where the current one used to be + parent.relatives[childtype] = target; + target.relatives[TLNode.PARENT] = parent; + + // clean up the current node + node.relatives[TLNode.LT_CHILD] = node.relatives[TLNode.GT_CHILD] = null; + + return parent; + + } // end deleteNodeRecursion + + private void deleteNode(TLNode node) + { + if (node==null) + return; + node.term = false; + while (node!=null) + node = deleteNodeRecursion(node); + + } // end deleteNode + + private boolean getEntry(String word) + { + TLNode node = getNode(word,root_node); + if (node!=null) + return node.term; + else + return false; + + } // end getEntry + + private boolean checkHyphenates(String word) + { + if (word.indexOf('-')<0) + return false; // non-hyphenated + + int st = 0; + int p; + boolean ok = true; + do + { // break the word up into hyphenated compounds + p = word.indexOf('-',st); + String frag; + if (p>=0) + { // get the middle fragment of the word + frag = word.substring(st,p); + st = p + 1; + + } // end if + else // get it from the end + frag = word.substring(st); + + // check this fragment... + if (frag.length()<=1) + ok = true; // fragments of length 0 or 1 are always OK + else // anything else goes through getEntry + ok = getEntry(frag); + + } while (ok && (p>=0)); + + return ok; + + } // end checkHyphenates + + /*-------------------------------------------------------------------------------- + * Implementations from interface SpellingDictionary + *-------------------------------------------------------------------------------- + */ + + public synchronized int size() + { + return num_entries; + + } // end size + + public synchronized boolean checkWord(String word) + { + if (word.length()<=1) + return true; // words of length 1 get a free pass + String real_word = word.toLowerCase(); + if (getEntry(real_word)) + return true; // word is in lexicon - we're OK to go + + if ((real_word.indexOf('\'')==(real_word.length()-2)) && (real_word.charAt(real_word.length()-1)=='s')) + { // drop the apostrophe-s from the end of the word and retry + String base = real_word.substring(0,real_word.length()-2); + if (getEntry(base)) + return true; + return checkHyphenates(base); + + } // end if + else // try hyphenated forms + return checkHyphenates(real_word); + + } // end checkWord + + /*-------------------------------------------------------------------------------- + * Implementations from interface ModSpellingDictionary + *-------------------------------------------------------------------------------- + */ + + public synchronized void addWord(String word) + { + TLNode node = getOrCreateNode(word.toLowerCase()); + node.term = true; + num_entries++; + + } // end addWord + + public synchronized void delWord(String word) + { + TLNode node = getNode(word.toLowerCase(),root_node); + if (node!=null) + { // delete the node and any unnecessary subnodes + deleteNode(node); + num_entries--; + + } // end if + + } // end delWord + + public synchronized void clear() + { + root_node = null; + num_entries = 0; + + } // end clear + +} // end class TernaryLexicon diff --git a/src/com/silverwrist/venice/security/AuditRecord.java b/src/com/silverwrist/venice/security/AuditRecord.java index aedd33b..47f59b7 100644 --- a/src/com/silverwrist/venice/security/AuditRecord.java +++ b/src/com/silverwrist/venice/security/AuditRecord.java @@ -7,7 +7,7 @@ * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * - * The Original Code is the Venice Web Community System. + * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox , * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are @@ -20,10 +20,125 @@ package com.silverwrist.venice.security; import java.sql.*; import java.util.*; import com.silverwrist.venice.db.SQLUtil; +import com.silverwrist.venice.core.AuditData; +import com.silverwrist.venice.core.DataException; import com.silverwrist.venice.core.InternalStateError; -public class AuditRecord implements Audit +public class AuditRecord implements AuditData { + /*-------------------------------------------------------------------------------- + * Private implementation of ReadOnlyVector + *-------------------------------------------------------------------------------- + */ + + static class ReadOnlyVector extends AbstractList + { + private Vector my_vec; // local vector + + ReadOnlyVector(Vector vec) + { + my_vec = vec; + my_vec.trimToSize(); + + } // end constructor + + protected void finalize() throws Throwable + { + my_vec = null; + super.finalize(); + + } // end finalize + + public Object get(int index) + { + return my_vec.elementAt(index); + + } // end get + + public int size() + { + return my_vec.size(); + + } // end size + + } // end class ReadOnlyVector + + /*-------------------------------------------------------------------------------- + * Internal class for caching description strings on load + *-------------------------------------------------------------------------------- + */ + + static class DescrStringCache + { + private Hashtable descr_cache = new Hashtable(); + private Hashtable uname_cache = new Hashtable(); + private Hashtable signame_cache = new Hashtable(); + private Statement stmt; + + DescrStringCache(Connection conn) throws SQLException + { + stmt = conn.createStatement(); + + } // end constructor + + String getDescription(int code) throws SQLException, DataException + { + Integer code_x = new Integer(code); + String rc = (String)(descr_cache.get(code_x)); + if (rc==null) + { // OK, get it from the database... + ResultSet rs = stmt.executeQuery("SELECT descr FROM refaudit WHERE type = " + code + ";"); + if (!(rs.next())) + throw new DataException("description string not found for code " + code); + rc = rs.getString(1); + descr_cache.put(code_x,rc); + + } // end if + + return rc; + + } // end getDescription + + String getUserName(int uid) throws SQLException, DataException + { + Integer uid_x = new Integer(uid); + String rc = (String)(uname_cache.get(uid_x)); + if (rc==null) + { // OK, get it from the database + ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE uid = " + uid + ";"); + if (!(rs.next())) + throw new DataException("user name not found for UID " + uid); + rc = rs.getString(1); + uname_cache.put(uid_x,rc); + + } // end if + + return rc; + + } // end getUserName + + String getSIGName(int sigid) throws SQLException, DataException + { + if (sigid<=0) + return ""; // no SIG + Integer sigid_x = new Integer(sigid); + String rc = (String)(signame_cache.get(sigid_x)); + if (rc==null) + { // OK, get it from the database + ResultSet rs = stmt.executeQuery("SELECT signame FROM sigs WHERE sigid = " + sigid + ";"); + if (!(rs.next())) + throw new DataException("SIG name not found for SIGID " + sigid); + rc = rs.getString(1); + signame_cache.put(sigid_x,rc); + + } // end if + + return rc; + + } // end getSIGName + + } // end class DescrStringCache + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- @@ -37,6 +152,8 @@ public class AuditRecord implements Audit private String ip; // the IP address of the user private String[] data; // the data values associated with the record private String descr = null; // audit record description + private String uname = null; // user name of user + private String signame = null; // name of SIG /*-------------------------------------------------------------------------------- * Constructors @@ -124,6 +241,25 @@ public class AuditRecord implements Audit } // end constructor + protected AuditRecord(ResultSet rs, DescrStringCache cache) throws SQLException, DataException + { + record = rs.getLong("record"); + when = SQLUtil.getFullDateTime(rs,"on_date"); + type = rs.getInt("event"); + uid = rs.getInt("uid"); + sigid = rs.getInt("sigid"); + ip = rs.getString("ip"); + data = new String[DATA_COUNT]; + data[0] = rs.getString("data1"); + data[1] = rs.getString("data2"); + data[2] = rs.getString("data3"); + data[3] = rs.getString("data4"); + descr = cache.getDescription(type); + uname = cache.getUserName(uid); + signame = cache.getSIGName(sigid); + + } // end constructor + /*-------------------------------------------------------------------------------- * Internal functions *-------------------------------------------------------------------------------- @@ -141,7 +277,7 @@ public class AuditRecord implements Audit private void setData(String data1, String data2, String data3, String data4) { - data = new String[4]; + data = new String[DATA_COUNT]; data[0] = data1; data[1] = data2; data[2] = data3; @@ -150,7 +286,7 @@ public class AuditRecord implements Audit } // end setData /*-------------------------------------------------------------------------------- - * External operations + * Implementations from interface AuditData *-------------------------------------------------------------------------------- */ @@ -202,6 +338,23 @@ public class AuditRecord implements Audit } // end getDescription + public String getUserName() + { + return uname; + + } // end getUserName + + public String getSIGName() + { + return signame; + + } // end getSIGName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + public void store(Connection conn) throws SQLException { if (record!=0) @@ -238,4 +391,47 @@ public class AuditRecord implements Audit } // end store + /*-------------------------------------------------------------------------------- + * External static operations + *-------------------------------------------------------------------------------- + */ + + public static List getAuditRecords(Connection conn, int sigid, int offset, int count) + throws SQLException, DataException + { + Vector rc = new Vector(); + DescrStringCache cache = new DescrStringCache(conn); + + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("SELECT * FROM audit"); + if (sigid>0) + sql.append(" WHERE sigid = ").append(sigid); + sql.append(" ORDER BY on_date DESC LIMIT ").append(offset).append(", ").append(count).append(';'); + ResultSet rs = stmt.executeQuery(sql.toString()); + + while (rs.next()) + { // load the results + AuditData dta = new AuditRecord(rs,cache); + rc.add(dta); + + } // end while + + return new ReadOnlyVector(rc); + + } // end getAuditRecords + + public static int getAuditRecordCount(Connection conn, int sigid) throws SQLException + { + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("SELECT COUNT(*) FROM audit"); + if (sigid>0) + sql.append(" WHERE sigid = ").append(sigid); + sql.append(';'); + ResultSet rs = stmt.executeQuery(sql.toString()); + if (!(rs.next())) + throw new InternalStateError("query failure on getAuditRecordCount"); + return rs.getInt(1); + + } // end getAuditRecordCount + } // end class AuditRecord diff --git a/src/com/silverwrist/venice/servlets/Account.java b/src/com/silverwrist/venice/servlets/Account.java index 811ccdb..108f49a 100644 --- a/src/com/silverwrist/venice/servlets/Account.java +++ b/src/com/silverwrist/venice/servlets/Account.java @@ -176,12 +176,26 @@ public class Account extends VeniceServlet return new ErrorBox("Error","You cannot create a new account while logged in on an existing " + "one. You must log out first.",tgt); - // display the "Create Account" dialog - NewAccountDialog dlg = makeNewAccountDialog(); - dlg.setEngine(engine); - dlg.setTarget(tgt); - dlg.setFieldValue("country","US"); - return dlg; + final String yes = "yes"; + if (yes.equals(request.getParameter("agree"))) + { // display the "Create Account" dialog + NewAccountDialog dlg = makeNewAccountDialog(); + dlg.setEngine(engine); + dlg.setTarget(tgt); + dlg.setFieldValue("country","US"); + return dlg; + + } // end if + else + { // display the account terms dialog + String[] choices = new String[2]; + choices[0] = "account?cmd=C&agree=yes&tgt=" + URLEncoder.encode(tgt); + choices[1] = "top"; + return new TextMessageDialog(TextMessageDialog.TYPE_ACCEPT_DECLINE, + rdat.getStockMessage("user-agreement-title"), + rdat.getStockMessage("user-agreement"),choices); + + } // end else } // end if ("C" command) diff --git a/src/com/silverwrist/venice/servlets/SIGAdmin.java b/src/com/silverwrist/venice/servlets/SIGAdmin.java index 1ec34e5..919bf1e 100644 --- a/src/com/silverwrist/venice/servlets/SIGAdmin.java +++ b/src/com/silverwrist/venice/servlets/SIGAdmin.java @@ -254,6 +254,47 @@ public class SIGAdmin extends VeniceServlet } // end if ("M" command) + if (cmd.equals("A")) + { // "A" = "Display Audit Records" + try + { + int offset = 0; + try + { // convert the offset parameter + String s_ofs = request.getParameter("ofs"); + if (!StringUtil.isStringEmpty(s_ofs)) + offset = Integer.parseInt(s_ofs); + + } // end try + catch (NumberFormatException nfe) + { // if it's untranslatable, set it at 0 + offset = 0; + + } // end catch + + // generate the lists + List audit_list = sig.getAuditRecords(offset,engine.getNumAuditRecordsPerPage()); + int audit_count = sig.getAuditRecordCount(); + + // return the audit viewer + return new AuditDataViewer(engine,audit_list,offset,audit_count,"Audit Records for SIG \"" + + sig.getName() + "\"","sigadmin?sig=" + sig.getSIGID() + "&cmd=A&ofs=%"); + + } // end try + catch (AccessError ae) + { // you don't have access + return new ErrorBox("Access Error",ae.getMessage(),on_error); + + } // end catch + catch (DataException de) + { // something wrong in the database + return new ErrorBox("Database Error","Database error getting audit records: " + de.getMessage(), + on_error); + + } // end catch + + } // end if ("A" command) + if (cmd.equals("DEL")) { // "DEL" = "Delete SIG" (requires a confirmation) if (!(sig.canDelete())) diff --git a/src/com/silverwrist/venice/servlets/SystemAdmin.java b/src/com/silverwrist/venice/servlets/SystemAdmin.java index f4ff99e..3766288 100644 --- a/src/com/silverwrist/venice/servlets/SystemAdmin.java +++ b/src/com/silverwrist/venice/servlets/SystemAdmin.java @@ -18,9 +18,11 @@ package com.silverwrist.venice.servlets; import java.io.*; +import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.log4j.*; +import com.silverwrist.util.StringUtil; import com.silverwrist.venice.core.*; import com.silverwrist.venice.servlets.format.*; @@ -82,7 +84,50 @@ public class SystemAdmin extends VeniceServlet if (logger.isDebugEnabled()) logger.debug("SystemAdmin/doGet command value = " + cmd); - // TODO: command handling + if (cmd.equals("A")) + { // "A" = View System Audit Records + try + { // get the list of audit records + AdminOperations adm = user.getAdminInterface(); + int offset = 0; + try + { // convert the offset parameter + String s_ofs = request.getParameter("ofs"); + if (!StringUtil.isStringEmpty(s_ofs)) + offset = Integer.parseInt(s_ofs); + + } // end try + catch (NumberFormatException nfe) + { // if it's untranslatable, set it at 0 + offset = 0; + + } // end catch + + // generate the lists + List audit_list = adm.getAuditRecords(offset,engine.getNumAuditRecordsPerPage()); + int audit_count = adm.getAuditRecordCount(); + + // return the audit viewer + setMyLocation(request,"sysadmin?cmd=A&ofs=" + offset); + return new AuditDataViewer(engine,audit_list,offset,audit_count,"System Audit Records", + "sysadmin?cmd=A&ofs=%"); + + } // end try + catch (AccessError ae) + { // an access error generally means we're not an administrator + return new ErrorBox("Access Error","You do not have permission to administer the system.",null); + + } // end catch + catch (DataException de) + { // error pulling the audit records + return new ErrorBox("Database Error","Unable to retrieve audit records: " + de.getMessage(), + "sysadmin"); + + } // end catch + + } // end if ("A" command) + + // TODO: other command handling if (!(user.hasAdminAccess())) return new ErrorBox("Access Error","You do not have permission to administer the system.",null); diff --git a/src/com/silverwrist/venice/servlets/format/AuditDataViewer.java b/src/com/silverwrist/venice/servlets/format/AuditDataViewer.java new file mode 100644 index 0000000..d9ba639 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/AuditDataViewer.java @@ -0,0 +1,152 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets.format; + +import java.io.*; +import java.util.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.core.*; + +public class AuditDataViewer implements ContentRender +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private List audit_list; + private int offset; + private int total_count; + private int last_index; + private String title; + private String next_url; + private String prev_url; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public AuditDataViewer(VeniceEngine engine, List audit_list, int offset, int total_count, String title, + String template_url) + { + this.audit_list = audit_list; + this.offset = offset; + this.total_count = total_count; + int npage = engine.getNumAuditRecordsPerPage(); + this.last_index = offset + npage; + if (this.last_index>total_count) + this.last_index = total_count; + this.title = title; + if (this.last_index0) + this.prev_url = StringUtil.replaceAllInstances(template_url,"%",String.valueOf(offset-npage)); + else + this.prev_url = null; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Implementations from interface VeniceContent + *-------------------------------------------------------------------------------- + */ + + public String getPageTitle(RenderData rdat) + { + return title; + + } // end getPageTitle + + /*-------------------------------------------------------------------------------- + * Implementations from interface ContentRender + *-------------------------------------------------------------------------------- + */ + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + rdat.writeContentHeader(out,title,null); + + // Write the informational and navigational table + out.write("
" + rdat.getStdFontTag(null,2)); + out.write("\nDisplaying records " + (offset+1) + " to " + last_index + " of " + + total_count + "\n"); + out.write("\n"); + if (prev_url==null) + out.write("\"\"\n"); + else + out.write("\"Previous\""); + out.write(" "); + if (next_url==null) + out.write("\"\"\n"); + else + out.write("\"Next\""); + out.write("\n
\n"); + + // Start writing the table containing the actual audit records. + String tb_font = rdat.getStdFontTag(null,2); + out.write("\n"); + out.write("\n\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n\n"); + Iterator it = audit_list.iterator(); + while (it.hasNext()) + { // display each record in turn + AuditData dat = (AuditData)(it.next()); + out.write("\n\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + for (int i=0; i" + tb_font); + if (dat.getData(i)!=null) + out.write(StringUtil.encodeHTML(dat.getData(i))); + else + out.write(" "); + out.write("\n"); + + } // end for + + out.write("\n"); + + } // end while + + out.write("
" + tb_font + "Date/Time" + tb_font + "Description" + tb_font + "User" + tb_font + "SIG" + tb_font + "IP Address" + tb_font + "Additional Data
" + tb_font + + rdat.formatDateForDisplay(dat.getDateTime()) + "" + tb_font + + StringUtil.encodeHTML(dat.getDescription()) + "" + tb_font + + StringUtil.encodeHTML(dat.getUserName()) + "" + tb_font + + StringUtil.encodeHTML(dat.getSIGName()) + "" + tb_font + + StringUtil.encodeHTML(dat.getIPAddress()) + "
\n"); + rdat.writeFooter(out); + + } // end renderHere + +} // end class AuditDataViewer diff --git a/src/com/silverwrist/venice/servlets/format/CDTimeZoneListFormField.java b/src/com/silverwrist/venice/servlets/format/CDTimeZoneListFormField.java index 97ac487..b2786a8 100644 --- a/src/com/silverwrist/venice/servlets/format/CDTimeZoneListFormField.java +++ b/src/com/silverwrist/venice/servlets/format/CDTimeZoneListFormField.java @@ -36,6 +36,7 @@ public class CDTimeZoneListFormField extends CDPickListFormField TimeZoneList() { array = TimeZone.getAvailableIDs(); + Arrays.sort(array); } // end constructor diff --git a/src/com/silverwrist/venice/servlets/format/RenderConfig.java b/src/com/silverwrist/venice/servlets/format/RenderConfig.java index 3d2d2a5..1f45dbe 100644 --- a/src/com/silverwrist/venice/servlets/format/RenderConfig.java +++ b/src/com/silverwrist/venice/servlets/format/RenderConfig.java @@ -54,7 +54,6 @@ public class RenderConfig private boolean want_comments; private boolean allow_gzip; private String font_face; - private String base_url; private String image_url; private String static_url; private String site_logo; @@ -130,17 +129,6 @@ public class RenderConfig } // end if DOMElementHelper paths_sect_h = new DOMElementHelper(paths_sect); - base_url = paths_sect_h.getSubElementText("base"); - if (base_url==null) - { // no tag - bail out now! - logger.fatal(" section has no element"); - throw new ConfigException("no found in section",paths_sect); - - } // end if - - if (logger.isDebugEnabled()) - logger.debug("Base path: " + base_url); - image_url = paths_sect_h.getSubElementText("image"); if (image_url==null) { // no tag - bail out now! @@ -284,14 +272,6 @@ public class RenderConfig } // end isGZIPAllowed - String getFullServletPath(String name) - { - StringBuffer buf = new StringBuffer(); - buf.append(base_url).append(name); - return buf.toString(); - - } // end getFullServletPath - String getFullImagePath(String name) { StringBuffer buf = new StringBuffer(); @@ -341,6 +321,14 @@ public class RenderConfig } // end getStdFontTag + String getStdBaseFontTag(int size) + { + StringBuffer buf = new StringBuffer("
\n\n" + + "\n\n\n" + + "
\n"); + out.write(getStdFontTag(null,1)); + out.write(getStockMessage("footer-text")); + out.write("\n\n" + + "\"Powered\n\n"); + out.write("\" ALT=\"Powered by Venice\" WIDTH=140 HEIGHT=80 BORDER=0 HSPACE=0 VSPACE=0>\n
\n"); } // end writeFooter diff --git a/src/com/silverwrist/venice/servlets/format/RenderData.java b/src/com/silverwrist/venice/servlets/format/RenderData.java index 9ef6d1e..f223964 100644 --- a/src/com/silverwrist/venice/servlets/format/RenderData.java +++ b/src/com/silverwrist/venice/servlets/format/RenderData.java @@ -137,13 +137,15 @@ public class RenderData public String getFullServletPath(String name) { - return rconf.getFullServletPath(name); + StringBuffer buf = new StringBuffer(request.getContextPath()); + buf.append('/').append(name); + return buf.toString(); } // end getFullServletPath public String getEncodedServletPath(String name) { - return response.encodeURL(rconf.getFullServletPath(name)); + return response.encodeURL(this.getFullServletPath(name)); } // end getEncodedServletPath @@ -171,6 +173,12 @@ public class RenderData } // end getStdFontTag + public String getStdBaseFontTag(int size) + { + return rconf.getStdBaseFontTag(size); + + } // end getStdBaseFontTag + public String getTitleTag(String specific) { return rconf.getTitleTag(specific); @@ -306,7 +314,7 @@ public class RenderData public void redirectTo(String servlet) throws IOException { - String url = response.encodeRedirectURL(rconf.getFullServletPath(servlet)); + String url = response.encodeRedirectURL(this.getFullServletPath(servlet)); response.sendRedirect(url); } // end redirectTo diff --git a/src/com/silverwrist/venice/servlets/format/SIGAdminTop.java b/src/com/silverwrist/venice/servlets/format/SIGAdminTop.java index 65dbe95..0bc0dbb 100644 --- a/src/com/silverwrist/venice/servlets/format/SIGAdminTop.java +++ b/src/com/silverwrist/venice/servlets/format/SIGAdminTop.java @@ -36,6 +36,7 @@ public class SIGAdminTop extends ContentMenuPanel addChoice("Set SIG Category","sigadmin?sig=$s&cmd=T"); addChoice("Set SIG Features","sigadmin?sig=$s&cmd=F"); addChoice("Membership Control","sigadmin?sig=$s&cmd=M"); + addChoice("Display Audit Records","sigadmin?sig=$s&cmd=A"); // TODO: More options addChoice("Delete SIG","sigadmin?sig=$s&cmd=DEL"); diff --git a/src/com/silverwrist/venice/servlets/format/SystemAdminTop.java b/src/com/silverwrist/venice/servlets/format/SystemAdminTop.java index 9df7483..057fd13 100644 --- a/src/com/silverwrist/venice/servlets/format/SystemAdminTop.java +++ b/src/com/silverwrist/venice/servlets/format/SystemAdminTop.java @@ -34,6 +34,7 @@ public class SystemAdminTop extends ContentMenuPanel addChoice("Set Global Parameters","TODO"); addChoice("View/Edit Banned Users","TODO"); addChoice("User Account Management","TODO"); + addChoice("System Audit Logs","sysadmin?cmd=A"); } // end constructor diff --git a/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java b/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java new file mode 100644 index 0000000..89d8314 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java @@ -0,0 +1,156 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets.format; + +import java.io.*; +import javax.servlet.*; +import javax.servlet.http.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.htmlcheck.*; +import com.silverwrist.venice.core.*; + +public class TextMessageDialog implements ContentRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Dialog types + public static final int TYPE_ACCEPT_DECLINE = 0; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private int type; + private String title; + private String text; + private String[] choices; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public TextMessageDialog(int type, String title, String text, String[] choices) + { + this.type = type; + this.title = title; + this.text = text; + this.choices = choices; + + int nbutton = getNumButtons(type); + if (choices.length

\n

\n"); + + int nbutton = getNumButtons(type); + for (int i=0; i0) + out.write(" \n"); + out.write("\"");\n"); + + } // end for + + out.write("
\n"); + rdat.writeFooter(out); + + } // end renderHere + +} // end class TextMessageDialog diff --git a/web/format/base.jsp b/web/format/base.jsp index cc2671f..f0b7e66 100644 --- a/web/format/base.jsp +++ b/web/format/base.jsp @@ -43,6 +43,7 @@ private static void renderMenu(HttpSession session, java.io.Writer out, RenderDa <%= rdat.getTitleTag(basedat.getTitle(rdat)) %> + <%= rdat.getStdBaseFontTag(3) %> diff --git a/web/format/top_content.jsp b/web/format/top_content.jsp index f7e45e0..abdcd0e 100644 --- a/web/format/top_content.jsp +++ b/web/format/top_content.jsp @@ -28,7 +28,7 @@ <% if (rdat.useHTMLComments()) { %><% } %> <% if (data.displayWelcome()) { %> <% rdat.writeContentHeader(out,rdat.getStockMessage("welcome-top"),null); %> - <%= rdat.getStdFontTag(null,1) %><% rdat.writeStockMessage(out,"welcome"); %>

+ <%= rdat.getStdFontTag(null,1) %><%= rdat.getStockMessage(out,"welcome"); %>

<% } // end if %> <% rdat.writeContentHeader(out,rdat.getStockMessage("currents-top"),null); %> <% int ntp = data.getNumTopPosts(); %> diff --git a/web/images/bn_i_accept.gif b/web/images/bn_i_accept.gif new file mode 100644 index 0000000..9fd030f Binary files /dev/null and b/web/images/bn_i_accept.gif differ diff --git a/web/images/bn_i_decline.gif b/web/images/bn_i_decline.gif new file mode 100644 index 0000000..d9ea853 Binary files /dev/null and b/web/images/bn_i_decline.gif differ