From 63fedc9db6e4e287d7a6ae97c75278cf94a0db52 Mon Sep 17 00:00:00 2001 From: "Eric J. Bowersox" Date: Mon, 9 Apr 2001 03:20:58 +0000 Subject: [PATCH] some serious new feature implementation: - cookie-based persistent logins - expanded activity reporting - "top" and "fixed" left menus are now dynamically generated from XML config, not hard coded - error reporting enhanced and protection increased - "About Venice" page first draft - new means of "framing" static content within the Venice "frame" - base page now includes the "footer" itself, "content" pages don't anymore - general cleanup of some heavyweight old containers, replaced with faster Collections framework containers - probably more, there's a LOT of stuff in here --- etc/render-config.xml | 36 +- etc/web.xml | 13 + setup/database.sql | 3 + .../silverwrist/util/cachemap/CacheMap.java | 400 ++++++++++++++++++ .../util/cachemap/CacheMapEntry.java | 154 +++++++ .../cachemap/CacheMapStrategy.java} | 21 +- .../util/rcache/ReferenceCache.java | 5 +- .../silverwrist/venice/core/TopicContext.java | 12 + .../silverwrist/venice/core/UserContext.java | 4 + .../core/impl/CategoryDescriptorImpl.java | 36 +- .../venice/core/impl/ConferenceCoreData.java | 13 +- .../core/impl/ConferenceUserContextImpl.java | 45 +- .../venice/core/impl/EngineBackend.java | 4 + .../venice/core/impl/SIGCoreData.java | 9 +- .../venice/core/impl/SIGUserContextImpl.java | 15 +- .../impl/TopicMessageUserContextImpl.java | 5 +- .../core/impl/TopicUserContextImpl.java | 155 ++++++- .../venice/core/impl/UserContextImpl.java | 237 ++++++++++- .../venice/core/impl/VeniceEngineImpl.java | 74 ++-- .../venice/security/AuditRecord.java | 11 +- src/com/silverwrist/venice/security/Role.java | 349 ++++++++------- .../silverwrist/venice/servlets/Account.java | 17 +- .../venice/servlets/ConfOperations.java | 36 +- .../venice/servlets/FrameStatic.java | 63 +++ .../venice/servlets/UserDisplay.java | 2 +- .../venice/servlets/Variables.java | 111 ++++- .../venice/servlets/VeniceServlet.java | 310 +++++++++----- .../servlets/format/AuditDataViewer.java | 1 - .../venice/servlets/format/BaseJSPData.java | 20 +- .../servlets/format/ConferenceActivity.java | 60 ++- .../venice/servlets/format/ConfirmBox.java | 1 - .../venice/servlets/format/ContentDialog.java | 1 - .../servlets/format/ContentMenuPanel.java | 1 - .../venice/servlets/format/ErrorBox.java | 1 - .../venice/servlets/format/LoginDialog.java | 4 +- .../servlets/format/ManageConference.java | 8 +- .../venice/servlets/format/RenderConfig.java | 83 +++- .../venice/servlets/format/RenderData.java | 41 +- .../servlets/format/ReportConferenceMenu.java | 128 ++++++ .../venice/servlets/format/StaticRender.java | 184 ++++++++ .../servlets/format/TextMessageDialog.java | 1 - .../venice/servlets/format/TopDisplay.java | 1 - .../format/menus/AbsoluteLeftMenuItem.java} | 45 +- .../format/menus/FrameLeftMenuItem.java | 57 +++ .../servlets/format/menus/LeftMenu.java | 188 ++++++++ .../servlets/format/menus/LeftMenuItem.java | 93 ++++ .../format/menus/ServletLeftMenuItem.java | 57 +++ web/format/attach_form.jsp | 1 - web/format/base.jsp | 76 ++-- web/format/conf_activity.jsp | 30 +- web/format/conf_member.jsp | 1 - web/format/conf_sequence.jsp | 1 - web/format/conferences.jsp | 1 - web/format/find.jsp | 1 - web/format/hotlist.jsp | 1 - web/format/invitation.jsp | 1 - web/format/manage_aliases.jsp | 1 - web/format/manage_conf.jsp | 8 +- web/format/newsigwelcome.jsp | 2 - web/format/newtopic.jsp | 3 +- web/format/posts.jsp | 3 +- web/format/preview.jsp | 1 - web/format/report_conf.jsp | 73 ++++ web/format/sig_member.jsp | 1 - web/format/sigcatbrowser.jsp | 1 - web/format/siglist.jsp | 1 - web/format/sigprofile.jsp | 1 - web/format/sigwelcome.jsp | 1 - web/format/slippage.jsp | 3 - web/format/top_content.jsp | 2 +- web/format/topics.jsp | 1 - web/format/userprofile.jsp | 2 - web/images/sw-main.gif | Bin 0 -> 4544 bytes web/images/venicelogo.jpg | Bin 0 -> 9323 bytes web/static/about-venice.html | 43 ++ web/static/images/sw-main.gif | Bin 0 -> 4544 bytes web/static/images/venicelogo.jpg | Bin 0 -> 9323 bytes 77 files changed, 2817 insertions(+), 558 deletions(-) create mode 100644 src/com/silverwrist/util/cachemap/CacheMap.java create mode 100644 src/com/silverwrist/util/cachemap/CacheMapEntry.java rename src/com/silverwrist/{venice/servlets/format/MenuTop.java => util/cachemap/CacheMapStrategy.java} (60%) create mode 100644 src/com/silverwrist/venice/servlets/FrameStatic.java create mode 100644 src/com/silverwrist/venice/servlets/format/ReportConferenceMenu.java create mode 100644 src/com/silverwrist/venice/servlets/format/StaticRender.java rename src/com/silverwrist/{util/collections/ReadOnlyVector.java => venice/servlets/format/menus/AbsoluteLeftMenuItem.java} (64%) create mode 100644 src/com/silverwrist/venice/servlets/format/menus/FrameLeftMenuItem.java create mode 100644 src/com/silverwrist/venice/servlets/format/menus/LeftMenu.java create mode 100644 src/com/silverwrist/venice/servlets/format/menus/LeftMenuItem.java create mode 100644 src/com/silverwrist/venice/servlets/format/menus/ServletLeftMenuItem.java create mode 100644 web/format/report_conf.jsp create mode 100644 web/images/sw-main.gif create mode 100644 web/images/venicelogo.jpg create mode 100644 web/static/about-venice.html create mode 100644 web/static/images/sw-main.gif create mode 100644 web/static/images/venicelogo.jpg diff --git a/etc/render-config.xml b/etc/render-config.xml index 24a4642..2c2fb03 100644 --- a/etc/render-config.xml +++ b/etc/render-config.xml @@ -8,7 +8,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 @@ -86,4 +86,38 @@ Text of this agreement is TBD. + + + + + +
Front Page
+ + Calendar + TODO + + + + Chat + TODO + + +
+ + + +
About This Site
+ + Documentation + TODO + + + + About Venice + about-venice.html + +
+ +
+ diff --git a/etc/web.xml b/etc/web.xml index e9937ad..3c43bdc 100644 --- a/etc/web.xml +++ b/etc/web.xml @@ -69,6 +69,14 @@ 3 + + framestatic + + Displays static content inside the Venice frame. + + com.silverwrist.venice.servlets.FrameStatic + + account @@ -216,6 +224,11 @@ /top + + framestatic + /frame/* + + account /account diff --git a/setup/database.sql b/setup/database.sql index 4c432b9..8acd443 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -71,6 +71,7 @@ CREATE TABLE users ( uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL, passhash VARCHAR(64) NOT NULL, + tokenauth VARCHAR(64), contactid INT DEFAULT -1, is_anon TINYINT DEFAULT 0, verify_email TINYINT DEFAULT 0, @@ -345,6 +346,8 @@ CREATE TABLE topicsettings ( uid INT NOT NULL, hidden TINYINT DEFAULT 0, last_message INT DEFAULT -1, + last_read DATETIME, + last_post DATETIME, PRIMARY KEY (topicid, uid) ); diff --git a/src/com/silverwrist/util/cachemap/CacheMap.java b/src/com/silverwrist/util/cachemap/CacheMap.java new file mode 100644 index 0000000..7aea300 --- /dev/null +++ b/src/com/silverwrist/util/cachemap/CacheMap.java @@ -0,0 +1,400 @@ +/* + * 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.util.cachemap; + +import java.util.*; + +public class CacheMap implements Map +{ + /*-------------------------------------------------------------------------------- + * Internal class used to do comparisons for cache shrinkage + *-------------------------------------------------------------------------------- + */ + + static class CacheOrdering implements Comparator + { + private CacheMapStrategy strategy; // CacheMap's strategy object + private long tick; // when the sort operation started + + CacheOrdering(CacheMapStrategy strategy) + { + this.strategy = strategy; + this.tick = System.currentTimeMillis(); + + } // end constructor + + public int compare(Object o1, Object o2) + { + long figm1 = strategy.getEntryValue((CacheMapEntry)o1,tick); + long figm2 = strategy.getEntryValue((CacheMapEntry)o2,tick); + return (int)(figm1 - figm2); // we want the largest figures of merit to go first + + } // end compare + + public boolean equals(Object o) + { + return (o instanceof CacheOrdering); + + } // end equals + + } // end class CacheOrdering + + /*-------------------------------------------------------------------------------- + * Internal class implementing a default cache ordering strategy + *-------------------------------------------------------------------------------- + */ + + static class DefaultStrategy implements CacheMapStrategy + { + private static final long SCALING_FACTOR = 5000; + + DefaultStrategy() + { // do nothing + } // end constructor + + public long getEntryValue(CacheMapEntry entry, long tick) + { + return (entry.getHits() * SCALING_FACTOR) - entry.getAge(tick); + + } // end getEntryValue + + } // end class DefaultStrategy + + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static final DefaultStrategy default_strategy_singleton = new DefaultStrategy(); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private int capacity; // capacity of the CacheMap + private int shrink_percentage; // what percentage we shrink by when full + private CacheMapStrategy strategy; // strategy routine to use to purge entries + private HashMap base_map; // maps keys to CacheMapEntry values + private ArrayList element_list; // the actual elements + + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + + public CacheMap(int capacity, int shrink_percentage, CacheMapStrategy strategy) + { + if (capacity<=0) + throw new IllegalArgumentException("capacity must be greater than 0"); + if ((shrink_percentage<=0) || (shrink_percentage>100)) + throw new IllegalArgumentException("shrink_percentage must be in [1, 100]"); + if (strategy==null) + throw new NullPointerException("no strategy passed to CacheMap"); + + this.capacity = capacity; + this.shrink_percentage = shrink_percentage; + this.strategy = strategy; + this.base_map = new HashMap(10); + this.element_list = new ArrayList(10); + + } // end constructor + + public CacheMap(int capacity, int shrink_percentage) + { + this(capacity,shrink_percentage,default_strategy_singleton); + + } // end constructor + + public CacheMap(int capacity) + { + this(capacity,10,default_strategy_singleton); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Implementations from interface Map + *-------------------------------------------------------------------------------- + */ + + public int size() + { + return base_map.size(); + + } // end size + + public boolean isEmpty() + { + return base_map.isEmpty(); + + } // end isEmpty + + public boolean containsKey(Object key) + { + return base_map.containsKey(key); + + } // end containsKey + + public boolean containsValue(Object value) + { + Iterator it = element_list.iterator(); + while (it.hasNext()) + { // look at all the CacheMapEntry values we have + CacheMapEntry cme = (CacheMapEntry)(it.next()); + Object my_val = cme.getValue(); + if (my_val==null) + { // test for also null + if (value==null) + return true; + + } // end if + else + { // make sure the other value is non-null before we test equality + if ((value!=null) && my_val.equals(value)) + return true; + + } // end else + + } // end while + + return false; // nope, sorry + + } // end containsValue + + public Object get(Object key) + { + CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); + if (cme==null) + return null; + cme.touch(); + return cme.getValue(); + + } // end get + + public Object put(Object key, Object value) + { + Object rc = null; + CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); + if (cme==null) + { // create a new CacheMapEntry for this key + cme = new CacheMapEntry(key,value); + + synchronized (this) + { // insert it into the basic object + if (base_map.size()==capacity) + shrink(); + element_list.add(cme); + base_map.put(cme.getKey(),cme); + + } // end synchronized block + + } // end if + else + { // we have an old value - replace it and touch the entry + cme.touch(); + rc = cme.setValue(value); + + } // end else + + return rc; + + } // end put + + public Object remove(Object key) + { + Object rc = null; + CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); + if (cme!=null) + { // save the mapped value before we remove it + rc = cme.getValue(); + + synchronized (this) + { // remove the values + base_map.remove(key); + element_list.remove(cme); + + } // end synchronized block + + } // end if + + return rc; + + } // end remove + + public void putAll(Map map) + { + synchronized (this) + { // make sure we have enough space in the CacheMap for all the new elements! + while ((map.size() + base_map.size()) > capacity) + shrink(); + + } // end synchronized block + + Iterator it = map.entrySet().iterator(); + while (it.hasNext()) + { // add each element in turn + Map.Entry me = (Map.Entry)(it.next()); + put(me.getKey(),me.getValue()); + + } // end while + + } // end putAll + + public synchronized void clear() + { + base_map.clear(); + element_list.clear(); + + } // end clear + + public Set keySet() + { + return base_map.keySet(); + + } // end keySet + + public Collection values() + { + return null; // not implemented + + } // end values + + public Set entrySet() + { + return null; // not implemented + + } // end entrySet + + public boolean equals(Object o) + { + if ((o==null) || !(o instanceof Map)) + return false; // not a map + Map other = (Map)o; + if (other.size()!=base_map.size()) + return false; // size does matter! + Iterator it = base_map.values().iterator(); + while (it.hasNext()) + { // get each of the entries out and use that to do a key-value comparison + CacheMapEntry cme = (CacheMapEntry)(it.next()); + Object o1 = cme.getValue(); + Object o2 = other.get(cme.getKey()); + if (o1==null) + { // must have a matching null + if (o2!=null) + return false; + + } // end if + else + { // make sure we have a matching object (not null) + if ((o2==null) || !(o2.equals(o1))) + return false; + + } // end else + + } // end while + + return true; // all OK! + + } // end equals + + public int hashCode() + { + int rc = 0; + Iterator it = base_map.values().iterator(); + while (it.hasNext()) + { // add up the hash codes and return them + CacheMapEntry cme = (CacheMapEntry)(it.next()); + rc += cme.hashCode(); + + } // end while + + return rc; + + } // end hashCode + + /*-------------------------------------------------------------------------------- + * External getters/setters + *-------------------------------------------------------------------------------- + */ + + public int getCapacity() + { + return capacity; + + } // end getCapacity + + public void setCapacity(int c) + { + if (c<=0) + throw new IllegalArgumentException("capacity must be greater than 0"); + capacity = c; + + } // end setCapacity + + public int getShrinkPercentage() + { + return shrink_percentage; + + } // end getShrinkPercentage + + public void setShrinkPercentage(int p) + { + if ((p<=0) || (p>100)) + throw new IllegalArgumentException("shrink_percentage must be in [1, 100]"); + shrink_percentage = p; + + } // end setShrinkPercentage + + public CacheMapStrategy getStrategy() + { + return strategy; + + } // end getStrategy + + public void setStrategy(CacheMapStrategy s) + { + if (s==null) + throw new NullPointerException("no strategy passed to CacheMap"); + strategy = s; + + } // end setStrategy + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public synchronized void shrink() + { + // Figure out how many elements to remove. + int num_remove = (element_list.size() * shrink_percentage) / 100; + + // Sort the element list to figure out which elements to remove. + Collections.sort(element_list,new CacheOrdering(strategy)); + + // The elements we want to remove are at the end of the array, so start from there. + for (int i=0; i. + * + * 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.util.cachemap; + +import java.util.*; + +class CacheMapEntry implements Map.Entry +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private Object key; + private Object value; + private int hits = 0; + private long timestamp; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + CacheMapEntry(Object key, Object value) + { + this.key = key; + this.value = value; + this.timestamp = System.currentTimeMillis(); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * finalize() function + *-------------------------------------------------------------------------------- + */ + + protected void finalize() + { + key = null; + value = null; + + } // end finalize + + /*-------------------------------------------------------------------------------- + * Implementations from interface MapEntry + *-------------------------------------------------------------------------------- + */ + + public final Object getKey() + { + return key; + + } // end getKey + + public final Object getValue() + { + return value; + + } // end getValue + + public final Object setValue(Object o) + { + Object rc = value; + value = o; + return rc; + + } // end setValue + + public final boolean equals(Object o) + { + // make sure the other element is a Map.Entry + if ((o==null) || !(o instanceof Map.Entry)) + return false; + Map.Entry other = (Map.Entry)o; + + // compare the keys + if (key==null) + { // the other key must be null + if (other.getKey()!=null) + return false; + + } // end if + else + { // the other key must be equal to us + if ((other.getKey()==null) || (!(key.equals(other.getKey())))) + return false; + + } // end else + + // compare the values + if (value==null) + return (other.getValue()==null); + else + return ((other.getValue()!=null) && value.equals(other.getValue())); + + } // end equals + + public final int hashCode() + { + int rc = 0; + if (key!=null) + rc ^= key.hashCode(); + if (value!=null) + rc ^= value.hashCode(); + return rc; + + } // end hashCode + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + final int getHits() + { + return hits; + + } // end getHits + + final long getTimestamp() + { + return timestamp; + + } // end getTimestamp + + final long getAge(long tick) + { + return (tick - timestamp); + + } // end getAge + + final void touch() + { + hits++; + timestamp = System.currentTimeMillis(); + + } // end touch + +} // end class CacheMapEntry diff --git a/src/com/silverwrist/venice/servlets/format/MenuTop.java b/src/com/silverwrist/util/cachemap/CacheMapStrategy.java similarity index 60% rename from src/com/silverwrist/venice/servlets/format/MenuTop.java rename to src/com/silverwrist/util/cachemap/CacheMapStrategy.java index e8c2fe2..10f1e33 100644 --- a/src/com/silverwrist/venice/servlets/format/MenuTop.java +++ b/src/com/silverwrist/util/cachemap/CacheMapStrategy.java @@ -15,23 +15,10 @@ * * Contributor(s): */ -package com.silverwrist.venice.servlets.format; +package com.silverwrist.util.cachemap; -import java.io.Writer; -import java.io.IOException; - -public class MenuTop implements ComponentRender +public interface CacheMapStrategy { - public MenuTop() - { // constructor does nothing - } // end constructor + public abstract long getEntryValue(CacheMapEntry entry, long tick); - public void renderHere(Writer out, RenderData rdat) throws IOException - { - out.write("Front Page
\n"); - out.write("Calendar
\n"); // TODO: fill this link in - out.write("Chat\n"); // TODO: fill this link in - - } // end renderHere - -} // end class MenuTop +} // end interface CacheMapStrategy diff --git a/src/com/silverwrist/util/rcache/ReferenceCache.java b/src/com/silverwrist/util/rcache/ReferenceCache.java index ebc12cc..e310428 100644 --- a/src/com/silverwrist/util/rcache/ReferenceCache.java +++ b/src/com/silverwrist/util/rcache/ReferenceCache.java @@ -18,7 +18,6 @@ package com.silverwrist.util.rcache; import java.util.*; -import com.silverwrist.util.collections.*; public class ReferenceCache { @@ -124,7 +123,7 @@ public class ReferenceCache public List sweepReturn() { - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); int count = 0; synchronized (this) @@ -155,7 +154,7 @@ public class ReferenceCache } // end synchronized block - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end sweepReturn diff --git a/src/com/silverwrist/venice/core/TopicContext.java b/src/com/silverwrist/venice/core/TopicContext.java index 2a8f528..3a15113 100644 --- a/src/com/silverwrist/venice/core/TopicContext.java +++ b/src/com/silverwrist/venice/core/TopicContext.java @@ -78,5 +78,17 @@ public interface TopicContext public abstract void delete() throws DataException, AccessError; + public abstract List getActivePosters(int skip, int limit) throws DataException, AccessError; + + public abstract List getActivePosters(int limit) throws DataException, AccessError; + + public abstract List getActivePosters() throws DataException, AccessError; + + public abstract List getActiveReaders(int skip, int limit) throws DataException, AccessError; + + public abstract List getActiveReaders(int limit) throws DataException, AccessError; + + public abstract List getActiveReaders() throws DataException, AccessError; + } // end interface TopicContext diff --git a/src/com/silverwrist/venice/core/UserContext.java b/src/com/silverwrist/venice/core/UserContext.java index fb08fe7..9330426 100644 --- a/src/com/silverwrist/venice/core/UserContext.java +++ b/src/com/silverwrist/venice/core/UserContext.java @@ -103,4 +103,8 @@ public interface UserContext extends SearchMode public abstract void setTimeZone(TimeZone timezone) throws DataException; + public abstract String getAuthenticationToken() throws AccessError, DataException; + + public abstract boolean authenticateWithToken(String token) throws DataException; + } // end interface UserContext diff --git a/src/com/silverwrist/venice/core/impl/CategoryDescriptorImpl.java b/src/com/silverwrist/venice/core/impl/CategoryDescriptorImpl.java index 2d68dbb..452caee 100644 --- a/src/com/silverwrist/venice/core/impl/CategoryDescriptorImpl.java +++ b/src/com/silverwrist/venice/core/impl/CategoryDescriptorImpl.java @@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl; import java.sql.*; import java.util.*; import org.apache.log4j.*; -import com.silverwrist.util.collections.*; import com.silverwrist.venice.db.*; import com.silverwrist.venice.core.*; @@ -70,7 +69,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable */ private DataPool datapool; // used for doing database lookups - private Vector cats; // the actual category segments + private LinkedList cats; // the actual category segments private int symlink = -1; // if our category is actually a symlink private boolean do_hide = true; // do we hide subcategories marked hide_dir? @@ -82,7 +81,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable CategoryDescriptorImpl(DataPool datapool, int catid, boolean do_hide) throws DataException { this.datapool = datapool; - cats = new Vector(); + cats = new LinkedList(); this.do_hide = do_hide; if (catid<0) @@ -114,7 +113,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable throws SQLException, DataException { this.datapool = datapool; - cats = new Vector(); + cats = new LinkedList(); this.do_hide = do_hide; if (catid<0) @@ -127,19 +126,18 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable protected CategoryDescriptorImpl(DataPool datapool, int id, int symlink, String name, boolean do_hide) { this.datapool = datapool; - this.cats = new Vector(); + this.cats = new LinkedList(); this.symlink = symlink; this.do_hide = do_hide; this.cats.add(new CatSegment(id,name)); - this.cats.trimToSize(); } // end constructor protected CategoryDescriptorImpl(CategoryDescriptorImpl other, int copy_levels) { this.datapool = other.datapool; - this.cats = new Vector(); + this.cats = new LinkedList(); this.symlink = ((copy_levels==other.cats.size()) ? other.symlink : -1); this.do_hide = other.do_hide; @@ -147,7 +145,6 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable { // copy the references to the objects directly for (int i=0; i0) - return ((CatSegment)(cats.lastElement())).getID(); + return ((CatSegment)(cats.getLast())).getID(); else return -1; @@ -260,7 +254,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable } // end if Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // get a connection and create a statement conn = datapool.getConnection(); @@ -294,8 +288,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable } // end finally - // wrap the vector in a ReadOnlyVector object - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getSubCategories @@ -393,7 +386,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable static List getTopLevelCategoryList(DataPool datapool, boolean do_hide) throws DataException { Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // get a connection and create a statement conn = datapool.getConnection(); @@ -426,8 +419,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable } // end finally - // wrap the vector in a ReadOnlyVector object - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getTopLevelCategoryList @@ -438,7 +430,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable logger.debug("Category search: mode = " + String.valueOf(mode) + ", term '" + term + "', offset = " + String.valueOf(offset) + ", count = " + String.valueOf(count)); - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); Connection conn = null; // pooled database connection try @@ -505,7 +497,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end searchForCategories diff --git a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java index e7b256f..b6ebdc1 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java @@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl; import java.sql.*; import java.util.*; import org.apache.log4j.*; -import com.silverwrist.util.collections.*; import com.silverwrist.venice.db.*; import com.silverwrist.venice.security.AuditRecord; import com.silverwrist.venice.security.DefaultLevels; @@ -233,7 +232,7 @@ class ConferenceCoreData implements ConferenceData throw new DataException("This conference has been deleted."); Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // get a database connection from this object @@ -263,7 +262,7 @@ class ConferenceCoreData implements ConferenceData } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getAlias @@ -273,7 +272,7 @@ class ConferenceCoreData implements ConferenceData throw new DataException("This conference has been deleted."); Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // get a database connection from this object @@ -312,7 +311,7 @@ class ConferenceCoreData implements ConferenceData } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getHosts @@ -1059,7 +1058,7 @@ class ConferenceCoreData implements ConferenceData if (logger.isDebugEnabled()) logger.debug("Member list: conference = " + confid); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1104,7 +1103,7 @@ class ConferenceCoreData implements ConferenceData } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getMemberList diff --git a/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java b/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java index e6a3570..9e775c6 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java @@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl; import java.sql.*; import java.util.*; import org.apache.log4j.*; -import com.silverwrist.util.collections.*; import com.silverwrist.util.rcache.ReferencedData; import com.silverwrist.venice.db.*; import com.silverwrist.venice.htmlcheck.*; @@ -95,19 +94,21 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end constructor - public void doFix(Statement stmt, int uid) throws SQLException + public void doFix(Statement stmt, int uid, java.util.Date date) throws SQLException { StringBuffer sql = new StringBuffer(); if (do_insert) { // construct an SQL INSERT statement - sql.append("INSERT INTO topicsettings (topicid, uid, last_message) VALUES (").append(topicid); - sql.append(", ").append(uid).append(", ").append(top_message).append(");"); + sql.append("INSERT INTO topicsettings (topicid, uid, last_message, last_read) VALUES ("); + sql.append(topicid).append(", ").append(uid).append(", ").append(top_message).append(", '"); + sql.append(SQLUtil.encodeDate(date)).append("');"); } // end if else { // construct an SQL UPDATE statement - sql.append("UPDATE topicsettings SET last_message = ").append(top_message).append(" WHERE topicid = "); - sql.append(topicid).append(" AND uid = ").append(uid).append(';'); + sql.append("UPDATE topicsettings SET last_message = ").append(top_message).append(", last_read = '"); + sql.append(SQLUtil.encodeDate(date)).append("' WHERE topicid = ").append(topicid); + sql.append(" AND uid = ").append(uid).append(';'); } // end else @@ -941,8 +942,17 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend // now we need to reset our last post date Connection conn = null; try - { // get a connection and feed it to the touchPost function + { // get a connection conn = datapool.getConnection(); + + // create a new record in topicsettings (we WERE the first to post in the topic after all!) + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("INSERT INTO topicsettings (topicid, uid, last_post) VALUES ("); + sql.append(new_topic_inf.getTopicID()).append(", ").append(sig.realUID()).append(", '"); + sql.append(SQLUtil.encodeDate(new_topic_inf.getCreateDate())).append("');"); + stmt.executeUpdate(sql.toString()); + + // update the conference last-post information touchPost(conn,new_topic_inf.getCreateDate()); } // end try @@ -1024,16 +1034,17 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend ResultSet rs = stmt.executeQuery(sql.toString()); // use the results to build up a list of FixSeenHelpers - Vector tmp = new Vector(); + ArrayList tmp = new ArrayList(); while (rs.next()) tmp.add(new FixSeenHelper(rs.getInt(1),rs.getInt(2),rs.getBoolean(3))); // now iterate over the list and call doFix on each one Iterator it = tmp.iterator(); + java.util.Date now = new java.util.Date(); while (it.hasNext()) { // just hit each one in turn FixSeenHelper fsh = (FixSeenHelper)(it.next()); - fsh.doFix(stmt,sig.realUID()); + fsh.doFix(stmt,sig.realUID(),now); } // end while @@ -1074,7 +1085,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end if Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // retrieve a connection from the datapool @@ -1115,7 +1126,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getActivePosters @@ -1141,7 +1152,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end if Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // retrieve a connection from the datapool @@ -1182,7 +1193,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getActiveReaders @@ -1700,7 +1711,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend { if (logger.isDebugEnabled()) logger.debug("getSIGConferences for SIG # " + sig.realSIGID() + ", user #" + sig.realUID()); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1751,7 +1762,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getSIGConferences @@ -1863,7 +1874,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend logger.debug("getUserHotlist for user #" + user.realUID()); Connection conn = null; // pooled database connection - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function try { // get a database connection @@ -1926,7 +1937,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getUserHotlist diff --git a/src/com/silverwrist/venice/core/impl/EngineBackend.java b/src/com/silverwrist/venice/core/impl/EngineBackend.java index c0bad70..80a344c 100644 --- a/src/com/silverwrist/venice/core/impl/EngineBackend.java +++ b/src/com/silverwrist/venice/core/impl/EngineBackend.java @@ -93,4 +93,8 @@ public interface EngineBackend public abstract void unpublish(long postid); + public abstract String generateRandomAuthString(); + + public abstract boolean isValidRandomAuthString(String s); + } // end interface EngineBackend diff --git a/src/com/silverwrist/venice/core/impl/SIGCoreData.java b/src/com/silverwrist/venice/core/impl/SIGCoreData.java index 7e177c6..a60a29b 100644 --- a/src/com/silverwrist/venice/core/impl/SIGCoreData.java +++ b/src/com/silverwrist/venice/core/impl/SIGCoreData.java @@ -21,7 +21,6 @@ import java.sql.*; import java.util.*; import org.apache.log4j.*; import com.silverwrist.util.StringUtil; -import com.silverwrist.util.collections.*; import com.silverwrist.util.rcache.*; import com.silverwrist.venice.db.*; import com.silverwrist.venice.core.*; @@ -1493,7 +1492,7 @@ class SIGCoreData implements SIGData, SIGDataBackend logger.debug("Member search: SIG = " + sigid + ", field = " + field + ", mode = " + mode + ", term '" + term + "', offset = " + offset + ", count = " + count); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1584,7 +1583,7 @@ class SIGCoreData implements SIGData, SIGDataBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end searchForMembers @@ -1689,7 +1688,7 @@ class SIGCoreData implements SIGData, SIGDataBackend if (logger.isDebugEnabled()) logger.debug("Member list: SIG = " + sigid); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1737,7 +1736,7 @@ class SIGCoreData implements SIGData, SIGDataBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getMemberList diff --git a/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java b/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java index 102af25..11777be 100644 --- a/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/SIGUserContextImpl.java @@ -21,7 +21,6 @@ import java.sql.*; import java.util.*; import org.apache.log4j.*; import com.silverwrist.util.StringUtil; -import com.silverwrist.util.collections.*; import com.silverwrist.util.rcache.ReferencedData; import com.silverwrist.venice.db.*; import com.silverwrist.venice.security.AuditRecord; @@ -163,7 +162,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend { if (logger.isDebugEnabled()) logger.debug("setMemberValues(" + String.valueOf(granted_level) + ", " + String.valueOf(member) - + ", " + String.valueOf(locked)); + + ", " + String.valueOf(locked) + ")"); if (user.realBaseLevel()>granted_level) this.level = user.realBaseLevel(); @@ -1484,7 +1483,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend { if (logger.isDebugEnabled()) logger.debug("getMemberSIGEntries for user #" + String.valueOf(user.realUID())); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1521,7 +1520,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getMemberSIGEntries @@ -1611,7 +1610,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend + ", term '" + term + "', offset = " + String.valueOf(offset) + ", count = " + String.valueOf(count)); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1688,7 +1687,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end searchForSIGs @@ -1780,7 +1779,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend logger.debug("reading SIGs in category " + String.valueOf(catid) + ", offset = " + String.valueOf(offset) + ", count = " + String.valueOf(count)); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1823,7 +1822,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getSIGsInCategory diff --git a/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java b/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java index 717aad1..88b50c6 100644 --- a/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java @@ -23,7 +23,6 @@ import java.util.*; import java.util.zip.*; import org.apache.log4j.*; import com.silverwrist.util.StringUtil; -import com.silverwrist.util.collections.*; import com.silverwrist.venice.db.*; import com.silverwrist.venice.security.AuditRecord; import com.silverwrist.venice.security.Capability; @@ -1092,7 +1091,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext logger.debug("loadMessageRange for conf # " + conf.realConfID() + ", topic #" + topicid + ", range [" + post_low + ", " + post_high + "]"); - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); Connection conn = null; // pooled database connection try @@ -1138,7 +1137,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext } // end finally - return new ReadOnlyVector(rc); // wrap the return vector + return Collections.unmodifiableList(rc); // wrap the return vector } // end loadMessageRange diff --git a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java index 1e421f8..f9f1601 100644 --- a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java @@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl; import java.sql.*; import java.util.*; import org.apache.log4j.*; -import com.silverwrist.util.collections.*; import com.silverwrist.venice.db.*; import com.silverwrist.venice.htmlcheck.*; import com.silverwrist.venice.security.AuditRecord; @@ -511,7 +510,9 @@ class TopicUserContextImpl implements TopicContext try { // start by trying to see if we can update topicsettings directly StringBuffer sql = new StringBuffer("UPDATE topicsettings SET last_message = "); - sql.append(last_msg).append(" WHERE topicid = ").append(topicid).append(" AND uid = "); + sql.append(last_msg).append(", last_read = '"); + java.util.Date now = new java.util.Date(); + sql.append(SQLUtil.encodeDate(now)).append("' WHERE topicid = ").append(topicid).append(" AND uid = "); sql.append(conf.realUID()).append(';'); if (logger.isDebugEnabled()) logger.debug("SQL: " + sql.toString()); @@ -540,8 +541,9 @@ class TopicUserContextImpl implements TopicContext // OK, just insert a new row into topicsettings, why dontcha... sql.setLength(0); - sql.append("INSERT INTO topicsettings (topicid, uid, last_message) VALUES (").append(topicid); - sql.append(", ").append(conf.realUID()).append(", ").append(last_msg).append(");"); + sql.append("INSERT INTO topicsettings (topicid, uid, last_message, last_read) VALUES ("); + sql.append(topicid).append(", ").append(conf.realUID()).append(", ").append(last_msg).append(", '"); + sql.append(SQLUtil.encodeDate(now)).append("');"); if (logger.isDebugEnabled()) logger.debug("SQL: " + sql.toString()); stmt.executeUpdate(sql.toString()); @@ -691,7 +693,7 @@ class TopicUserContextImpl implements TopicContext // slap a lock on all the tables we need to touch stmt.executeUpdate("LOCK TABLES confs WRITE, topics WRITE, posts WRITE, postdata WRITE, " - + "confsettings WRITE, topicsettings READ;"); + + "confsettings WRITE, topicsettings WRITE;"); try { // refresh our current status and recheck allowed status @@ -755,6 +757,25 @@ class TopicUserContextImpl implements TopicContext sql.append(real_text).append("');"); stmt.executeUpdate(sql.toString()); + // mark that we posted to the topic + sql.setLength(0); + sql.append("UPDATE topicsettings SET last_post = '").append(SQLUtil.encodeDate(posted_date)); + sql.append("' WHERE topicid = ").append(topicid).append(" AND uid = ").append(conf.realUID()); + sql.append(';'); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + if (stmt.executeUpdate(sql.toString())<1) + { // we had no topicsettings record, add one + sql.setLength(0); + sql.append("INSERT INTO topicsettings (topicid, uid, last_post) VALUES (").append(topicid); + sql.append(", ").append(conf.realUID()).append(", '").append(SQLUtil.encodeDate(posted_date)); + sql.append("');"); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + stmt.executeUpdate(sql.toString()); + + } // end if + // mark that we posted to the conference conf.touchUpdate(conn,posted_date); conf.touchPost(conn,posted_date); @@ -925,6 +946,126 @@ class TopicUserContextImpl implements TopicContext } // end delete + public List getActivePosters(int skip, int limit) throws DataException + { + Connection conn = null; + ArrayList rc = new ArrayList(); + + try + { // retrieve a connection from the datapool + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // create the SQL statement to retrieve all posters + StringBuffer sql = + new StringBuffer("SELECT s.uid, u.username, s.last_read, s.last_post FROM topicsettings s, " + + "users u WHERE u.uid = s.uid AND s.topicid = "); + sql.append(topicid).append(" AND u.is_anon = 0 AND ISNULL(s.last_post) = 0 ORDER BY s.last_post DESC"); + if ((skip>=0) && (limit>0)) + sql.append(" LIMIT ").append(skip).append(", ").append(limit); + sql.append(';'); + + // execute the statement + ResultSet rs = stmt.executeQuery(sql.toString()); + + while (rs.next()) + { // return all the records as ActiveUser data elements + ActiveUser usr = new ActiveUserImpl(rs.getInt(1),rs.getString(2),SQLUtil.getFullDateTime(rs,3), + SQLUtil.getFullDateTime(rs,4)); + rc.add(usr); + + } // end while + + } // end try + catch (SQLException e) + { // this becomes a DataException + logger.error("DB error getting active poster list: " + e.getMessage(),e); + throw new DataException("unable to get active poster listing: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return Collections.unmodifiableList(rc); + + } // end getActivePosters + + public List getActivePosters(int limit) throws DataException, AccessError + { + return getActivePosters(0,limit); + + } // end getActivePosters + + public List getActivePosters() throws DataException, AccessError + { + return getActivePosters(-1,-1); + + } // end getActivePosters + + public List getActiveReaders(int skip, int limit) throws DataException, AccessError + { + Connection conn = null; + ArrayList rc = new ArrayList(); + + try + { // retrieve a connection from the datapool + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // create the SQL statement to retrieve all readers + StringBuffer sql = + new StringBuffer("SELECT s.uid, u.username, s.last_read, s.last_post FROM topicsettings s, " + + "users u WHERE u.uid = s.uid AND s.topicid = "); + sql.append(topicid).append(" AND u.is_anon = 0 AND ISNULL(s.last_read) = 0 ORDER BY s.last_read DESC"); + if ((skip>=0) && (limit>0)) + sql.append(" LIMIT ").append(skip).append(", ").append(limit); + sql.append(';'); + + // execute the statement + ResultSet rs = stmt.executeQuery(sql.toString()); + + while (rs.next()) + { // return all the records as ActiveUser data elements + ActiveUser usr = new ActiveUserImpl(rs.getInt(1),rs.getString(2),SQLUtil.getFullDateTime(rs,3), + SQLUtil.getFullDateTime(rs,4)); + rc.add(usr); + + } // end while + + } // end try + catch (SQLException e) + { // this becomes a DataException + logger.error("DB error getting active reader list: " + e.getMessage(),e); + throw new DataException("unable to get active reader listing: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return Collections.unmodifiableList(rc); + + } // end getActiveReaders + + public List getActiveReaders(int limit) throws DataException, AccessError + { + return getActiveReaders(0,limit); + + } // end getActiveReaders + + public List getActiveReaders() throws DataException, AccessError + { + return getActiveReaders(-1,-1); + + } // end getActiveReaders + /*-------------------------------------------------------------------------------- * External operations usable only from within the package *-------------------------------------------------------------------------------- @@ -936,7 +1077,7 @@ class TopicUserContextImpl implements TopicContext if (logger.isDebugEnabled()) logger.debug("getTopicList for conf # " + String.valueOf(conf.realConfID()) + ", user #" + String.valueOf(conf.realUID())); - Vector rc = new Vector(); // return from this function + ArrayList rc = new ArrayList(); // return from this function Connection conn = null; // pooled database connection try @@ -1088,7 +1229,7 @@ class TopicUserContextImpl implements TopicContext } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getTopicList diff --git a/src/com/silverwrist/venice/core/impl/UserContextImpl.java b/src/com/silverwrist/venice/core/impl/UserContextImpl.java index de536b2..c803a3f 100644 --- a/src/com/silverwrist/venice/core/impl/UserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/UserContextImpl.java @@ -22,7 +22,6 @@ import java.sql.*; import org.apache.log4j.*; import com.silverwrist.util.LocaleFactory; import com.silverwrist.util.StringUtil; -import com.silverwrist.util.collections.*; import com.silverwrist.util.rcache.ReferencedData; import com.silverwrist.venice.*; import com.silverwrist.venice.core.*; @@ -41,6 +40,9 @@ class UserContextImpl implements UserContext, UserBackend private static Category logger = Category.getInstance(UserContextImpl.class.getName()); + private static final String AUTH_TOKEN_PREFIX = "VQAT:"; + private static final char AUTH_TOKEN_SEP = '|'; + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- @@ -902,7 +904,7 @@ class UserContextImpl implements UserContext, UserBackend public List getSideBoxList() throws DataException { Connection conn = null; - Vector rc = new Vector(); + ArrayList rc = new ArrayList(); try { // retrieve a connection from the data pool @@ -934,7 +936,7 @@ class UserContextImpl implements UserContext, UserBackend } // end finally - return new ReadOnlyVector(rc); + return Collections.unmodifiableList(rc); } // end getSideBoxList @@ -1052,6 +1054,235 @@ class UserContextImpl implements UserContext, UserBackend } // end setTimeZone + public String getAuthenticationToken() throws AccessError, DataException + { + if (!isLoggedIn()) + { // can't generate an authentication token if we're not authenticated! + logger.error("UserContext not authenticated, cannot generate auth token"); + throw new AccessError("You cannot generate an authentication token without logging in."); + + } // end if + + // Generate a random authentication string and poke it into the database for this user. + String tokenauth = engine.generateRandomAuthString(); + Connection conn = null; + + try + { // retrieve a connection from the data pool + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("UPDATE users SET tokenauth = '"); + sql.append(tokenauth).append("' WHERE uid = ").append(uid).append(';'); + stmt.executeUpdate(sql.toString()); + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error setting token authentication string: " + e.getMessage(),e); + throw new DataException("Unable to set authentication token: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + // Build the full authentication token string value. + int checkvalue = uid ^ tokenauth.hashCode(); + StringBuffer buf = new StringBuffer(AUTH_TOKEN_PREFIX); + buf.append(uid).append(AUTH_TOKEN_SEP).append(tokenauth).append(AUTH_TOKEN_SEP).append(checkvalue); + buf.append(AUTH_TOKEN_SEP); + return buf.toString(); + + } // end getAuthenticationToken + + public boolean authenticateWithToken(String token) throws DataException + { + if (isLoggedIn()) + { // already authenticated, can't authenticate again + logger.error("UserContext already authenticated (with uid " + uid + ")"); + throw new InternalStateError("context already authenticated"); + + } // end if + + if (logger.isDebugEnabled()) + logger.debug("decoding authtoken: " + token); + + // Pick apart the authentication token value. + if (!(token.startsWith(AUTH_TOKEN_PREFIX))) + { // token parse error + logger.error("Token parse error: prefix not valid"); + return false; + + } // end if + + int xstart = AUTH_TOKEN_PREFIX.length(); + int xend = token.indexOf(AUTH_TOKEN_SEP,xstart); + if (xend<0) + { // could not find the UID separator + logger.error("Token parse error: UID sep not found"); + return false; + + } // end if + + int pending_uid; + try + { // get the user ID + pending_uid = Integer.parseInt(token.substring(xstart,xend)); + + } // end try + catch (NumberFormatException nfe) + { // we couldn't parse the UID + logger.error("Token parse error: invalid UID value"); + return false; + + } // end catch + + xstart = xend + 1; + xend = token.indexOf(AUTH_TOKEN_SEP,xstart); + if (xend<0) + { // could not find the auth string separator + logger.error("Token parse error: auth string sep not found"); + return false; + + } // end if + + String pending_auth = token.substring(xstart,xend); + if (!(engine.isValidRandomAuthString(pending_auth))) + { // the auth string is not valid by the rules under which it was generated + logger.error("Token parse error: invalid auth string value"); + return false; + + } // end if + + xstart = xend + 1; + xend = token.indexOf(AUTH_TOKEN_SEP,xstart); + if (xend<0) + { // could not find the checkvalue separator + logger.error("Token parse error: checkvalue sep not found"); + return false; + + } // end if + + int checkvalue; + try + { // get the check value + checkvalue = Integer.parseInt(token.substring(xstart,xend)); + + } // end try + catch (NumberFormatException nfe) + { // we couldn't parse the checkvalue + logger.error("Token parse error: invalid checkvalue"); + return false; + + } // end catch + + if (checkvalue!=(pending_uid ^ pending_auth.hashCode())) + { // the checkvalue does not match what it should - possible corrupted token + logger.error("Token parse error: checkvalue does not match"); + return false; + + } // end if + + // At this point, we now have a UID and authentication string extracted from the token. + // Proceed to authenticate. + if (logger.isDebugEnabled()) + logger.debug("Authenticating user ID#" + pending_uid); + + Connection conn = null; + AuditRecord ar = null; + + try + { // look for a user record matching this user ID + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE uid = " + pending_uid + ";"); + + if (!(rs.next())) + { // user not found + logger.error("...user not found"); + ar = new AuditRecord(AuditRecord.LOGIN_FAIL,0,remote_addr,"Bad token UID: " + pending_uid); + return false; + + } // end if + + if (rs.getBoolean("is_anon")) + { // can't log in as Anonymous Honyak + logger.error("...user is the Anonymous Honyak, can't explicitly login"); + ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Anonymous user"); + return false; + + } // end if + + if (rs.getBoolean("lockout")) + { // account locked out + logger.error("...user is locked out by the Admin"); + ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Account locked out"); + return false; + + } // end if + + // compare the stored token auth value to what we have + if (!(pending_auth.equals(rs.getString("tokenauth")))) + { // the auth string is bad - we can't log in + logger.warn("...invalid authentication string"); + ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Bad auth-string"); + return false; + + } // end if + + if (logger.isDebugEnabled()) + logger.debug("...authenticated"); + + // we're authenticated - load the user data into the context + loadUserData(rs); + + // update the "last access" time in the database + java.util.Date mydate = new java.util.Date(); + stmt.executeUpdate("UPDATE users SET lastaccess = '" + SQLUtil.encodeDate(mydate) + + "' WHERE uid = " + uid + ";"); + + // update the "last access" time in this object + last_access = mydate; + + // an audit record indicating we logged in OK + ar = new AuditRecord(AuditRecord.LOGIN_OK,uid,remote_addr); + + if (logger.isDebugEnabled()) + logger.debug("...context loaded, we're ready :-)"); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("DB error reading user data: " + e.getMessage(),e); + throw new DataException("unable to access user data: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + try + { // save off the audit record before we go, though + if ((ar!=null) && (conn!=null)) + ar.store(conn); + + } // end try + catch (SQLException e) + { // we couldn't store the audit record! + logger.error("DB error saving audit record: " + e.getMessage(),e); + + } // end catch + + if (conn!=null) + datapool.releaseConnection(conn); + + } // end if + + return true; // token authentication worked! + + } // end authenticateWithToken + /*-------------------------------------------------------------------------------- * 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 ff4d6ab..24997bd 100644 --- a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java +++ b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java @@ -23,7 +23,6 @@ import org.apache.log4j.*; import org.w3c.dom.*; import com.silverwrist.util.StringUtil; import com.silverwrist.util.DOMElementHelper; -import com.silverwrist.util.collections.*; import com.silverwrist.util.rcache.*; import com.silverwrist.venice.core.*; import com.silverwrist.venice.db.*; @@ -385,6 +384,10 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend private static Category logger = Category.getInstance(VeniceEngineImpl.class.getName()); + private static final String AUTH_ALPHABET = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"; + private static final int AUTH_STRING_LEN = 32; + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- @@ -406,7 +409,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend private int[] gp_ints; // global integer parameters private MasterSideBox[] sideboxes; // master sidebox table private Hashtable sidebox_ids = new Hashtable(); // maps sidebox IDs to MasterSideBox objects - private Vector cache_fp_posts = new Vector(); // all posts that have been published to front page + private LinkedList cache_fp_posts = new LinkedList(); // all posts that have been published to front page private boolean cache_fp_posts_busy = false; // busy flag for above vector /*-------------------------------------------------------------------------------- @@ -482,7 +485,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend this.config = config; - Vector dictionary_tmp; + ArrayList dictionary_tmp; try { // first, verify that this is a valid configuration Element root = config.getDocumentElement(); @@ -542,7 +545,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end if // Retrieve the list of dictionary files to load into the spellchecker. - dictionary_tmp = new Vector(); + dictionary_tmp = new ArrayList(); NodeList dict_nodes = dict_sect.getChildNodes(); for (i=0; i element looking for elements @@ -625,15 +628,13 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend logger.debug(max_value + " features loaded from database"); // load the master sidebox table - Vector sidebox_tmp = new Vector(); + ArrayList sidebox_tmp = new ArrayList(); rs = stmt.executeQuery("SELECT * FROM refsidebox ORDER BY boxid;"); while (rs.next()) sidebox_tmp.add(new MasterSideBox(rs)); // store the real master sidebox table as an array - sideboxes = new MasterSideBox[sidebox_tmp.size()]; - for (i=0; igp_ints[IP_NUMFRONTPAGEPOSTS]) - cache_fp_posts.remove(cache_fp_posts.size()-1); + cache_fp_posts.removeLast(); } // end pubmsg @@ -1962,4 +1960,28 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end unpublish + public String generateRandomAuthString() + { + StringBuffer buf = new StringBuffer(AUTH_STRING_LEN); + for (int i=0; i. + * + * 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; + +import java.io.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.log4j.*; +import com.silverwrist.venice.core.*; +import com.silverwrist.venice.servlets.format.*; + +public class FrameStatic extends VeniceServlet +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(FrameStatic.class); + + /*-------------------------------------------------------------------------------- + * Overrides from class HttpServlet + *-------------------------------------------------------------------------------- + */ + + public String getServletInfo() + { + String rc = "FrameStatic servlet - Displays a static page inside the Venice \"frame\"\n" + + "Part of the Venice Web Communities System\n"; + return rc; + + } // end getServletInfo + + /*-------------------------------------------------------------------------------- + * Overrides from class VeniceServlet + *-------------------------------------------------------------------------------- + */ + + protected VeniceContent doVeniceGet(HttpServletRequest request, VeniceEngine engine, + UserContext user, RenderData rdat) + throws ServletException, IOException, VeniceServletResult + { + setMyLocation(request,"frame" + request.getPathInfo()); + return StaticRender.getStaticRender(request.getPathInfo().substring(1)); + + } // end doVeniceGet + +} // end class FrameStatic diff --git a/src/com/silverwrist/venice/servlets/UserDisplay.java b/src/com/silverwrist/venice/servlets/UserDisplay.java index ea0c8b2..fc2fc19 100644 --- a/src/com/silverwrist/venice/servlets/UserDisplay.java +++ b/src/com/silverwrist/venice/servlets/UserDisplay.java @@ -31,7 +31,7 @@ public class UserDisplay extends VeniceServlet *-------------------------------------------------------------------------------- */ - private static Category logger = Category.getInstance(UserDisplay.class.getName()); + private static Category logger = Category.getInstance(UserDisplay.class); /*-------------------------------------------------------------------------------- * Overrides from class HttpServlet diff --git a/src/com/silverwrist/venice/servlets/Variables.java b/src/com/silverwrist/venice/servlets/Variables.java index 4b64693..6f18b67 100644 --- a/src/com/silverwrist/venice/servlets/Variables.java +++ b/src/com/silverwrist/venice/servlets/Variables.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 @@ -23,6 +23,7 @@ import javax.servlet.http.*; import org.apache.log4j.*; import com.silverwrist.venice.core.*; import com.silverwrist.venice.servlets.format.*; +import com.silverwrist.venice.servlets.format.menus.LeftMenu; public class Variables { @@ -35,19 +36,21 @@ public class Variables protected static final String ENGINE_ATTRIBUTE = "com.silverwrist.venice.core.Engine"; protected static final String COUNTRYLIST_ATTRIBUTE = "com.silverwrist.venice.db.CountryList"; protected static final String LANGUAGELIST_ATTRIBUTE = "com.silverwrist.venice.db.LanguageList"; - protected static final String TOPMENU_ATTRIBUTE = "com.silverwrist.venice.servlets.MenuTop"; // HttpSession ("session") attributes protected static final String USERCTXT_ATTRIBUTE = "user.context"; protected static final String MENU_ATTRIBUTE = "current.menu"; + // ServletRequest ("request" attributes) + protected static final String COOKIEJAR_ATTRIBUTE = "com.silverwrist.venice.servlets.CookieJar"; + // Servlet initialization parameters protected static final String ENGINE_INIT_PARAM = "venice.config"; // Cookie name - public static final String LOGIN_COOKIE = "VeniceLogin"; + public static final String LOGIN_COOKIE = "VeniceAuth"; - private static Category logger = Category.getInstance(Variables.class.getName()); + private static Category logger = Category.getInstance(Variables.class); private static Integer engine_gate = new Integer(0); /*-------------------------------------------------------------------------------- @@ -90,7 +93,8 @@ public class Variables } // end getVeniceEngine - public static UserContext getUserContext(ServletContext ctxt, ServletRequest request, HttpSession session) + public static UserContext getUserContext(ServletContext ctxt, HttpServletRequest request, + HttpSession session) throws ServletException { Object uctmp = session.getAttribute(USERCTXT_ATTRIBUTE); @@ -101,6 +105,33 @@ public class Variables { // use the Venice engine to create a new user context and save it off VeniceEngine engine = getVeniceEngine(ctxt); UserContext user = engine.createUserContext(request.getRemoteAddr()); + + // Did the user send a Venice authentication cookie? If so, try to use it. + Cookie[] cookies = request.getCookies(); + Cookie venice_cookie = null; + for (int i=0; (venice_cookie==null) && (i\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/BaseJSPData.java b/src/com/silverwrist/venice/servlets/format/BaseJSPData.java index 0dd516c..8889fe7 100644 --- a/src/com/silverwrist/venice/servlets/format/BaseJSPData.java +++ b/src/com/silverwrist/venice/servlets/format/BaseJSPData.java @@ -22,6 +22,8 @@ import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; import org.apache.log4j.*; +import com.silverwrist.venice.servlets.Variables; +import com.silverwrist.venice.servlets.format.menus.LeftMenu; public class BaseJSPData { @@ -114,6 +116,22 @@ public class BaseJSPData } // end transfer + public void renderMenu(HttpSession session, Writer out, RenderData rdat) throws IOException + { + ComponentRender menu = Variables.getMenu(session); + if (menu==null) + menu = (ComponentRender)(rdat.getLeftMenu("top")); + menu.renderHere(out,rdat); + + } // end renderMenu + + public void renderFixedMenu(Writer out, RenderData rdat) throws IOException + { + ComponentRender menu = (ComponentRender)(rdat.getLeftMenu("fixed")); + menu.renderHere(out,rdat); + + } // end renderFixedMenu + public void renderContent(ServletContext ctxt, Writer out, RenderData rdat) throws IOException, ServletException { @@ -142,7 +160,7 @@ public class BaseJSPData rdat.flushOutput(); // now make sure the included page is properly flushed return; - } // end if + } // end else if else // this is the fallback if we don't recognize the content new ErrorBox(null,"Internal Error: Content of invalid type",null).renderHere(out,rdat); diff --git a/src/com/silverwrist/venice/servlets/format/ConferenceActivity.java b/src/com/silverwrist/venice/servlets/format/ConferenceActivity.java index 9745dc5..5491c21 100644 --- a/src/com/silverwrist/venice/servlets/format/ConferenceActivity.java +++ b/src/com/silverwrist/venice/servlets/format/ConferenceActivity.java @@ -39,24 +39,39 @@ public class ConferenceActivity implements JSPRender private SIGContext sig; // the SIG we're in private ConferenceContext conf; // the conference being listed + private TopicContext topic; // the topic being listed private boolean posters; // is this a list of posters? private List records; // the actual data records + private String locator = null; // our locator /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ - public ConferenceActivity(SIGContext sig, ConferenceContext conf, boolean posters) + public ConferenceActivity(SIGContext sig, ConferenceContext conf, TopicContext topic, boolean posters) throws DataException, AccessError { this.sig = sig; this.conf = conf; + this.topic = topic; this.posters = posters; - if (posters) - this.records = conf.getActivePosters(); + if (topic!=null) + { // do the report on the topic + if (posters) + this.records = topic.getActivePosters(); + else + this.records = topic.getActiveReaders(); + + } // end if else - this.records = conf.getActiveReaders(); + { // do the report on the conference + if (posters) + this.records = conf.getActivePosters(); + else + this.records = conf.getActiveReaders(); + + } // end else } // end constructor @@ -78,10 +93,22 @@ public class ConferenceActivity implements JSPRender public String getPageTitle(RenderData rdat) { - if (posters) - return "Users Posting in Conference " + conf.getName(); + if (topic!=null) + { // it's a topic report + if (posters) + return "Users Posting in Topic " + topic.getName(); + else + return "Users Reading Topic " + topic.getName(); + + } // end if else - return "Users Reading Conference " + conf.getName(); + { // it's a conference report + if (posters) + return "Users Posting in Conference " + conf.getName(); + else + return "Users Reading Conference " + conf.getName(); + + } // end else } // end getPageTitle @@ -113,12 +140,29 @@ public class ConferenceActivity implements JSPRender } // end getConfName + public String getTopicName() + { + if (topic==null) + return null; + else + return topic.getName(); + + } // end getTopicName + public String getLocator() { - return "sig=" + sig.getSIGID() + "&conf=" + conf.getConfID(); + if (locator==null) + locator = "sig=" + sig.getSIGID() + "&conf=" + conf.getConfID(); + return locator; } // end getLocator + public boolean isTopicReport() + { + return (topic!=null); + + } // end isTopicReport + public boolean isPosterReport() { return posters; diff --git a/src/com/silverwrist/venice/servlets/format/ConfirmBox.java b/src/com/silverwrist/venice/servlets/format/ConfirmBox.java index 07bf881..b8a2c24 100644 --- a/src/com/silverwrist/venice/servlets/format/ConfirmBox.java +++ b/src/com/silverwrist/venice/servlets/format/ConfirmBox.java @@ -122,7 +122,6 @@ public class ConfirmBox implements ContentRender out.write("\"No\"\n"); out.write("

\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/ContentDialog.java b/src/com/silverwrist/venice/servlets/format/ContentDialog.java index af5a878..81e30b8 100644 --- a/src/com/silverwrist/venice/servlets/format/ContentDialog.java +++ b/src/com/silverwrist/venice/servlets/format/ContentDialog.java @@ -216,7 +216,6 @@ public class ContentDialog implements Cloneable, ContentRender } // end if out.write("\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/ContentMenuPanel.java b/src/com/silverwrist/venice/servlets/format/ContentMenuPanel.java index 0eb44cd..f5172fd 100644 --- a/src/com/silverwrist/venice/servlets/format/ContentMenuPanel.java +++ b/src/com/silverwrist/venice/servlets/format/ContentMenuPanel.java @@ -135,7 +135,6 @@ public class ContentMenuPanel implements Cloneable, ContentRender } // end while out.write("\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/ErrorBox.java b/src/com/silverwrist/venice/servlets/format/ErrorBox.java index 6d32252..96d8321 100644 --- a/src/com/silverwrist/venice/servlets/format/ErrorBox.java +++ b/src/com/silverwrist/venice/servlets/format/ErrorBox.java @@ -78,7 +78,6 @@ public class ErrorBox extends VeniceServletResult implements ContentRender else out.write("Go back.\n"); out.write("

\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/LoginDialog.java b/src/com/silverwrist/venice/servlets/format/LoginDialog.java index 68d3172..b908dd5 100644 --- a/src/com/silverwrist/venice/servlets/format/LoginDialog.java +++ b/src/com/silverwrist/venice/servlets/format/LoginDialog.java @@ -37,8 +37,8 @@ public class LoginDialog extends ContentDialog addFormField(new CDPasswordFormFieldCommand("pass","Password",null,false,32,128, new CDImageButton("remind","bn_reminder.gif","Reminder", 80,24))); - //addFormField(new CDCheckBoxFormField("saveme","Save my user name and password for automatic logins", - // null,"Y")); + addFormField(new CDCheckBoxFormField("saveme","Remember me for next time so I can log in automatically", + null,"Y")); addCommandButton(new CDImageButton("login","bn_log_in.gif","Log In",80,24)); addCommandButton(new CDImageButton("cancel","bn_cancel.gif","Cancel",80,24)); diff --git a/src/com/silverwrist/venice/servlets/format/ManageConference.java b/src/com/silverwrist/venice/servlets/format/ManageConference.java index 7ea12a3..5d757e7 100644 --- a/src/com/silverwrist/venice/servlets/format/ManageConference.java +++ b/src/com/silverwrist/venice/servlets/format/ManageConference.java @@ -39,6 +39,7 @@ public class ManageConference implements JSPRender private SIGContext sig; // the SIG we're in private ConferenceContext conf; // the conference being listed + private String locator = null; // the locator we use /*-------------------------------------------------------------------------------- * Constructor @@ -116,7 +117,9 @@ public class ManageConference implements JSPRender public String getLocator() { - return "sig=" + sig.getSIGID() + "&conf=" + conf.getConfID(); + if (locator==null) + locator = "sig=" + sig.getSIGID() + "&conf=" + conf.getConfID(); + return locator; } // end getLocator @@ -128,8 +131,7 @@ public class ManageConference implements JSPRender public boolean displayAdminSection() { - return conf.canChangeConference(); - // TODO: needs to have "delete" permission OR'ed in + return conf.canChangeConference() || conf.canDeleteConference(); } // end displayAdminSection diff --git a/src/com/silverwrist/venice/servlets/format/RenderConfig.java b/src/com/silverwrist/venice/servlets/format/RenderConfig.java index 1f45dbe..b634a3a 100644 --- a/src/com/silverwrist/venice/servlets/format/RenderConfig.java +++ b/src/com/silverwrist/venice/servlets/format/RenderConfig.java @@ -31,6 +31,7 @@ import com.silverwrist.util.StringUtil; import com.silverwrist.venice.core.ConfigException; import com.silverwrist.venice.core.UserContext; import com.silverwrist.venice.servlets.Variables; +import com.silverwrist.venice.servlets.format.menus.LeftMenu; public class RenderConfig { @@ -57,7 +58,8 @@ public class RenderConfig private String image_url; private String static_url; private String site_logo; - private Hashtable stock_messages; + private HashMap stock_messages; + private HashMap menus; /*-------------------------------------------------------------------------------- * Constructor @@ -171,9 +173,10 @@ public class RenderConfig } // end if // Initialize the stock messages list. - stock_messages = new Hashtable(); + stock_messages = new HashMap(); NodeList msg_nodes = msg_sect.getChildNodes(); - for (int i=0; i section - bail out now! + logger.fatal("config document has no section"); + throw new ConfigException("no section found in config file",root); + + } // end if + + // Initialize the menus list. + menus = new HashMap(); + NodeList menu_nodes = menu_sect.getChildNodes(); + for (i=0; i subnodes and use them to initialize menus + Node mn = menu_nodes.item(i); + if (mn.getNodeType()==Node.ELEMENT_NODE) + { // found an element - now check it's name + if (mn.getNodeName().equals("menudef")) + { // root of a menu definition - get its ID, build it, and save it + Element mel = (Element)mn; + String menuid = mel.getAttribute("id"); + if (menuid==null) + { // no menu ID attribute + logger.fatal(" seen with no \"id\" attribute"); + throw new ConfigException(" seen with no \"id\" attribute",mel); + + } // end if + + // create the menu and add it to the mapping + LeftMenu menu = new LeftMenu(mel,menuid); + menus.put(menuid,menu); + if (logger.isDebugEnabled()) + logger.debug("menu \"" + menuid + "\" defined"); + + } // end if (found the root of a menu definition) + else + { // unknown element - bail out! + logger.fatal("config document has unknown node <" + mn.getNodeName() + + "/> inside "); + throw new ConfigException("unknown node name <" + mn.getNodeName() + "/> in ", + menu_sect); + + } // end else + + } // end if + + } // end for + + if (logger.isDebugEnabled()) + logger.debug(menus.size() + " menu definitions loaded from config"); + } // end constructor /*-------------------------------------------------------------------------------- @@ -337,20 +390,6 @@ public class RenderConfig } // end getRequiredBullet - void writeFooter(Writer out) throws IOException - { - out.write("


\n" - + "\n\n\n" - + "
\n"); - out.write(getStdFontTag(null,1)); - out.write(getStockMessage("footer-text")); - out.write("\n\n" - + "\"Powered\n
\n"); - - } // end writeFooter - void writeContentHeader(Writer out, String primary, String secondary) throws IOException { out.write(getStdFontTag("#3333AA",5) + "" + StringUtil.encodeHTML(primary) + ""); @@ -374,6 +413,12 @@ public class RenderConfig } // end writeStockMessage + public LeftMenu getLeftMenu(String identifier) + { + return (LeftMenu)(menus.get(identifier)); + + } // end getLeftMenu + /*-------------------------------------------------------------------------------- * Static operations for use by VeniceServlet *-------------------------------------------------------------------------------- @@ -410,14 +455,14 @@ public class RenderConfig HttpServletResponse response) throws ServletException { UserContext uc = Variables.getUserContext(ctxt,request,request.getSession(true)); - return new RenderData(getRenderConfig(ctxt),uc,request,response); + return new RenderData(getRenderConfig(ctxt),uc,ctxt,request,response); } // end createRenderData public static RenderData createRenderData(ServletContext ctxt, UserContext uc, HttpServletRequest request, HttpServletResponse response) throws ServletException { - return new RenderData(getRenderConfig(ctxt),uc,request,response); + return new RenderData(getRenderConfig(ctxt),uc,ctxt,request,response); } // end createRenderData diff --git a/src/com/silverwrist/venice/servlets/format/RenderData.java b/src/com/silverwrist/venice/servlets/format/RenderData.java index 044ec9d..351c114 100644 --- a/src/com/silverwrist/venice/servlets/format/RenderData.java +++ b/src/com/silverwrist/venice/servlets/format/RenderData.java @@ -29,6 +29,7 @@ import com.silverwrist.venice.core.IDUtils; import com.silverwrist.venice.core.UserContext; import com.silverwrist.venice.db.PostLinkRewriter; import com.silverwrist.venice.db.UserNameRewriter; +import com.silverwrist.venice.servlets.format.menus.LeftMenu; public class RenderData { @@ -47,6 +48,7 @@ public class RenderData */ private RenderConfig rconf; + private ServletContext ctxt; private HttpServletRequest request; private HttpServletResponse response; private boolean can_gzip = false; @@ -60,9 +62,11 @@ public class RenderData *-------------------------------------------------------------------------------- */ - RenderData(RenderConfig rconf, UserContext uc, HttpServletRequest request, HttpServletResponse response) + RenderData(RenderConfig rconf, UserContext uc, ServletContext ctxt, HttpServletRequest request, + HttpServletResponse response) { this.rconf = rconf; + this.ctxt = ctxt; this.request = request; this.response = response; @@ -167,7 +171,13 @@ public class RenderData { return "/format/" + name; - } // end getFullFormatJSPPath + } // end getFormatJSPPath + + public String getStaticIncludePath(String name) + { + return "/static/" + name; + + } // end getStaticIncludePath public String getStdFontTag(String color, int size) { @@ -193,12 +203,6 @@ public class RenderData } // end getRequiredBullet - public void writeFooter(Writer out) throws IOException - { - rconf.writeFooter(out); - - } // end writeFooter - public void writeContentHeader(Writer out, String primary, String secondary) throws IOException { rconf.writeContentHeader(out,primary,secondary); @@ -250,6 +254,12 @@ public class RenderData } // end writeStockMessage + public LeftMenu getLeftMenu(String identifier) + { + return rconf.getLeftMenu(identifier); + + } // end getLeftMenu + public String formatDateForDisplay(Date date) { if (display_date==null) @@ -491,4 +501,19 @@ public class RenderData } // end rewritePostData + public String mapToPath(String path) + { + return ctxt.getRealPath(path); + + } // end mapToPath + + public Cookie createCookie(String name, String value, int age) + { + Cookie rc = new Cookie(name,value); + rc.setMaxAge(age); + rc.setPath(request.getContextPath()); + return rc; + + } // end createCookie + } // end class RenderData diff --git a/src/com/silverwrist/venice/servlets/format/ReportConferenceMenu.java b/src/com/silverwrist/venice/servlets/format/ReportConferenceMenu.java new file mode 100644 index 0000000..eb21b55 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/ReportConferenceMenu.java @@ -0,0 +1,128 @@ +/* + * 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.util.*; +import javax.servlet.*; +import javax.servlet.http.*; +import com.silverwrist.venice.core.*; + +public class ReportConferenceMenu implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.ReportConferenceMenu"; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private SIGContext sig; // the SIG we're in + private ConferenceContext conf; // the conference being listed + private List topics; // the topics in this conference + private String locator = null; // the locator + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public ReportConferenceMenu(SIGContext sig, ConferenceContext conf) throws DataException, AccessError + { + this.sig = sig; + this.conf = conf; + this.topics = conf.getTopicList(ConferenceContext.GET_ALL,ConferenceContext.SORT_NUMBER); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static ReportConferenceMenu retrieve(ServletRequest request) + { + return (ReportConferenceMenu)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface VeniceContent + *-------------------------------------------------------------------------------- + */ + + public String getPageTitle(RenderData rdat) + { + return "Conference Reports: " + conf.getName(); + + } // end getPageTitle + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "report_conf.jsp"; + + } // end getTargetJSPName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public int getConfID() + { + return conf.getConfID(); + + } // end getConfID + + public String getConfName() + { + return conf.getName(); + + } // end getConfName + + public String getLocator() + { + if (locator==null) + locator = "sig=" + sig.getSIGID() + "&conf=" + conf.getConfID(); + return locator; + + } // end getLocator + + public Iterator getTopics() + { + return topics.iterator(); + + } // end getTopics + +} // end class ReportConferenceMenu diff --git a/src/com/silverwrist/venice/servlets/format/StaticRender.java b/src/com/silverwrist/venice/servlets/format/StaticRender.java new file mode 100644 index 0000000..f929232 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/StaticRender.java @@ -0,0 +1,184 @@ +/* + * 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 com.silverwrist.util.cachemap.CacheMap; + +public class StaticRender implements ContentRender +{ + /*-------------------------------------------------------------------------------- + * Static data values + *-------------------------------------------------------------------------------- + */ + + private static CacheMap cache = new CacheMap(15,25); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private String name; + private boolean processed = false; + private String title = null; + private String content = null; + + /*-------------------------------------------------------------------------------- + * Static data values + *-------------------------------------------------------------------------------- + */ + + protected StaticRender(String name) + { + this.name = name; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + + private static String searchBetweenTags(StringBuffer data, String search, String tagname) + { + tagname = tagname.toUpperCase(); + String start = "<" + tagname; + String end = ""; + + int startpos = search.indexOf(start); + if (startpos<0) + return null; + startpos += start.length(); + + int bkt_pos = search.indexOf('>',startpos); + if (bkt_pos<0) + return null; + + int end_pos = search.indexOf(end,++bkt_pos); + if (end_pos<0) + return data.substring(bkt_pos); + else + return data.substring(bkt_pos,end_pos); + + } // end searchBetweenTags + + private synchronized void process(RenderData rdat) + { + if (processed) + return; // check and set flag + + // Map the content path to a real filename. + String real_path = rdat.mapToPath(rdat.getStaticIncludePath(name)); + if (real_path==null) + { // not found! + title = name; + content = "File not mappable: " + name; + return; + + } // end if + + // Read in the whole thing. + StringBuffer raw_file = new StringBuffer(); + try + { // read in from the file + FileReader rdr = new FileReader(real_path); + char[] buffer = new char[4096]; + int rd = rdr.read(buffer); + while (rd>=0) + { // read in the raw characters + if (rd>0) + raw_file.append(buffer,0,rd); + rd = rdr.read(buffer); + + } // end while + + rdr.close(); + + } // end try + catch (IOException ioe) + { // I/O exception - just discard + title = name; + content = "I/O error reading " + name + ": " + ioe.getMessage(); + return; + + } // end catch + + // make the upper-case search page and use that to locate the page title and body + String search_page = raw_file.toString().toUpperCase(); + title = searchBetweenTags(raw_file,search_page,"TITLE"); + content = searchBetweenTags(raw_file,search_page,"BODY"); + if (content==null) + { // no content? + content = "No content seen on " + name; + processed = false; + + } // end if + + processed = true; // set the flag to indicate we've got everything + + } // end process + + /*-------------------------------------------------------------------------------- + * Implementations from interface VeniceContent + *-------------------------------------------------------------------------------- + */ + + public String getPageTitle(RenderData rdat) + { + process(rdat); + if (title==null) + return name; + else + return title; + + } // end getPageTitle + + /*-------------------------------------------------------------------------------- + * Implementations from interface ContentRender + *-------------------------------------------------------------------------------- + */ + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + process(rdat); + if (content!=null) + out.write(content); + + } // end renderHere + + /*-------------------------------------------------------------------------------- + * External static operations + *-------------------------------------------------------------------------------- + */ + + public static StaticRender getStaticRender(String name) + { + StaticRender rc = (StaticRender)(cache.get(name)); + if (rc==null) + { // create a new object and cache it + rc = new StaticRender(name); + cache.put(name,rc); + + } // end if + + return rc; + + } // end getStaticRender + +} // end class StaticRender diff --git a/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java b/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java index 89d8314..82118fd 100644 --- a/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java +++ b/src/com/silverwrist/venice/servlets/format/TextMessageDialog.java @@ -149,7 +149,6 @@ public class TextMessageDialog implements ContentRender } // end for out.write("\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/venice/servlets/format/TopDisplay.java b/src/com/silverwrist/venice/servlets/format/TopDisplay.java index 35e63ab..aa1b72a 100644 --- a/src/com/silverwrist/venice/servlets/format/TopDisplay.java +++ b/src/com/silverwrist/venice/servlets/format/TopDisplay.java @@ -243,7 +243,6 @@ public class TopDisplay implements ContentRender // Finish up. out.write("\n"); - rdat.writeFooter(out); } // end renderHere diff --git a/src/com/silverwrist/util/collections/ReadOnlyVector.java b/src/com/silverwrist/venice/servlets/format/menus/AbsoluteLeftMenuItem.java similarity index 64% rename from src/com/silverwrist/util/collections/ReadOnlyVector.java rename to src/com/silverwrist/venice/servlets/format/menus/AbsoluteLeftMenuItem.java index 07022c1..22e0572 100644 --- a/src/com/silverwrist/util/collections/ReadOnlyVector.java +++ b/src/com/silverwrist/venice/servlets/format/menus/AbsoluteLeftMenuItem.java @@ -15,58 +15,43 @@ * * Contributor(s): */ -package com.silverwrist.util.collections; +package com.silverwrist.venice.servlets.format.menus; -import java.util.*; +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.venice.servlets.format.RenderData; -public class ReadOnlyVector extends AbstractList +class AbsoluteLeftMenuItem extends LeftMenuItem { /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ - private Vector my_vec; // local vector + String url; /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ - public ReadOnlyVector(Vector vec) + AbsoluteLeftMenuItem(Element elt) { - my_vec = vec; - my_vec.trimToSize(); + super(elt); + DOMElementHelper h = new DOMElementHelper(elt); + url = h.getSubElementText("absolute"); } // end constructor /*-------------------------------------------------------------------------------- - * finalize() method + * Overrides from class LeftMenuItem *-------------------------------------------------------------------------------- */ - protected void finalize() throws Throwable + protected void appendURL(StringBuffer sbuf, RenderData rdat) { - my_vec = null; - super.finalize(); + sbuf.append(url); - } // end finalize + } // end appendURL - /*-------------------------------------------------------------------------------- - * Implementations from superclass AbstractList - *-------------------------------------------------------------------------------- - */ - - public Object get(int index) - { - return my_vec.elementAt(index); - - } // end get - - public int size() - { - return my_vec.size(); - - } // end size - -} // end class ReadOnlyVector +} // end class AbsoluteLeftMenuItem diff --git a/src/com/silverwrist/venice/servlets/format/menus/FrameLeftMenuItem.java b/src/com/silverwrist/venice/servlets/format/menus/FrameLeftMenuItem.java new file mode 100644 index 0000000..ec0fd32 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/menus/FrameLeftMenuItem.java @@ -0,0 +1,57 @@ +/* + * 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.menus; + +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.venice.servlets.format.RenderData; + +class FrameLeftMenuItem extends LeftMenuItem +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + String url; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + FrameLeftMenuItem(Element elt) + { + super(elt); + DOMElementHelper h = new DOMElementHelper(elt); + url = h.getSubElementText("frame"); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Overrides from class LeftMenuItem + *-------------------------------------------------------------------------------- + */ + + protected void appendURL(StringBuffer sbuf, RenderData rdat) + { + sbuf.append(rdat.getEncodedServletPath("frame/" + url)); + + } // end appendURL + +} // end class FrameLeftMenuItem diff --git a/src/com/silverwrist/venice/servlets/format/menus/LeftMenu.java b/src/com/silverwrist/venice/servlets/format/menus/LeftMenu.java new file mode 100644 index 0000000..1748894 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/menus/LeftMenu.java @@ -0,0 +1,188 @@ +/* + * 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.menus; + +import java.io.Writer; +import java.io.IOException; +import java.util.*; +import org.apache.log4j.*; +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.venice.core.ConfigException; +import com.silverwrist.venice.servlets.format.ComponentRender; +import com.silverwrist.venice.servlets.format.RenderData; + +public class LeftMenu implements ComponentRender +{ + /*-------------------------------------------------------------------------------- + * Internal class representing a header component + *-------------------------------------------------------------------------------- + */ + + static class Header implements ComponentRender + { + private String txt; // the actual stored text + + Header(Element elt) + { + DOMElementHelper h = new DOMElementHelper(elt); + StringBuffer buf = new StringBuffer(""); + buf.append(StringUtil.encodeHTML(h.getElementText())).append("
\n"); + txt = buf.toString(); + + } // end constructor + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + out.write(txt); + + } // end renderHere + + } // end class Header + + /*-------------------------------------------------------------------------------- + * Internal class representing a separator component + *-------------------------------------------------------------------------------- + */ + + static class Separator implements ComponentRender + { + Separator() + { // do nothing + } // end constructor + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + out.write("
\n"); + + } // end renderHere + + } // end class Separator + + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(LeftMenu.class); + private static final Separator separator_singleton = new Separator(); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private String identifier; + private ArrayList menu_items = new ArrayList(); + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public LeftMenu(Element elt, String identifier) throws ConfigException + { + if (!(elt.getNodeName().equals("menudef"))) + { // just some shorts-checking here to make sure the element is OK + logger.fatal("huh?!? this should have been a if it got here!"); + throw new ConfigException("not a element"); + + } // end if + + NodeList items = elt.getChildNodes(); + for (int i=0; i element has no subelement"); + throw new ConfigException(" element has no subelement",h.getElement()); + + } // end if + + LeftMenuItem mitem = null; + if (h.hasChildElement("servlet")) + mitem = new ServletLeftMenuItem(h.getElement()); + else if (h.hasChildElement("absolute")) + mitem = new AbsoluteLeftMenuItem(h.getElement()); + else if (h.hasChildElement("frame")) + mitem = new FrameLeftMenuItem(h.getElement()); + else + { // we don't know what type of menu this is! + logger.fatal("unknown type seen in menu"); + throw new ConfigException("unknown type seen in menu",h.getElement()); + + } // end else + + menu_items.add(mitem); + + } // end if + else if (n.getNodeName().equals("header")) + menu_items.add(new Header((Element)n)); // add a new header + else if (n.getNodeName().equals("separator")) + menu_items.add(separator_singleton); // all separators are exactly the same + else + { // menu definition has an unknown item + logger.fatal("unknown element <" + n.getNodeName() + "/> inside "); + throw new ConfigException("unknown element <" + n.getNodeName() + "/> inside ",n); + + } // end else + + } // end if (an element node) + + } // end for (each child node) + + this.identifier = identifier; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Implementations from interface ComponentRender + *-------------------------------------------------------------------------------- + */ + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + Iterator it = menu_items.iterator(); + while (it.hasNext()) + { // render each menu item in turn + ComponentRender cr = (ComponentRender)(it.next()); + cr.renderHere(out,rdat); + + } // end while + + } // end renderHere + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public String getIdentifier() + { + return identifier; + + } // end getIdentifier + +} // end class LeftMenu + diff --git a/src/com/silverwrist/venice/servlets/format/menus/LeftMenuItem.java b/src/com/silverwrist/venice/servlets/format/menus/LeftMenuItem.java new file mode 100644 index 0000000..f3da3ab --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/menus/LeftMenuItem.java @@ -0,0 +1,93 @@ +/* + * 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.menus; + +import java.io.Writer; +import java.io.IOException; +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.venice.servlets.format.ComponentRender; +import com.silverwrist.venice.servlets.format.RenderData; + +abstract class LeftMenuItem implements ComponentRender +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private String text; + private boolean disabled = false; + private boolean new_window = false; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + protected LeftMenuItem(Element elt) + { + DOMElementHelper h = new DOMElementHelper(elt); + text = StringUtil.encodeHTML(h.getSubElementText("text")); + if (h.hasChildElement("disabled")) + disabled = true; + if (h.hasChildElement("new-window")) + new_window = true; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Abstract functions which MUST be overridden + *-------------------------------------------------------------------------------- + */ + + protected abstract void appendURL(StringBuffer sbuf, RenderData rdat); + + /*-------------------------------------------------------------------------------- + * Implementations from interface ComponentRender + *-------------------------------------------------------------------------------- + */ + + public void renderHere(Writer out, RenderData rdat) throws IOException + { + StringBuffer buf = new StringBuffer(); + + if (disabled) + buf.append(""); + else + { // write the tag + buf.append("'); + + } // end else (writing tag) + + buf.append(text); + if (disabled) + buf.append(""); + else + buf.append(""); + buf.append("
\n"); + out.write(buf.toString()); + + } // end renderHere + +} // end class LeftMenuItem diff --git a/src/com/silverwrist/venice/servlets/format/menus/ServletLeftMenuItem.java b/src/com/silverwrist/venice/servlets/format/menus/ServletLeftMenuItem.java new file mode 100644 index 0000000..8785b9d --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/menus/ServletLeftMenuItem.java @@ -0,0 +1,57 @@ +/* + * 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.menus; + +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.venice.servlets.format.RenderData; + +class ServletLeftMenuItem extends LeftMenuItem +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + String url; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + ServletLeftMenuItem(Element elt) + { + super(elt); + DOMElementHelper h = new DOMElementHelper(elt); + url = h.getSubElementText("servlet"); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Overrides from class LeftMenuItem + *-------------------------------------------------------------------------------- + */ + + protected void appendURL(StringBuffer sbuf, RenderData rdat) + { + sbuf.append(rdat.getEncodedServletPath(url)); + + } // end appendURL + +} // end class ServletLeftMenuItem diff --git a/web/format/attach_form.jsp b/web/format/attach_form.jsp index c79b568..d7b4f83 100644 --- a/web/format/attach_form.jsp +++ b/web/format/attach_form.jsp @@ -38,4 +38,3 @@ WIDTH=80 HEIGHT=24 BORDER=0>

-<% rdat.writeFooter(out); %> diff --git a/web/format/base.jsp b/web/format/base.jsp index f0b7e66..5b76682 100644 --- a/web/format/base.jsp +++ b/web/format/base.jsp @@ -20,19 +20,6 @@ <%@ page import = "com.silverwrist.venice.core.*" %> <%@ page import = "com.silverwrist.venice.servlets.Variables" %> <%@ page import = "com.silverwrist.venice.servlets.format.*" %> -<%! - -private static void renderMenu(HttpSession session, java.io.Writer out, RenderData rdat) - throws java.io.IOException -{ - ComponentRender menu = Variables.getMenu(session); - if (menu==null) - menu = new MenuTop(); - menu.renderHere(out,rdat); - -} // end renderMenu - -%> <% BaseJSPData basedat = BaseJSPData.retrieve(request); Variables.failIfNull(basedat); @@ -97,36 +84,51 @@ private static void renderMenu(HttpSession session, java.io.Writer out, RenderDa <% if (rdat.useHTMLComments()) { %><% } %> - - - -
- - <% if (rdat.useHTMLComments()) { %><% } %> - +
<%= rdat.getStdFontTag(null,2) %> - <% if (rdat.useHTMLComments()) { %><% } %> - <% renderMenu(session,out,rdat); %> -
+ + +
+ + <% if (rdat.useHTMLComments()) { %><% } %> + - - + + - <% if (rdat.useHTMLComments()) { %><% } %> + <% if (rdat.useHTMLComments()) { %><% } %> -
<%= rdat.getStdFontTag(null,2) %> + <% if (rdat.useHTMLComments()) { %><% } %> + <% basedat.renderMenu(session,out,rdat); %> +
 
<%= rdat.getStdFontTag(null,2) %> - <% if (rdat.useHTMLComments()) { %><% } %> - About This Site
- Documentation
- About Venice -
 
<%= rdat.getStdFontTag(null,2) %> + <% if (rdat.useHTMLComments()) { %><% } %> + <% basedat.renderFixedMenu(out,rdat); %> +
-
+
-
+ <% if (rdat.useHTMLComments()) { %><% } %> <% basedat.renderContent(application,out,rdat); %> - <% if (rdat.useHTMLComments()) { %><% } %> -
-
+ <% if (rdat.useHTMLComments()) { %><% } %> + + + +   + + <% if (rdat.useHTMLComments()) { %><% } %> +


+ + + +
<%= rdat.getStdFontTag(null,1) %> + <%= rdat.getStockMessage("footer-text") %> + + " ALT="Powered by Venice" + WIDTH=140 HEIGHT=80 BORDER=0 HSPACE=0 VSPACE=0> +
+ + + diff --git a/web/format/conf_activity.jsp b/web/format/conf_activity.jsp index 65b3156..a1bcba8 100644 --- a/web/format/conf_activity.jsp +++ b/web/format/conf_activity.jsp @@ -25,11 +25,18 @@ Variables.failIfNull(data); RenderData rdat = RenderConfig.createRenderData(application,request,response); %> -<% rdat.writeContentHeader(out,(data.isPosterReport() ? "Posters in Conference:" - : "Readers in Conference:"),data.getConfName()); %> +<% + if (data.isTopicReport()) + rdat.writeContentHeader(out,(data.isPosterReport() ? "Posters in Topic:" + : "Readers in Topic:"), + data.getTopicName() + " in " + data.getConfName()); + else + rdat.writeContentHeader(out,(data.isPosterReport() ? "Posters in Conference:" + : "Readers in Conference:"),data.getConfName()); +%> <%= rdat.getStdFontTag(null,2) %> - ">Return to - Manage Conference Menu + ">Return to + Conference Reports Menu

<% if (data.anyElements()) { %> @@ -79,11 +86,18 @@ <% } else { %> <%= rdat.getStdFontTag(null,2) %> - <% if (data.isPosterReport()) { %> - No posters to conference "<%= StringUtil.encodeHTML(data.getConfName()) %>" found. + <% if (data.isTopicReport()) { %> + <% if (data.isPosterReport()) { %> + No posters to topic "<%= data.getTopicName() %>" found. + <% } else { %> + No readers of topic "<%= data.getTopicName() %>" found. + <% } // end if %> <% } else { %> - No readers of conference "<%= StringUtil.encodeHTML(data.getConfName()) %>" found. + <% if (data.isPosterReport()) { %> + No posters to conference "<%= StringUtil.encodeHTML(data.getConfName()) %>" found. + <% } else { %> + No readers of conference "<%= StringUtil.encodeHTML(data.getConfName()) %>" found. + <% } // end if %> <% } // end if %> <% } // end if %> -

<% rdat.writeFooter(out); %> diff --git a/web/format/conf_member.jsp b/web/format/conf_member.jsp index ccc9814..b3b717e 100644 --- a/web/format/conf_member.jsp +++ b/web/format/conf_member.jsp @@ -151,4 +151,3 @@ WIDTH=80 HEIGHT=24 BORDER=0>
<% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/conf_sequence.jsp b/web/format/conf_sequence.jsp index db9903b..14932af 100644 --- a/web/format/conf_sequence.jsp +++ b/web/format/conf_sequence.jsp @@ -137,4 +137,3 @@ <% } else { %> <%= rdat.getStdFontTag(null,2) %>There are no conferences in this SIG. <% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/conferences.jsp b/web/format/conferences.jsp index e39bad0..6595de9 100644 --- a/web/format/conferences.jsp +++ b/web/format/conferences.jsp @@ -72,4 +72,3 @@ BORDER=0>  <% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/find.jsp b/web/format/find.jsp index e08f384..d03a243 100644 --- a/web/format/find.jsp +++ b/web/format/find.jsp @@ -286,4 +286,3 @@ private static String getActivityString(SIGContext sig, RenderData rdat)
<% } // end if (results found) %> -<% rdat.writeFooter(out); %> diff --git a/web/format/hotlist.jsp b/web/format/hotlist.jsp index 2dfcbb0..c2ab595 100644 --- a/web/format/hotlist.jsp +++ b/web/format/hotlist.jsp @@ -96,4 +96,3 @@ by visiting the conferences and pressing the "Add to Hotlist" button. <% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/invitation.jsp b/web/format/invitation.jsp index 97eeb2c..b59ed05 100644 --- a/web/format/invitation.jsp +++ b/web/format/invitation.jsp @@ -49,4 +49,3 @@ -<% rdat.writeFooter(out); %> \ No newline at end of file diff --git a/web/format/manage_aliases.jsp b/web/format/manage_aliases.jsp index 411574c..9a19efb 100644 --- a/web/format/manage_aliases.jsp +++ b/web/format/manage_aliases.jsp @@ -68,4 +68,3 @@ -<% rdat.writeFooter(out); %> diff --git a/web/format/manage_conf.jsp b/web/format/manage_conf.jsp index 3fccf1e..2fa089a 100644 --- a/web/format/manage_conf.jsp +++ b/web/format/manage_conf.jsp @@ -61,13 +61,9 @@ Conference Aliases

">Manage Conference Members

- ">Conference - Posters Report

- ">Conference - Readers/Lurkers Report

+ ">Conference + Activity Reports

">Delete Conference

<% } // end if (displaying admin section) %> - -<% rdat.writeFooter(out); %> diff --git a/web/format/newsigwelcome.jsp b/web/format/newsigwelcome.jsp index 8d6ce15..3e0acc7 100644 --- a/web/format/newsigwelcome.jsp +++ b/web/format/newsigwelcome.jsp @@ -40,5 +40,3 @@ Invite Users -<% rdat.writeFooter(out); %> - diff --git a/web/format/newtopic.jsp b/web/format/newtopic.jsp index d7a5f53..9c4d236 100644 --- a/web/format/newtopic.jsp +++ b/web/format/newtopic.jsp @@ -48,7 +48,7 @@
<%= rdat.getStdFontTag(null,2) %>New topic name:
- +
<%= rdat.getStdFontTag(null,2) %>Your name/header:
@@ -77,4 +77,3 @@
-<% rdat.writeFooter(out); %> diff --git a/web/format/posts.jsp b/web/format/posts.jsp index 3b83993..ffbd496 100644 --- a/web/format/posts.jsp +++ b/web/format/posts.jsp @@ -176,7 +176,7 @@ ) <% if (msg.hasAttachment()) { %> " TARGET="_blank">" ALT="(Attachment <%= msg.getAttachmentFilename() %> - <%= msg.getAttachmentLength() %> bytes)" WIDTH=16 HEIGHT=16 BORDER=0> @@ -341,4 +341,3 @@ <% } else if (data.isTopicFrozen()) { %>

<%= rdat.getStdFontTag(null,2) %>This is a Frozen Topic
<% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/preview.jsp b/web/format/preview.jsp index 3fd11a4..ca5599f 100644 --- a/web/format/preview.jsp +++ b/web/format/preview.jsp @@ -84,4 +84,3 @@ -<% rdat.writeFooter(out); %> diff --git a/web/format/report_conf.jsp b/web/format/report_conf.jsp new file mode 100644 index 0000000..70ddddd --- /dev/null +++ b/web/format/report_conf.jsp @@ -0,0 +1,73 @@ +<%-- + 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): +--%> +<%@ page import = "java.util.*" %> +<%@ page import = "com.silverwrist.util.StringUtil" %> +<%@ page import = "com.silverwrist.venice.core.*" %> +<%@ page import = "com.silverwrist.venice.servlets.Variables" %> +<%@ page import = "com.silverwrist.venice.servlets.format.*" %> +<% + ReportConferenceMenu data = ReportConferenceMenu.retrieve(request); + Variables.failIfNull(data); + RenderData rdat = RenderConfig.createRenderData(application,request,response); + String stdfont = rdat.getStdFontTag(null,2); + String partial; +%> +<% if (rdat.useHTMLComments()) { %><% } %> +<% rdat.writeContentHeader(out,"Conference Reports:",data.getConfName()); %> + +<%= stdfont %> + ">Return to Manage + Conference Menu +

+ + + + + + + + + + + <% partial = "confops?" + data.getLocator() + "&cmd="; %> + + + + + <% Iterator it = data.getTopics(); %> + <% while (it.hasNext()) { %> + <% + TopicContext topic = (TopicContext)(it.next()); + partial = "confops?" + data.getLocator() + "&top=" + topic.getTopicNumber() + "&cmd="; + %> + + + + + + + <% } // end while %> +
<%= stdfont %>#<%= stdfont %>Topic Name<%= stdfont %>Reports
 <%= stdfont %>(Entire conference)<%= stdfont %> + ">Posters + <%= stdfont %> + ">Readers/Lurkers +
<%= stdfont %><%= topic.getTopicNumber() %><%= stdfont %><%= topic.getName() %><%= stdfont %> + ">Posters + <%= stdfont %> + ">Readers/Lurkers +

diff --git a/web/format/sig_member.jsp b/web/format/sig_member.jsp index 9618d9f..9f6015b 100644 --- a/web/format/sig_member.jsp +++ b/web/format/sig_member.jsp @@ -148,4 +148,3 @@ WIDTH=80 HEIGHT=24 BORDER=0>
<% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/sigcatbrowser.jsp b/web/format/sigcatbrowser.jsp index cbc8c48..4a919d5 100644 --- a/web/format/sigcatbrowser.jsp +++ b/web/format/sigcatbrowser.jsp @@ -69,4 +69,3 @@ SIG Administration menu.

-<% rdat.writeFooter(out); %> diff --git a/web/format/siglist.jsp b/web/format/siglist.jsp index 89cb8b7..3cd6e4d 100644 --- a/web/format/siglist.jsp +++ b/web/format/siglist.jsp @@ -62,4 +62,3 @@ <% } else { %> <%= rdat.getStdFontTag(null,2) %>You are not a member of any SIGs. <% } // end if %> -<% rdat.writeFooter(out); %> diff --git a/web/format/sigprofile.jsp b/web/format/sigprofile.jsp index 9691f96..84f534f 100644 --- a/web/format/sigprofile.jsp +++ b/web/format/sigprofile.jsp @@ -100,4 +100,3 @@ -<% rdat.writeFooter(out); %> diff --git a/web/format/sigwelcome.jsp b/web/format/sigwelcome.jsp index fe8fd29..ebb7c84 100644 --- a/web/format/sigwelcome.jsp +++ b/web/format/sigwelcome.jsp @@ -33,4 +33,3 @@ has elected to provide. Enjoy your membership!

-<% rdat.writeFooter(out); %> diff --git a/web/format/slippage.jsp b/web/format/slippage.jsp index 286da09..c39fbd8 100644 --- a/web/format/slippage.jsp +++ b/web/format/slippage.jsp @@ -106,6 +106,3 @@ -<% rdat.writeFooter(out); %> - - diff --git a/web/format/top_content.jsp b/web/format/top_content.jsp index f500125..8f1aef0 100644 --- a/web/format/top_content.jsp +++ b/web/format/top_content.jsp @@ -51,4 +51,4 @@ <% } // end for %> <% } else { %> <%= rdat.getStdFontTag(null,2) %>No front page postings found. -<% } // end if %> \ No newline at end of file +<% } // end if %> diff --git a/web/format/topics.jsp b/web/format/topics.jsp index ada14c9..75dd2c9 100644 --- a/web/format/topics.jsp +++ b/web/format/topics.jsp @@ -190,4 +190,3 @@ ] -<% rdat.writeFooter(out); %> diff --git a/web/format/userprofile.jsp b/web/format/userprofile.jsp index ed4c6ba..d11c55c 100644 --- a/web/format/userprofile.jsp +++ b/web/format/userprofile.jsp @@ -109,5 +109,3 @@ <% } // end if %> - -<% rdat.writeFooter(out); %> diff --git a/web/images/sw-main.gif b/web/images/sw-main.gif new file mode 100644 index 0000000000000000000000000000000000000000..6a1d4e2b2176c41426ebabd67d2251ee88a75a0b GIT binary patch literal 4544 zcmc(e`9DAc&1cDI=jX)p(+CUm;fo4z*(dN(|kxGTBR5XBDiKX3LNp4Hgo#ub zrow0{4AG!KEt*J0!&EeyiiQ9qXr)3#Dg;v@G!+6<0W}Z<(0~$PfgBhNw1G6x0?iO@ zAzBO`OeCU-M2NN@7z-j8CcPfD)7f4lo#K18JZI(VU~DK@1)mh9R1fU_USxL})aOhJdeNT|fbn zFocF7;3IGv7y!6HIWQV*3#;Oz)VXz^f24VmjbOJ2E0R{tYAPvz@M5_jA z5Q7JSivc6Seqbz!&=Bq8p!xdu-n8W36Vhz{dlg#kU!!TR|Mia6`qvKH5&mvSQ~C={ zyUAY;n($v~@Hv1h0rS94VATKX@$b;nL69JHjs`&cC)800q>JL!Yk2tlHo`1nH_%Yj zoya0+P@vaX+?&dY#)kYpp-z|1D4!DfyWTApFJ@L{H`r8WND$T?D$s8(?~OSVjQhYf z_Webcg5$A&4Nk!(5A^U7A5rENZwgIHGz+WPe)K%lX~w!ogpeoeod{uR)0+YlwVsr| z<1brlZS!5}vb#vFeiQBf8;c*AwrV~;J-@d8uTfh7=IJfAic>yqjbHm2SS9b<-MTRM zGH=_U{Le@VpO@j$!S3epP^*1^dDqEbgE8%%J2RI% zLO)cs>#s&D+Ij}hx73UdQIzFIzf>5Tew3mtPS4@0s}Yr(U5|G*Mj}f7)LGxWaew(a z8+-keCx**+1F!T-t3BRF@N2moR}O8-b4F?ZbJQvBm=yf^_8oqd)$L?`6k9we_uNzh zZ`eSQI$wg=7Y~N;Jc}f1QTSNWIlnfSR58BOUo-{A-tj}0w@|K$+$#!SHB_S&G&7V= z&JnSKSAI=pDawY-W!aujo-;5BgBb{VFFd~yBn763hNcEyrMbq7dLmg2cc!dK*3)^y z4i-5Ja2}t7c7-u^NO83AVwN~dTwtHIi(zarVV)?(9Qq?mVTJfD!SiIu$6~kWBi9Ph z-=;Fk^NZ)b^l{N!;yB}!%T*C}O>R~CY24PTB}qOe!a;(eOLCa`TS(dh=s?`<#CGF&rn;_U||V=-5B3i zZPV($&3gUZs7u<;!L3pg0%zv8-m3)`N(G?Mh|!0S{*3#lK8G7}pN)vFP_V}ZPwIyE z#C4vFcooI*>h*NqYQa?CRTaM-^+4By-1&-s_7#1KD_(6ageR`peRbmz=3A&)N-#~l zCc^#Bhj+%HG+8=@(~86+lJB#2Q^boqU0VywaPOr-&s^4(%k|^P!{vHdLNOw{@}f1GL)cVN=NTA%>8c!uv5S9wuLhdtD-OID7kHw+2l zEA9!aP|%Pzx)=8_fsdf3I_zbfTlMhZ$9(BQ=&1RsWDZtFLF4K^O1nSB;c7sl@YR1) zuhY{LiZBC`d+jU?VFB+v5E9P<>Wyf49uW?|p zb?ui3L&uCarvKS3;HwSqxEJ~QQjCyW%;iH+`#`_zXh(^F+(ss5@f^u{}34wl#2F@D24OiJPR zQ1nfBb^M2T)R3y|Pv3F^kr}FNzLeOMmPh6N9`+DxSV(RPwbRD> z=UmR0%DP(_mMx6#wHb#U2%${l*lzg9L<;I|o^O=R6$_3@>_k&t&`%NDC~J$yn!a^> za`PBnZmurOSV8=Fne)FlCVCuRdVWtXbv(OZ+;7w3l}%ypfIX~(v8J_8ls@AylT&w} zB}G2Pi~X0>WYb8NzDEAZawTr=>~DmToJ?(I-H3cECVX%u`kbb(Pq6p7^7B8MII3u9sG6 zT7*5`OKm8Skkb&MNI%~%yRUW8=(%CURiUj78N~d9wi;cEk?{K1Lze+N9j@Oo#btpE z!*jFhl)N6@$4(JzN57mFWqkx~eahUB{LCyG7{*9FSzF3mcGy3l{p3u2d>2XmpA^n6 z$F16A;mSFgjrzoLa8>C;e8ZGHd+$hYrkt;Ub%J1+RO%Kaqxs6=8_~r{|x}HMj zB?&J!<*yFTrH= z;}#v&T@21UY(Jm&t-UYhV59Yr7Hmh!rO>)O{bQC)blWq5xoa8T0FjY?$ zt-e`uHtC!u&sf#^7cL2T>`uRlUcYybZG~TV??e5{rAJCfS7Y&=*1BJ|pB|1@x4cpA zJj@*aoUQJ^Zly@km?uNe3{*9-zTKa|7lYmX&FvcomaSSGi4HUK22S9 zy=G)|$&CBvRQ;1J7Lv+xATrRKaDTgQTjf6v{iRPQ95-L69So(`|C##{y*<2rZllD% zbHNO0qFg%I+~~8^BYgPpeL6Sd7G*H~pIT08N<#TgErus8C~$u{U2N?638Z#V&1U+< z(`&*hQ@dtCA!(dnQku2*a zjXUNFG4_I?HsJ{t{ddg2UOse(UH6BN(?DEA?j6s88~%JZ? zUO0AN)QSalyGYC=AD>dymVy_WNb-dDQGAS{r?XJ+8b>njZg?06r$>f^r z_7kBtR)24{Sy%rZp1%9rZU<#w-OZ0Lw{3)lzM};8J=6aXyq@d0 z9VVyGTByjI3SQPs=X1HY)tz28VK?P^N?z8LnJe@3g1{NZ463joN-;yrC6h&5h~FhM z&(iJ`W0o`{UNBjiKLRg{FgkVX;B{ne*YqUS#MQglBqaoNG$!$?WSuVBq31qa<EVgri#*IFNRO!4Fu zt1Es{?z$sR*Y{w4Q=@ONV4LE$;0gk zu|-kgv*BG9WNQ^y`%DzqNpNPyI&NzfS$@~Ln4rbIsLJH#ntG_@)1b@9tozZ{`>Tph zTE`xQ>pv_UE7sd}elE`EFhRJmrPi+H4jH(|!0O-VO0P&^gOX)Aa>Ni>Zb*2|S^X}~HQ)%@30qo6F5{he zNH~^TJMFMNDD-->stKo*x9E^PRd(OTBh!GKQCvJwT*Y{?ympcF!h-cA@zi^ruujG@ z?g*!Y-)CM=6Wcq|aG5oKdQ!`enygjS_Oyj8&m|dFq|FSSH7p897pimAsLQQP9B`9; z<;lK?!#veH$sv4uc`>lpQ)(7-vQEA}Ueu2_^TKD?=W8qbx3GZvCK-Cuq>0xFzS<4z zmcGt&GVF&HKf)J2pVWK1CG!j;&HJ+P@LuEc8n6A^gnWwMyGp+g-y4}K8iZ1%8Ri4z zGXs;Y)f6Ut!wwo=O)CQR)U;fVKD4`ZqkogYWERJsws#ZKE z@4x#ZfW-q7ncr;A)V3$v^kvMSd(Y>PYpdB()4=!UY176d^Q}gb?RV%KK3B@7+-%nw NZzFZG(9uCk{{#9el&=5) literal 0 HcmV?d00001 diff --git a/web/images/venicelogo.jpg b/web/images/venicelogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7759b8301359378061811c37194516614d814e17 GIT binary patch literal 9323 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 literal 0 HcmV?d00001 diff --git a/web/static/about-venice.html b/web/static/about-venice.html new file mode 100644 index 0000000..865965f --- /dev/null +++ b/web/static/about-venice.html @@ -0,0 +1,43 @@ + + + + About Venice + + + +
Venice Web Communities System

+

Venice Web Communities System Release 0.01PR

+ + Copyright © 2001 Silverwrist Design Studios, All Rights Reserved.

+ This software is subject to the + Mozilla Public License Version 1.1. + It 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 Venice Project Team

+
+
Eric Bowersox <erbo>
+
Code wrangler, system guru, and administrator in general
+
Harry Pike <maddog>
+
EMinds host and domain expert in virtual community hosting
+
Finnegan <finnegan>
+
Designer of the Venice web site
+
Catherine Dodson <cait>
+
Technical documentation
+
Pamela Boulais <silverwrist>
+
Administrative support, liaison, and den mother
+
The Entire Electric Minds Community
+
Beta testing, bug hunting, and encouragement
+
+ +
+ Thanks to: Howard, who thought it up; Andre, who rescued it the first time; and the community, who + kept it together no matter what. +

+ +

Silverwrist Design Studios
+ + + diff --git a/web/static/images/sw-main.gif b/web/static/images/sw-main.gif new file mode 100644 index 0000000000000000000000000000000000000000..6a1d4e2b2176c41426ebabd67d2251ee88a75a0b GIT binary patch literal 4544 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 NZzFZG(9uCk{{#9el&=5) literal 0 HcmV?d00001 diff --git a/web/static/images/venicelogo.jpg b/web/static/images/venicelogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7759b8301359378061811c37194516614d814e17 GIT binary patch literal 9323 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 literal 0 HcmV?d00001