From 2e455b4bdd79db70da55d05d587ba3c07fddf306 Mon Sep 17 00:00:00 2001 From: "Eric J. Bowersox" Date: Wed, 28 Feb 2001 07:55:00 +0000 Subject: [PATCH] implemented front page content management, finally wired up the user locale and timezone default mechanism, and did some other bugfixing and stuff --- TODO | 5 - etc/erbo.dict | 1 + etc/render-config.xml | 18 +- setup/database.sql | 22 +- src/com/silverwrist/util/LocaleFactory.java | 62 +++ .../venice/core/TopicMessageContext.java | 6 +- .../silverwrist/venice/core/UserContext.java | 10 + .../silverwrist/venice/core/VeniceEngine.java | 2 + .../core/impl/BackgroundConferencePurge.java | 7 +- .../venice/core/impl/BackgroundSIGPurge.java | 8 +- .../core/impl/BackgroundTopicPurge.java | 21 +- .../venice/core/impl/ConferenceCoreData.java | 3 +- .../core/impl/ConferenceUserContextImpl.java | 2 +- .../venice/core/impl/ContactInfoImpl.java | 61 ++- .../venice/core/impl/EngineBackend.java | 7 + .../core/impl/PublishedMessageImpl.java | 426 ++++++++++++++++++ .../venice/core/impl/SIGCoreData.java | 3 +- .../impl/TopicMessageUserContextImpl.java | 195 ++++++-- .../core/impl/TopicUserContextImpl.java | 2 +- .../venice/core/impl/UserContextImpl.java | 184 +++++++- .../venice/core/impl/VeniceEngineImpl.java | 89 +++- .../silverwrist/venice/security/Audit.java | 5 +- .../venice/security/Capability.java | 8 +- .../venice/servlets/PostOperations.java | 24 + src/com/silverwrist/venice/servlets/Top.java | 2 +- .../venice/servlets/VeniceServlet.java | 2 +- .../format/CDCountryListFormField.java | 17 +- .../format/CDLocaleListFormField.java | 106 +++++ .../servlets/format/CDPickListFormField.java | 22 +- .../format/CDTimeZoneListFormField.java | 106 +++++ .../servlets/format/EditProfileDialog.java | 46 +- .../venice/servlets/format/RenderConfig.java | 57 ++- .../venice/servlets/format/RenderData.java | 42 +- .../venice/servlets/format/TopDisplay.java | 60 ++- web/format/posts.jsp | 5 + web/format/top_content.jsp | 28 +- web/images/bn_publish.gif | Bin 0 -> 937 bytes 37 files changed, 1555 insertions(+), 109 deletions(-) create mode 100644 src/com/silverwrist/util/LocaleFactory.java create mode 100644 src/com/silverwrist/venice/core/impl/PublishedMessageImpl.java create mode 100644 src/com/silverwrist/venice/servlets/format/CDLocaleListFormField.java create mode 100644 src/com/silverwrist/venice/servlets/format/CDTimeZoneListFormField.java create mode 100644 web/images/bn_publish.gif diff --git a/TODO b/TODO index 351fb69..83df3c7 100644 --- a/TODO +++ b/TODO @@ -17,11 +17,6 @@ Lots! - Unimplemented functions on the Top page: Customize Sideboxes -- The plan for the "main" part of the Top page is to let a sysadmin put - notices there by "publishing" selected conference messages to the front page. - We may include a welcome message up top to be displayed if the user's not - logged in. - - Slippage during posting is still untested. - Not everybody likes purple. Provide a way to change the default colors. diff --git a/etc/erbo.dict b/etc/erbo.dict index 1f8a59e..7e5a055 100644 --- a/etc/erbo.dict +++ b/etc/erbo.dict @@ -1,5 +1,6 @@ advogato ain't +anime anla'shok bajor bios diff --git a/etc/render-config.xml b/etc/render-config.xml index 3f56759..0c0689b 100644 --- a/etc/render-config.xml +++ b/etc/render-config.xml @@ -50,4 +50,20 @@ http://delenn:8080/venice/images/powered-by-venice.gif - \ No newline at end of file + + + + +Welcome to the Venice Web Communities System. To get the most out of this site, you should log in or create +an account, using one of the links above. + + + + Welcome to Venice + + + Venice Currents + + + + diff --git a/setup/database.sql b/setup/database.sql index c86e7d4..3fbbb26 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -1,4 +1,3 @@ - # MySQL script for initializing the Venice database. # Written by Eric J. Bowersox #--------------------------------------------------------------------------- @@ -39,7 +38,8 @@ CREATE TABLE globals ( old_posts_at_top INT NOT NULL, max_search_page INT NOT NULL, max_sig_mbr_page INT NOT NULL, - max_conf_mbr_page INT NOT NULL + max_conf_mbr_page INT NOT NULL, + fp_posts INT NOT NULL ); # The audit records table. Most "major" events add a record to this table. @@ -87,7 +87,7 @@ CREATE TABLE users ( CREATE TABLE userprefs ( uid INT NOT NULL PRIMARY KEY, tzid VARCHAR(64) DEFAULT 'UTC', - localeid VARCHAR(64) DEFAULT 'en-US-' + localeid VARCHAR(64) DEFAULT 'en_US' ); # Indicates what the top-level "sidebox" configuration is for any given user. @@ -377,6 +377,7 @@ CREATE TABLE postattach ( datalen INT, filename VARCHAR(255), mimetype VARCHAR(128), + stgmethod SMALLINT DEFAULT 0, data MEDIUMBLOB ); @@ -387,6 +388,15 @@ CREATE TABLE postdogear ( PRIMARY KEY (uid, postid) ); +# "Front page" publishing table. +CREATE TABLE postpublish ( + sigid INT NOT NULL, + postid BIGINT NOT NULL PRIMARY KEY, + by_uid INT NOT NULL, + on_date DATETIME NOT NULL, + INDEX display_order (on_date, postid) +); + ############################################################################## # Set table access rights ############################################################################## @@ -409,6 +419,7 @@ GRANT INSERT, DELETE, UPDATE, SELECT ON venice.* # Types of audit records. This MUST be kept in sync with the constant definitions in # com.silverwrist.venice.security.Audit!!!! INSERT INTO refaudit (type, descr) VALUES + (1, 'Publish Message to Front Page'), (101, 'Login OK'), (102, 'Login Failure'), (103, 'Account Created'), @@ -1312,8 +1323,9 @@ INSERT INTO refsigftr (ftr_code, is_default, is_locked, is_hidden, require_read, ############################################################################## # Initialize the system globals table. -INSERT INTO globals (posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page) - VALUES (20, 2, 20, 50, 50); +INSERT INTO globals (posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page, + fp_posts) + VALUES (20, 2, 20, 50, 50, 10); # Add the 'Anonymous Honyak' user to the users table. # (Do 'SELECT * FROM users WHERE is_anon = 1' to retrieve the AC user details.) diff --git a/src/com/silverwrist/util/LocaleFactory.java b/src/com/silverwrist/util/LocaleFactory.java new file mode 100644 index 0000000..c72482b --- /dev/null +++ b/src/com/silverwrist/util/LocaleFactory.java @@ -0,0 +1,62 @@ +/* + * 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; + +import java.util.*; + +public class LocaleFactory +{ + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + private LocaleFactory() + { // this object cannot be instantiated + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static operations + *-------------------------------------------------------------------------------- + */ + + public static Locale createLocale(String streq) + { + if ((streq==null) || (streq.length()==0)) + return Locale.getDefault(); + int p1 = streq.indexOf('_'); + if (p1<0) + return new Locale(streq,""); + String x_lang = streq.substring(0,p1); + int p2 = streq.indexOf('_',p1+1); + if (p2<0) + { // there's only one underscore - figure out what part the last part is + String lastpart = streq.substring(p1+1); + if (lastpart.length()==2) + return new Locale(streq.substring(0,p1),lastpart); + else + return new Locale(streq.substring(0,p1),"",lastpart); + + } // end if + + // do all three variants + return new Locale(streq.substring(0,p1),streq.substring(p1+1,p2),streq.substring(p2+1)); + + } // end createLocale + +} // end class LocaleFactory diff --git a/src/com/silverwrist/venice/core/TopicMessageContext.java b/src/com/silverwrist/venice/core/TopicMessageContext.java index b5c934e..dc34ad3 100644 --- a/src/com/silverwrist/venice/core/TopicMessageContext.java +++ b/src/com/silverwrist/venice/core/TopicMessageContext.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 @@ -73,4 +73,8 @@ public interface TopicMessageContext public abstract void attachData(String m_type, String file, int length, InputStream data) throws AccessError, DataException; + public abstract boolean canPublish(); + + public abstract void publish() throws DataException, AccessError; + } // end interface TopicMessageContext diff --git a/src/com/silverwrist/venice/core/UserContext.java b/src/com/silverwrist/venice/core/UserContext.java index 73a007c..fb08fe7 100644 --- a/src/com/silverwrist/venice/core/UserContext.java +++ b/src/com/silverwrist/venice/core/UserContext.java @@ -18,6 +18,8 @@ package com.silverwrist.venice.core; import java.util.List; +import java.util.Locale; +import java.util.TimeZone; public interface UserContext extends SearchMode { @@ -93,4 +95,12 @@ public interface UserContext extends SearchMode public abstract AdminOperations getAdminInterface() throws AccessError; + public abstract Locale getLocale() throws DataException; + + public abstract void setLocale(Locale locale) throws DataException; + + public abstract TimeZone getTimeZone() throws DataException; + + public abstract void setTimeZone(TimeZone timezone) throws DataException; + } // end interface UserContext diff --git a/src/com/silverwrist/venice/core/VeniceEngine.java b/src/com/silverwrist/venice/core/VeniceEngine.java index 2f3f247..e6fe9ed 100644 --- a/src/com/silverwrist/venice/core/VeniceEngine.java +++ b/src/com/silverwrist/venice/core/VeniceEngine.java @@ -73,4 +73,6 @@ public interface VeniceEngine extends SearchMode public abstract List getMasterSideBoxList(); + public abstract List getPublishedMessages(boolean all) throws DataException; + } // end interface VeniceEngine diff --git a/src/com/silverwrist/venice/core/impl/BackgroundConferencePurge.java b/src/com/silverwrist/venice/core/impl/BackgroundConferencePurge.java index 08b379f..b7c96f1 100644 --- a/src/com/silverwrist/venice/core/impl/BackgroundConferencePurge.java +++ b/src/com/silverwrist/venice/core/impl/BackgroundConferencePurge.java @@ -37,6 +37,7 @@ class BackgroundConferencePurge implements Runnable *-------------------------------------------------------------------------------- */ + private EngineBackend engine; private DataPool datapool; private int confid; private int num_topics; @@ -47,8 +48,10 @@ class BackgroundConferencePurge implements Runnable *-------------------------------------------------------------------------------- */ - BackgroundConferencePurge(DataPool datapool, int confid, int num_topics, int max_topicid) + BackgroundConferencePurge(EngineBackend engine, DataPool datapool, int confid, int num_topics, + int max_topicid) { + this.engine = engine; this.datapool = datapool; this.confid = confid; this.num_topics = num_topics; @@ -100,7 +103,7 @@ class BackgroundConferencePurge implements Runnable rs = stmt.executeQuery(sql.toString()); if (!(rs.next())) throw new InternalStateError("BackgroundConferencePurge.run screwup on post SELECT"); - rq.queue(new BackgroundTopicPurge(datapool,topicids[i],rs.getInt(1),rs.getLong(2))); + rq.queue(new BackgroundTopicPurge(engine,datapool,topicids[i],rs.getInt(1),rs.getLong(2))); } // end for diff --git a/src/com/silverwrist/venice/core/impl/BackgroundSIGPurge.java b/src/com/silverwrist/venice/core/impl/BackgroundSIGPurge.java index c1c6672..43df8a4 100644 --- a/src/com/silverwrist/venice/core/impl/BackgroundSIGPurge.java +++ b/src/com/silverwrist/venice/core/impl/BackgroundSIGPurge.java @@ -39,6 +39,7 @@ class BackgroundSIGPurge implements Runnable *-------------------------------------------------------------------------------- */ + private EngineBackend engine; private DataPool datapool; private UserBackend user; private int sigid; @@ -51,9 +52,10 @@ class BackgroundSIGPurge implements Runnable *-------------------------------------------------------------------------------- */ - BackgroundSIGPurge(DataPool datapool, UserBackend user, int sigid, int num_confs, int max_confid, - Hashtable conf_objects) + BackgroundSIGPurge(EngineBackend engine, DataPool datapool, UserBackend user, int sigid, int num_confs, + int max_confid, Hashtable conf_objects) { + this.engine = engine; this.datapool = datapool; this.user = user; this.sigid = sigid; @@ -137,7 +139,7 @@ class BackgroundSIGPurge implements Runnable rs = stmt.executeQuery(sql.toString()); if (!(rs.next())) throw new InternalStateError("BackgroundSIGPurge.run screwup on conference SELECT"); - rq.queue(new BackgroundConferencePurge(datapool,key.intValue(),rs.getInt(1),rs.getInt(2))); + rq.queue(new BackgroundConferencePurge(engine,datapool,key.intValue(),rs.getInt(1),rs.getInt(2))); } // end if (have to delete conference data) diff --git a/src/com/silverwrist/venice/core/impl/BackgroundTopicPurge.java b/src/com/silverwrist/venice/core/impl/BackgroundTopicPurge.java index c7b9d95..407c87d 100644 --- a/src/com/silverwrist/venice/core/impl/BackgroundTopicPurge.java +++ b/src/com/silverwrist/venice/core/impl/BackgroundTopicPurge.java @@ -35,6 +35,7 @@ class BackgroundTopicPurge implements Runnable *-------------------------------------------------------------------------------- */ + private EngineBackend engine; private DataPool datapool; private int topicid; private int num_posts; @@ -45,8 +46,9 @@ class BackgroundTopicPurge implements Runnable *-------------------------------------------------------------------------------- */ - BackgroundTopicPurge(DataPool datapool, int topicid, int num_posts, long max_postid) + BackgroundTopicPurge(EngineBackend engine, DataPool datapool, int topicid, int num_posts, long max_postid) { + this.engine = engine; this.datapool = datapool; this.topicid = topicid; this.num_posts = num_posts; @@ -62,7 +64,7 @@ class BackgroundTopicPurge implements Runnable public void run() { if (logger.isDebugEnabled()) - logger.debug("BackgroundTopicPurge running on topic #" + String.valueOf(topicid)); + logger.debug("BackgroundTopicPurge running on topic #" + topicid); long[] postids = new long[num_posts]; // stores the post IDs Connection conn = null; // pooled database connection @@ -82,22 +84,23 @@ class BackgroundTopicPurge implements Runnable for (int i=0; i0) + engine.unpublish(postids[i]); } // end for // all done, Bunky! if (logger.isDebugEnabled()) - logger.debug("BackgroundTopicPurge complete for topic #" + String.valueOf(topicid)); + logger.debug("BackgroundTopicPurge complete for topic #" + topicid); } // end try catch (SQLException e) { // on an error, just die - logger.error("BackgroundTopicPurge FATAL EXCEPTION purging #" + String.valueOf(topicid) + ": " - + e.getMessage(),e); + logger.error("BackgroundTopicPurge FATAL EXCEPTION purging #" + topicid + ": " + e.getMessage(),e); } // end catch finally diff --git a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java index c087a2a..1a09039 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java @@ -1238,7 +1238,8 @@ class ConferenceCoreData implements ConferenceData } // end finally // Delete the rest of the gunk in the background; spin off another thread to handle it. - BackgroundConferencePurge purger = new BackgroundConferencePurge(datapool,confid,topic_count,topic_max); + BackgroundConferencePurge purger = new BackgroundConferencePurge(engine,datapool,confid,topic_count, + topic_max); Thread thrd = new Thread(purger); thrd.setPriority(Thread.NORM_PRIORITY-1); thrd.start(); diff --git a/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java b/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java index 7b753c7..e41fb39 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceUserContextImpl.java @@ -789,7 +789,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend else { // need to insert a confsettings row sql.append("INSERT INTO confsettings (confid, uid, default_pseud) VALUES (").append(confid); - sql.append(", ").append(sig.realUID()).append(", '").append(SQLUtil.encodeString(val)).append("';"); + sql.append(", ").append(sig.realUID()).append(", '").append(SQLUtil.encodeString(val)).append("');"); } // end else diff --git a/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java b/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java index f34b086..e029efa 100644 --- a/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java +++ b/src/com/silverwrist/venice/core/impl/ContactInfoImpl.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 @@ -19,12 +19,24 @@ package com.silverwrist.venice.core.impl; import java.sql.*; import java.util.*; +import org.apache.log4j.*; import com.silverwrist.venice.core.*; import com.silverwrist.venice.db.*; class ContactInfoImpl implements ContactInfo, Stashable { - // Attributes + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(ContactInfoImpl.class.getName()); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + private int contactid; // ID of this contact record (-1 = new) private String given_name; // given name ("first name") private String family_name; // family name ("last name") @@ -53,6 +65,11 @@ class ContactInfoImpl implements ContactInfo, Stashable private java.util.Date last_update; // date of last update private boolean is_modified = false; // have we modified this ContactInfo? + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + /** * Constructs a new blank ContactInfoImpl object. * @@ -60,6 +77,8 @@ class ContactInfoImpl implements ContactInfo, Stashable */ ContactInfoImpl(int owner_uid) { + if (logger.isDebugEnabled()) + logger.debug("new ContactInfoImpl (empty) for UID = " + owner_uid); makeEmpty(owner_uid,-1); } // end constructor @@ -72,6 +91,8 @@ class ContactInfoImpl implements ContactInfo, Stashable */ ContactInfoImpl(int owner_uid, int owner_sigid) { + if (logger.isDebugEnabled()) + logger.debug("new ContactInfoImpl (empty) for UID = " + owner_uid + ", SIGID = " + owner_sigid); makeEmpty(owner_uid,owner_sigid); } // end constructor @@ -85,6 +106,8 @@ class ContactInfoImpl implements ContactInfo, Stashable */ ContactInfoImpl(DataPool dp, int contactid) throws DataException { + if (logger.isDebugEnabled()) + logger.debug("new ContactInfoImpl (loading CID " + contactid + ")"); Connection conn = null; try @@ -95,6 +118,7 @@ class ContactInfoImpl implements ContactInfo, Stashable } // end try catch (SQLException e) { // turn SQLExceptions at this level into DataExceptions + logger.error("DB error loading contact ID " + contactid + ": " + e.getMessage(),e); throw new DataException("Unable to look up contact info: " + e.getMessage(),e); } // end catch @@ -107,6 +131,11 @@ class ContactInfoImpl implements ContactInfo, Stashable } // end constructor + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + private void makeEmpty(int owner_uid, int owner_sigid) { this.contactid = -1; @@ -180,18 +209,28 @@ class ContactInfoImpl implements ContactInfo, Stashable last_update = SQLUtil.getFullDateTime(rs,"lastupdate"); } // end if - else // contact was not found + else + { // contact was not found + logger.error("contact ID " + contactid + " not found in database"); throw new DataException("Contact was not found."); + } // end else + } // end try catch (SQLException e) { // map all SQLExceptions into DataExceptions + logger.error("DB error loading contact ID " + contactid + ": " + e.getMessage(),e); throw new DataException("Unable to look up contact info: " + e.getMessage(),e); } // end catch } // end loadData + /*-------------------------------------------------------------------------------- + * Implementations from interface ContactInfo + *-------------------------------------------------------------------------------- + */ + public int getContactID() { return contactid; @@ -559,6 +598,11 @@ class ContactInfoImpl implements ContactInfo, Stashable } // end getModified + /*-------------------------------------------------------------------------------- + * Implementations from interface Stashable + *-------------------------------------------------------------------------------- + */ + public int getStashableUID() { return getOwnerUID(); @@ -567,6 +611,8 @@ class ContactInfoImpl implements ContactInfo, Stashable public void stash(Connection conn) throws DataException, SQLException { + if (logger.isDebugEnabled()) + logger.debug("stashing contact ID " + contactid); java.util.Date update = null; Statement stmt = conn.createStatement(); StringBuffer buf; @@ -653,10 +699,19 @@ class ContactInfoImpl implements ContactInfo, Stashable int new_contactid; ResultSet rs = stmt.executeQuery("SELECT LAST_INSERT_ID();"); if (rs.next()) + { // found the contact ID... new_contactid = rs.getInt(1); + if (logger.isDebugEnabled()) + logger.debug("created new contact ID " + new_contactid); + + } // end if else + { // error reading back the contact ID + logger.error("unable to read back contact ID"); throw new DataException("unable to read back new contact ID"); + } // end else + // and patch the database table so we know what our contact ID is buf.setLength(0); if (owner_sigid>=0) diff --git a/src/com/silverwrist/venice/core/impl/EngineBackend.java b/src/com/silverwrist/venice/core/impl/EngineBackend.java index fbb7dde..76033f7 100644 --- a/src/com/silverwrist/venice/core/impl/EngineBackend.java +++ b/src/com/silverwrist/venice/core/impl/EngineBackend.java @@ -36,6 +36,7 @@ public interface EngineBackend public static final int IP_MAXSEARCHRETURN = 2; public static final int IP_MAXSIGMEMBERDISPLAY = 3; public static final int IP_MAXCONFMEMBERDISPLAY = 4; + public static final int IP_NUMFRONTPAGEPOSTS = 5; public abstract SimpleEmailer createEmailer(); @@ -83,4 +84,10 @@ public interface EngineBackend public abstract SideBoxDescriptor getMasterSideBoxDescriptor(int id); + public abstract void startPublish(); + + public abstract void publishNew(PublishedMessageImpl pubmsg); + + public abstract void unpublish(long postid); + } // end interface EngineBackend diff --git a/src/com/silverwrist/venice/core/impl/PublishedMessageImpl.java b/src/com/silverwrist/venice/core/impl/PublishedMessageImpl.java new file mode 100644 index 0000000..0f1fa3a --- /dev/null +++ b/src/com/silverwrist/venice/core/impl/PublishedMessageImpl.java @@ -0,0 +1,426 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core.impl; + +import java.io.*; +import java.sql.*; +import java.util.*; +import org.apache.log4j.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.db.*; +import com.silverwrist.venice.core.*; + +class PublishedMessageImpl implements TopicMessageContext +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(PublishedMessageImpl.class.getName()); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private DataPool datapool; + private long postid; + private long parent; + private int num; + private int linecount; + private int creator_uid; + private java.util.Date posted; + private String pseud; + private String creator_cache = null; + private String text_cache = null; + + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + + PublishedMessageImpl(DataPool datapool, long postid, long parent, int num, int linecount, int creator_uid, + java.util.Date posted, String pseud, String creator_cache, String text_cache) + { + this.datapool = datapool; + this.postid = postid; + this.parent = parent; + this.num = num; + this.linecount = linecount; + this.creator_uid = creator_uid; + this.posted = posted; + this.pseud = pseud; + this.creator_cache = creator_cache; + this.text_cache = text_cache; + + } // end constructor + + protected PublishedMessageImpl(DataPool datapool, long postid, long parent, int num, int linecount, + int creator_uid, java.util.Date posted, String pseud) + { + this.datapool = datapool; + this.postid = postid; + this.parent = parent; + this.num = num; + this.linecount = linecount; + this.creator_uid = creator_uid; + this.posted = posted; + this.pseud = pseud; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + + private static String quickGetUserName(Connection conn, int uid) throws SQLException + { + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE uid = " + String.valueOf(uid) + ";"); + if (rs.next()) + return rs.getString(1); + else + return "(unknown)"; + + } // end quickGetUserName + + /*-------------------------------------------------------------------------------- + * Implementations from interface TopicMessageContext + *-------------------------------------------------------------------------------- + */ + + public long getPostID() + { + return postid; + + } // end getPostID + + public long getParentPostID() + { + return parent; + + } // end getParentPostID + + public int getPostNumber() + { + return num; + + } // end getPostNumber + + public int getNumLines() + { + return linecount; + + } // end getNumLines + + public int getCreatorUID() + { + return creator_uid; + + } // end getCreatorUID + + public String getCreatorName() throws DataException + { + if (creator_cache==null) + { // we don't have the user name yet, get it out of the database + Connection conn = null; + + try + { // use a database connection to get the user name + conn = datapool.getConnection(); + creator_cache = quickGetUserName(conn,creator_uid); + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error reading user name: " + e.getMessage(),e); + throw new DataException("unable to retrieve user name: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end if + + return creator_cache; + + } // end getCreatorName + + public java.util.Date getPostDate() + { + return posted; + + } // end getPostDate + + public boolean isHidden() + { + return false; + + } // end isHidden + + public boolean isScribbled() + { + return false; + + } // end isScribbled + + public boolean isNuked() + { + return false; + + } // end isNuked + + public java.util.Date getScribbleDate() + { + return null; + + } // end getScribbleDate + + public String getPseud() + { + return pseud; + + } // end getPseud + + public String getBodyText() throws DataException + { + if (text_cache==null) + { // we don't have the body text yet, go get it + Connection conn = null; + + try + { // use a database connection to get the body text + conn = datapool.getConnection(); + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT data FROM postdata WHERE postid = " + + String.valueOf(postid) + ";"); + if (rs.next()) + text_cache = rs.getString(1); + else + return "Data Missing"; // FUTURE: throw an exception? + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error reading post data: " + e.getMessage(),e); + throw new DataException("unable to retrieve post data: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end if + + return text_cache; + + } // end getBodyCache + + public boolean hasAttachment() + { + return false; // FUTURE: allow publishing paperclips? + + } // end hasAttachment + + public String getAttachmentType() + { + return null; // FUTURE: allow publishing paperclips? + + } // end getAttachmentType + + public String getAttachmentFilename() + { + return null; // FUTURE: allow publishing paperclips? + + } // end getAttachmentFilename + + public int getAttachmentLength() + { + return 0; // FUTURE: allow publishing paperclips? + + } // end getAttachmentLength + + public InputStream getAttachmentData() throws AccessError, DataException + { + // FUTURE: allow publishing paperclips? + throw new AccessError("There is no attachment data for this message."); + + } // end getAttachmentData + + public boolean canHide() + { + return false; + + } // end canHide + + public boolean canScribble() + { + return false; + + } // end canScribble + + public boolean canNuke() + { + return false; + + } // end canNuke + + public void setHidden(boolean flag) throws DataException, AccessError + { + throw new AccessError("You are not permitted to change the hidden status of this message."); + + } // end setHidden + + public void scribble() throws DataException, AccessError + { + throw new AccessError("You are not permitted to scribble this message."); + + } // end scribble + + public void nuke() throws DataException, AccessError + { + throw new AccessError("You are not permitted to nuke this message."); + + } // end nuke + + public void attachData(String m_type, String file, int length, InputStream data) + throws AccessError, DataException + { + throw new AccessError("You are not permitted to add an attachment to this message."); + + } // end attachData + + public boolean canPublish() + { + return false; + + } // end canPublish + + public void publish() throws DataException, AccessError + { + throw new DataException("Cannot publish a message that has already been published."); + + } // end publish + + /*-------------------------------------------------------------------------------- + * Static operations usable only within package + *-------------------------------------------------------------------------------- + */ + + static void backfillCache(List cache_list, int desired_size, DataPool datapool) throws DataException + { + Connection conn = null; + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // create the statement to retrieve the post information + StringBuffer sql = + new StringBuffer("SELECT p.postid, q.parent, q.num, q.linecount, q.creator_uid, q.posted, " + + "q.pseud FROM postpublish p, posts q WHERE p.postid = q.postid " + + "ORDER BY p.on_date DESC, p.postid ASC LIMIT "); + sql.append(cache_list.size()).append(", ").append(desired_size-cache_list.size()).append(';'); + + // execute the statement! + ResultSet rs = stmt.executeQuery(sql.toString()); + while (rs.next()) + cache_list.add(new PublishedMessageImpl(datapool,rs.getLong(1),rs.getLong(2),rs.getInt(3),rs.getInt(4), + rs.getInt(5),SQLUtil.getFullDateTime(rs,6),rs.getString(7))); + + } // end try + catch (SQLException e) + { // error retrieving post information + logger.error("DB error reading post information: " + e.getMessage(),e); + throw new DataException("unable to retrieve post information: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end backfillCache + + static void backfillReturn(List return_list, DataPool datapool) throws DataException + { + Connection conn = null; + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // How many posts have been published anyway? + ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM postpublish;"); + if (!(rs.next())) + throw new InternalStateError("Count query screwup in PublishedMessageImpl.backfillReturn"); + if (rs.getInt(1)<=return_list.size()) + return; // nothing to do here! + + // execute the statement to retrieve the post information + final String sql = "SELECT p.postid, q.parent, q.num, q.linecount, q.creator_uid, q.posted, q.pseud " + + "FROM postpublish p, posts q WHERE p.postid = q.postid ORDER BY p.on_date DESC, " + + "p.postid ASC;"; + rs = stmt.executeQuery(sql); + + // append to the output list + int ctr = return_list.size(); + while (rs.next()) + { // we need to skip the first batch of output, because it's cached + if ((ctr--)<=0) + { // append context, please + TopicMessageContext ctxt = + new PublishedMessageImpl(datapool,rs.getLong(1),rs.getLong(2),rs.getInt(3),rs.getInt(4), + rs.getInt(5),SQLUtil.getFullDateTime(rs,6),rs.getString(7)); + return_list.add(ctxt); + + } // end if + + } // end while + + } // end try + catch (SQLException e) + { // error retrieving post information + logger.error("DB error reading post information: " + e.getMessage(),e); + throw new DataException("unable to retrieve post information: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end backfillReturn + +} // end class PublishedMessageImpl diff --git a/src/com/silverwrist/venice/core/impl/SIGCoreData.java b/src/com/silverwrist/venice/core/impl/SIGCoreData.java index c6f5549..c67be53 100644 --- a/src/com/silverwrist/venice/core/impl/SIGCoreData.java +++ b/src/com/silverwrist/venice/core/impl/SIGCoreData.java @@ -1852,7 +1852,8 @@ class SIGCoreData implements SIGData, SIGDataBackend } // end finally // Delete the rest of the gunk in the background; use another thread to do it. - BackgroundSIGPurge purger = new BackgroundSIGPurge(datapool,user,sigid,conf_count,conf_max,conf_objects); + BackgroundSIGPurge purger = new BackgroundSIGPurge(engine,datapool,user,sigid,conf_count,conf_max, + conf_objects); Thread thrd = new Thread(purger); thrd.setPriority(Thread.NORM_PRIORITY-1); thrd.start(); diff --git a/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java b/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java index 18ca619..dcff4ed 100644 --- a/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/TopicMessageUserContextImpl.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 @@ -24,6 +24,7 @@ import org.apache.log4j.*; import com.silverwrist.util.StringUtil; import com.silverwrist.venice.db.*; import com.silverwrist.venice.security.AuditRecord; +import com.silverwrist.venice.security.Capability; import com.silverwrist.venice.core.*; class TopicMessageUserContextImpl implements TopicMessageContext @@ -123,7 +124,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext private static String quickGetUserName(Connection conn, int uid) throws SQLException { Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE uid = " + String.valueOf(uid) + ";"); + ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE uid = " + uid + ";"); if (rs.next()) return rs.getString(1); else @@ -297,8 +298,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext if (scribble_date==null) { // let's go get the body text! Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT data FROM postdata WHERE postid = " - + String.valueOf(postid) + ";"); + ResultSet rs = stmt.executeQuery("SELECT data FROM postdata WHERE postid = " + postid + ";"); if (rs.next()) text_cache = rs.getString(1); else @@ -508,8 +508,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext // record what we did in an audit record ar = new AuditRecord(AuditRecord.HIDE_MESSAGE,conf.realUID(),conf.userRemoteAddress(), - conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",post=" - + String.valueOf(postid),flag ? "hide" : "unhide"); + conf.realSIGID(),"conf=" + conf.realConfID() + ",post=" + postid, + flag ? "hide" : "unhide"); } // end try catch (SQLException e) @@ -562,7 +562,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext Statement stmt = conn.createStatement(); // lock the tables we reference - stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE;"); + stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE, postpublish WRITE;"); try { // first, make sure we have the right status for our post refresh(conn); @@ -581,8 +581,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext stmt.executeUpdate(sql.toString()); // Determine if we need to "rub out" the post before we delete it. - ResultSet rs = stmt.executeQuery("SELECT LENGTH(data) FROM postdata WHERE postid = " - + String.valueOf(postid) + ";"); + ResultSet rs = stmt.executeQuery("SELECT LENGTH(data) FROM postdata WHERE postid = " + postid + ";"); if (rs.next()) { // use this data to overwrite the post with X's int len = rs.getInt(1); @@ -617,6 +616,12 @@ class TopicMessageUserContextImpl implements TopicMessageContext sql.append("DELETE FROM postattach WHERE postid = ").append(postid).append(';'); stmt.executeUpdate(sql.toString()); + // Un-publish the posting. + sql.setLength(0); + sql.append("DELETE FROM postpublish WHERE postid = ").append(postid).append(';'); + if (stmt.executeUpdate(sql.toString())>0) + engine.unpublish(postid); + // Update our internal data fields. linecount = 0; hidden = false; @@ -635,8 +640,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext // record what we did in an audit record ar = new AuditRecord(AuditRecord.SCRIBBLE_MESSAGE,conf.realUID(),conf.userRemoteAddress(), - conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",post=" - + String.valueOf(postid)); + conf.realSIGID(),"conf=" + conf.realConfID() + ",post=" + postid); } // end try catch (SQLException e) @@ -687,7 +691,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext Statement stmt = conn.createStatement(); // lock the tables we reference - stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE, postdogear WRITE;"); + stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE, postdogear WRITE, " + + "postpublish WRITE;"); try { // first, make sure we have the right status for our post @@ -696,10 +701,12 @@ class TopicMessageUserContextImpl implements TopicMessageContext return; // nuking a nuked post is futile // Delete any and all references to this post! - stmt.executeUpdate("DELETE FROM posts WHERE postid = " + String.valueOf(postid) + ";"); - stmt.executeUpdate("DELETE FROM postdata WHERE postid = " + String.valueOf(postid) + ";"); - stmt.executeUpdate("DELETE FROM postattach WHERE postid = " + String.valueOf(postid) + ";"); - stmt.executeUpdate("DELETE FROM postdogear WHERE postid = " + String.valueOf(postid) + ";"); + stmt.executeUpdate("DELETE FROM posts WHERE postid = " + postid + ";"); + stmt.executeUpdate("DELETE FROM postdata WHERE postid = " + postid + ";"); + stmt.executeUpdate("DELETE FROM postattach WHERE postid = " + postid + ";"); + stmt.executeUpdate("DELETE FROM postdogear WHERE postid = " + postid + ";"); + if (stmt.executeUpdate("DELETE FROM postpublish WHERE postid = " + postid + ";")>0) + engine.unpublish(postid); // Update our internal variables. linecount = 0; @@ -726,8 +733,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext // record what we did in an audit record ar = new AuditRecord(AuditRecord.NUKE_MESSAGE,conf.realUID(),conf.userRemoteAddress(), - conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",post=" - + String.valueOf(postid)); + conf.realSIGID(),"conf=" + conf.realConfID() + ",post=" + postid); } // end try catch (SQLException e) @@ -803,9 +809,9 @@ class TopicMessageUserContextImpl implements TopicMessageContext } // end if else if (length>MAX_ATTACH) { // the attachment is too damn long! - logger.error("attachment is too long (" + String.valueOf(length) + " bytes)"); - throw new AccessError("The attachment is too long to store. Maximum available length is " - + String.valueOf(MAX_ATTACH) + " bytes."); + logger.error("attachment is too long (" + length + " bytes)"); + throw new AccessError("The attachment is too long to store. Maximum available length is " + MAX_ATTACH + + " bytes."); } // end else if @@ -844,9 +850,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext // Generate an audit record indicating what we did. ar = new AuditRecord(AuditRecord.UPLOAD_ATTACHMENT,conf.realUID(),conf.userRemoteAddress(), - conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",post=" - + String.valueOf(postid),"len=" + String.valueOf(length) + ",type=" + m_type - + ",name=" + file); + conf.realSIGID(),"conf=" + conf.realConfID() + ",post=" + postid, + "len=" + length + ",type=" + m_type + ",name=" + file); } // end try catch (SQLException e) @@ -876,6 +881,136 @@ class TopicMessageUserContextImpl implements TopicMessageContext } // end attachData + public boolean canPublish() + { + if (!(Capability.canPublishToFrontPage(conf.realBaseLevel()))) + return false; // must be a sysadmin to publish + if ((scribble_date!=null) || nuked) + return false; // cannot publish a scribbled or nuked message + + Connection conn = null; + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // see if the post has already been published + ResultSet rs = stmt.executeQuery("SELECT by_uid FROM postpublish WHERE postid = " + postid + + " LIMIT 1;"); + return !(rs.next()); + + } // end try + catch (SQLException e) + { // just trap SQL exceptions and log them + logger.error("SQL exception in TopicMessageUserContextImpl.canPublish: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return false; // assume we can't + + } // end canPublish + + public void publish() throws DataException, AccessError + { + if (!(Capability.canPublishToFrontPage(conf.realBaseLevel()))) + { // you aren't allowed to publish - naughty naughty! + logger.error("unable to publish because we're not allowed"); + throw new AccessError("You are not permitted to publish postings to the front page."); + + } // end if + + if (nuked) + { // we can't publish a nuked message! + logger.error("unable to publish because message nuked"); + throw new DataException("Cannot publish a message that has been nuked."); + + } // end if + + if (scribble_date!=null) + { // we can't publish a scribbled message! + logger.error("unable to publish because message scribbled"); + throw new DataException("Cannot publish a message that has been scribbled."); + + } // end if + + Connection conn = null; + AuditRecord ar = null; + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // see if post has already been published + ResultSet rs = stmt.executeQuery("SELECT by_uid FROM postpublish WHERE postid = " + postid + + " LIMIT 1;"); + if (rs.next()) // can't do it, friend! + throw new DataException("This posting has already been published."); + + boolean done = false; + engine.startPublish(); + + try + { // insert the post reference into the database + StringBuffer sql = + new StringBuffer("INSERT INTO postpublish (sigid, postid, by_uid, on_date) VALUES ("); + sql.append(conf.realSIGID()).append(", ").append(postid).append(", ").append(conf.realUID()); + java.util.Date now = new java.util.Date(); + sql.append(", '").append(SQLUtil.encodeDate(now)).append("');"); + stmt.executeUpdate(sql.toString()); + + // generate an audit record indicating what we've done + ar = new AuditRecord(AuditRecord.PUBLISH_POST,conf.realUID(),conf.userRemoteAddress(), + conf.realSIGID(),"conf=" + conf.realConfID() + ",post=" + postid); + + // establish cached data object for front page + engine.publishNew(new PublishedMessageImpl(datapool,postid,parent,num,linecount,creator_uid, + posted,pseud,creator_cache,text_cache)); + done = true; + + } // end try + finally + { // make sure to release the lock if we goofed in here + if (!done) + engine.publishNew(null); + + } // end finally + + } // end try + catch (SQLException e) + { // just trap SQL exceptions and log them + logger.error("unable to publish posting: " + e.getMessage(),e); + throw new DataException("unable to publish posting: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection 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 finally + + } // end publish + /*-------------------------------------------------------------------------------- * External static operations *-------------------------------------------------------------------------------- @@ -885,9 +1020,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext int post_low, int post_high) throws DataException { if (logger.isDebugEnabled()) - logger.debug("loadMessageRange for conf # " + String.valueOf(conf.realConfID()) + ", topic #" - + String.valueOf(topicid) + ", range [" + String.valueOf(post_low) + ", " - + String.valueOf(post_high) + "]"); + logger.debug("loadMessageRange for conf # " + conf.realConfID() + ", topic #" + topicid + ", range [" + + post_low + ", " + post_high + "]"); Vector rc = new Vector(); Connection conn = null; // pooled database connection @@ -942,8 +1076,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext int topicid, int message_num) throws DataException { if (logger.isDebugEnabled()) - logger.debug("loadMessage for conf # " + String.valueOf(conf.realConfID()) + ", topic #" - + String.valueOf(topicid) + ", message " + String.valueOf(message_num)); + logger.debug("loadMessage for conf # " + conf.realConfID() + ", topic #" + topicid + ", message " + + message_num); Connection conn = null; // pooled database connection @@ -993,8 +1127,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext long postid) throws DataException { if (logger.isDebugEnabled()) - logger.debug("getMessage for conf # " + String.valueOf(conf.realConfID()) + ", post #" - + String.valueOf(postid)); + logger.debug("getMessage for conf # " + conf.realConfID() + ", post #" + postid); Connection conn = null; // pooled database connection diff --git a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java index bf73bda..302800d 100644 --- a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java @@ -880,7 +880,7 @@ class TopicUserContextImpl implements TopicContext } // end finally // Delete the rest of the gunk in the background; spin off another thread to handle it. - BackgroundTopicPurge purger = new BackgroundTopicPurge(datapool,topicid,post_count,post_max); + BackgroundTopicPurge purger = new BackgroundTopicPurge(engine,datapool,topicid,post_count,post_max); Thread thrd = new Thread(purger); thrd.setPriority(Thread.NORM_PRIORITY-1); thrd.start(); diff --git a/src/com/silverwrist/venice/core/impl/UserContextImpl.java b/src/com/silverwrist/venice/core/impl/UserContextImpl.java index 98e9a68..365e0b6 100644 --- a/src/com/silverwrist/venice/core/impl/UserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/UserContextImpl.java @@ -20,6 +20,7 @@ package com.silverwrist.venice.core.impl; import java.util.*; import java.sql.*; import org.apache.log4j.*; +import com.silverwrist.util.LocaleFactory; import com.silverwrist.util.StringUtil; import com.silverwrist.venice.*; import com.silverwrist.venice.core.*; @@ -59,6 +60,8 @@ class UserContextImpl implements UserContext, UserBackend private String my_email = null; // my email address (cached) private String my_pseud = null; // my pseud (cached) private String full_name = null; // my full name (cached) + private Locale my_locale = null; // my default locale (cached) + private TimeZone my_tz = null; // my default timezone (cached) /*-------------------------------------------------------------------------------- * Constructor @@ -84,6 +87,12 @@ class UserContextImpl implements UserContext, UserBackend username = null; created = null; last_access = null; + description = null; + my_email = null; + my_pseud = null; + full_name = null; + my_locale = null; + my_tz = null; } // end finalize @@ -108,18 +117,64 @@ class UserContextImpl implements UserContext, UserBackend // skip field "passreminder" description = rs.getString("description"); + // purge any "cached" fields that may be left over + my_email = null; + my_pseud = null; + full_name = null; + my_locale = null; + my_tz = null; + if (logger.isDebugEnabled()) { // spit it all out to debug info - logger.debug("Loaded: UID " + String.valueOf(uid) + ", username \"" + username + "\", contactid " - + String.valueOf(contactid)); - logger.debug("...is_anon " + String.valueOf(is_anon) + ", email_verified " - + String.valueOf(email_verified)); - logger.debug("... level " + String.valueOf(level)); + logger.debug("Loaded: UID " + uid + ", username \"" + username + "\", contactid " + contactid); + logger.debug("...is_anon " + is_anon + ", email_verified " + email_verified); + logger.debug("... level " + level); } // end if } // end loadUserData + private void loadPrefs(Connection conn) throws SQLException, DataException + { + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM userprefs WHERE uid = " + uid + ";"); + + if (!(rs.next())) + throw new DataException("cannot find preferences for user"); + + if (my_tz==null) + my_tz = TimeZone.getTimeZone(rs.getString("tzid")); + + if (my_locale==null) + my_locale = LocaleFactory.createLocale(rs.getString("localeid")); + + } // end loadPrefs + + private void loadPrefs() throws DataException + { + Connection conn = null; + + try + { // call through to lower level function + conn = datapool.getConnection(); + loadPrefs(conn); + + } // end try + catch (SQLException e) + { // translate into DataException, yadda yadda yadda + logger.error("SQL error reading preferences: " + e.getMessage(),e); + throw new DataException("Unable to read user preferences: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end loadPrefs + private void sendEmailConfirmation() throws DataException, EmailException { if (logger.isDebugEnabled()) @@ -180,7 +235,7 @@ class UserContextImpl implements UserContext, UserBackend public boolean isLoggedIn() { if (logger.isDebugEnabled()) - logger.debug("isLoggedIn(): uid = " + String.valueOf(uid) + ", is_anon = " + String.valueOf(is_anon)); + logger.debug("isLoggedIn(): uid = " + uid + ", is_anon = " + is_anon); return ((uid!=-1) && !is_anon); } // end is_logged_in @@ -195,7 +250,7 @@ class UserContextImpl implements UserContext, UserBackend { if (isLoggedIn()) { // already authenticated, can't authenticate again - logger.error("UserContext already authenticated (with uid " + String.valueOf(uid) + ")"); + logger.error("UserContext already authenticated (with uid " + uid + ")"); throw new InternalStateError("context already authenticated"); } // end if @@ -302,7 +357,7 @@ class UserContextImpl implements UserContext, UserBackend public void confirmEmail(int conf_num) throws AccessError, DataException { if (logger.isDebugEnabled()) - logger.debug("confirmEmail(): confirming for UID " + String.valueOf(uid)); + logger.debug("confirmEmail(): confirming for UID " + uid); if ((email_verified) || Capability.exemptFromEmailVerification(level)) { // already confirmed if (logger.isDebugEnabled()) @@ -368,7 +423,7 @@ class UserContextImpl implements UserContext, UserBackend public void resendEmailConfirmation() throws DataException, EmailException { if (logger.isDebugEnabled()) - logger.debug("resendEmailConfirmation(): resending for UID " + String.valueOf(uid)); + logger.debug("resendEmailConfirmation(): resending for UID " + uid); if ((email_verified) || Capability.exemptFromEmailVerification(level)) { // already confirmed, no need to resend if (logger.isDebugEnabled()) @@ -433,6 +488,9 @@ class UserContextImpl implements UserContext, UserBackend public ContactInfo getContactInfo() throws DataException { + if (logger.isDebugEnabled()) + logger.debug("getContactInfo() for UID " + uid); + ContactInfoImpl rc; if (contactid>=0) rc = new ContactInfoImpl(datapool,contactid); @@ -450,11 +508,14 @@ class UserContextImpl implements UserContext, UserBackend public boolean putContactInfo(ContactInfo ci) throws DataException, EmailException { + if (logger.isDebugEnabled()) + logger.debug("putContactInfo() for UID " + uid); + boolean email_changed = false; if ((ci.getOwnerUID()!=uid) || (ci.getOwnerSIGID()>=0)) { // the contact information is not owned correctly - logger.error("ContactInfo ownership wrong (it's " + String.valueOf(ci.getOwnerUID()) + ", " - + String.valueOf(ci.getOwnerSIGID()) + "), should be (" + String.valueOf(uid) + ", -1)"); + logger.error("ContactInfo ownership wrong (it's " + ci.getOwnerUID() + ", " + ci.getOwnerSIGID() + + "), should be (" + uid + ", -1)"); throw new DataException("invalid contact information record"); } // end if @@ -481,7 +542,7 @@ class UserContextImpl implements UserContext, UserBackend { // contact being established for the first time contactid = ci.getContactID(); if (logger.isDebugEnabled()) - logger.debug("...established initial contact (" + String.valueOf(contactid) + ") for user"); + logger.debug("...established initial contact (" + contactid + ") for user"); my_email = ci.getEmail(); sendEmailConfirmation(); email_changed = true; @@ -524,8 +585,7 @@ class UserContextImpl implements UserContext, UserBackend } // end else if - ar = new AuditRecord(AuditRecord.USER_CONTACT_INFO,uid,remote_addr, - "contactid=" + String.valueOf(contactid)); + ar = new AuditRecord(AuditRecord.USER_CONTACT_INFO,uid,remote_addr,"contactid=" + contactid); } // end try catch (ClassCastException cce) @@ -597,7 +657,7 @@ class UserContextImpl implements UserContext, UserBackend public UserProfile getProfile(int xuid) throws DataException { if (logger.isDebugEnabled()) - logger.debug("getProfile(#" + String.valueOf(xuid) + ")..."); + logger.debug("getProfile(#" + xuid + ")..."); Connection conn = null; try @@ -893,6 +953,94 @@ class UserContextImpl implements UserContext, UserBackend } // end getAdminInterface + public Locale getLocale() throws DataException + { + if (my_locale==null) + loadPrefs(); + + return my_locale; + + } // end getLocale + + public void setLocale(Locale locale) throws DataException + { + Connection conn = null; + + try + { // retrieve a connection from the data pool + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // create the update statement + StringBuffer sql = new StringBuffer("UPDATE userprefs SET localeid = '"); + sql.append(SQLUtil.encodeString(locale.toString())).append("' WHERE uid = ").append(uid).append(';'); + + // execute the statement + stmt.executeUpdate(sql.toString()); + + // replace the locale here + my_locale = locale; + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error setting user locale: " + e.getMessage(),e); + throw new DataException("unable to set user locale: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end setLocale + + public TimeZone getTimeZone() throws DataException + { + if (my_tz==null) + loadPrefs(); + + return my_tz; + + } // end getTimeZone + + public void setTimeZone(TimeZone timezone) throws DataException + { + Connection conn = null; + + try + { // retrieve a connection from the data pool + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // create the update statement + StringBuffer sql = new StringBuffer("UPDATE userprefs SET tzid = '"); + sql.append(SQLUtil.encodeString(timezone.getID())).append("' WHERE uid = ").append(uid).append(';'); + + // execute the statement + stmt.executeUpdate(sql.toString()); + + // replace the locale here + my_tz = timezone; + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error setting user timezone: " + e.getMessage(),e); + throw new DataException("unable to set user timezone: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end setTimeZone + /*-------------------------------------------------------------------------------- * Implementations from interface UserBackend *-------------------------------------------------------------------------------- @@ -918,6 +1066,8 @@ class UserContextImpl implements UserContext, UserBackend public String userDefaultPseud() throws DataException { + if (logger.isDebugEnabled()) + logger.debug("userDefaultPseud() for UID " + uid); if (my_pseud==null) getContactInfo(); return my_pseud; @@ -999,8 +1149,8 @@ class UserContextImpl implements UserContext, UserBackend java.util.Date created, java.util.Date last_access) { if (logger.isDebugEnabled()) - logger.debug("loadNewUser() on UserContext: addr " + remote_addr + ", uid " + String.valueOf(uid) - + ", level " + String.valueOf(level) + ", username \"" + username + "\""); + logger.debug("loadNewUser() on UserContext: addr " + remote_addr + ", uid " + uid + ", level " + + level + ", username \"" + username + "\""); this.remote_addr = remote_addr; this.uid = uid; diff --git a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java index be0aa15..615a2ae 100644 --- a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java +++ b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java @@ -304,6 +304,8 @@ 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 boolean cache_fp_posts_busy = false; // busy flag for above vector /*-------------------------------------------------------------------------------- * Constructor @@ -333,8 +335,8 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend private void loadDefaults(Statement stmt) throws SQLException, DataException { final String query = - "SELECT posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page " - + "FROM globals;"; + "SELECT posts_per_page, old_posts_at_top, max_search_page, max_sig_mbr_page, max_conf_mbr_page, " + + "fp_posts FROM globals;"; ResultSet rs = stmt.executeQuery(query); if (!(rs.next())) throw new DataException("Globals table does not appear to be loaded!"); @@ -345,6 +347,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend gp_ints[IP_MAXSEARCHRETURN] = rs.getInt(3); gp_ints[IP_MAXSIGMEMBERDISPLAY] = rs.getInt(4); gp_ints[IP_MAXCONFMEMBERDISPLAY] = rs.getInt(5); + gp_ints[IP_NUMFRONTPAGEPOSTS] = rs.getInt(6); } // end loadDefaults @@ -489,7 +492,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end catch // Allocate the global parameter arrays. - gp_ints = new int[5]; + gp_ints = new int[6]; // initialize anything that requires us to pull from the database Connection conn = null; @@ -1391,6 +1394,48 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end getMasterSideBoxList + public List getPublishedMessages(boolean all) throws DataException + { + Vector rc = new Vector(); + + synchronized (this) + { // Make sure the cache is in condition. + while (cache_fp_posts_busy) + { // the list is busy, don't fsck with it until we're done + try + { // wait to see if they can be un-busy + wait(); + + } // end try + catch (InterruptedException e) + { // do nothing + } // end catch + + } // end while + + // If the cache list contains too few items, backfill it from the database. + if (cache_fp_posts.size()gp_ints[IP_NUMFRONTPAGEPOSTS]) + cache_fp_posts.remove(cache_fp_posts.size()-1); + + } // end pubmsg + + cache_fp_posts_busy = false; + notifyAll(); + + } // end publishNew + + public synchronized void unpublish(long postid) + { + Iterator it = cache_fp_posts.iterator(); + while (it.hasNext()) + { // get each published message in the cache in turn, looking for the specified post ID + PublishedMessageImpl pmi = (PublishedMessageImpl)(it.next()); + if (pmi.getPostID()==postid) + { // drop the specified post ID like a hot rock + it.remove(); + break; + + } // end if + + } // end while + + } // end unpublish + } // end class VeniceEngineImpl diff --git a/src/com/silverwrist/venice/security/Audit.java b/src/com/silverwrist/venice/security/Audit.java index c583c6d..a8ac24b 100644 --- a/src/com/silverwrist/venice/security/Audit.java +++ b/src/com/silverwrist/venice/security/Audit.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 @@ -19,7 +19,8 @@ package com.silverwrist.venice.security; public interface Audit { - // Codes 0-100 - System events + // Codes 1-100 - System events + public static final int PUBLISH_POST = 1; // Codes 101-200 - Login/user events public static final int LOGIN_OK = 101; diff --git a/src/com/silverwrist/venice/security/Capability.java b/src/com/silverwrist/venice/security/Capability.java index b53f1cc..c3c0405 100644 --- a/src/com/silverwrist/venice/security/Capability.java +++ b/src/com/silverwrist/venice/security/Capability.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 @@ -103,4 +103,10 @@ public class Capability implements SecLevels } // end canAdministerSystem + public static boolean canPublishToFrontPage(int level) + { + return (level>=GLOBAL_ANYADMIN); + + } // end canPublishToFrontPage + } // end class Capability diff --git a/src/com/silverwrist/venice/servlets/PostOperations.java b/src/com/silverwrist/venice/servlets/PostOperations.java index 48187d8..0702adb 100644 --- a/src/com/silverwrist/venice/servlets/PostOperations.java +++ b/src/com/silverwrist/venice/servlets/PostOperations.java @@ -172,6 +172,30 @@ public class PostOperations extends VeniceServlet } // end if ("nuke") + if (cmd.equals("PU")) + { // we want to publish the message to the front page + try + { // attempt to publish the message + msg.publish(); + + // go back and display stuff + throw new RedirectResult(location); + + } // end try + catch (DataException de) + { // there was a database error + return new ErrorBox("Database Error","Database error publishing message: " + de.getMessage(), + location); + + } // end catch + catch (AccessError ae) + { // naughty naughty = you can't do this! + return new ErrorBox("Access Error",ae.getMessage(),location); + + } // end catch + + } // end if + // unrecognized command! logger.error("invalid command to PostOperations.doGet: " + cmd); return new ErrorBox("Internal Error","Invalid command to PostOperations.doGet",location); diff --git a/src/com/silverwrist/venice/servlets/Top.java b/src/com/silverwrist/venice/servlets/Top.java index 7b9477a..aafdc17 100644 --- a/src/com/silverwrist/venice/servlets/Top.java +++ b/src/com/silverwrist/venice/servlets/Top.java @@ -76,7 +76,7 @@ public class Top extends VeniceServlet try { // attempt to get the user content - return new TopDisplay(getServletContext(),user); + return new TopDisplay(getServletContext(),engine,user); } // end try catch (DataException de) diff --git a/src/com/silverwrist/venice/servlets/VeniceServlet.java b/src/com/silverwrist/venice/servlets/VeniceServlet.java index 4297f3b..a4288bb 100644 --- a/src/com/silverwrist/venice/servlets/VeniceServlet.java +++ b/src/com/silverwrist/venice/servlets/VeniceServlet.java @@ -565,7 +565,7 @@ public abstract class VeniceServlet extends HttpServlet ServletContext ctxt = getServletContext(); VeniceEngine engine = Variables.getVeniceEngine(ctxt); UserContext user = Variables.getUserContext(ctxt,request,request.getSession(true)); - RenderData rdat = RenderConfig.createRenderData(ctxt,request,response); + RenderData rdat = RenderConfig.createRenderData(ctxt,user,request,response); ServletMultipartHandler mphandler = null; VeniceContent content = null; diff --git a/src/com/silverwrist/venice/servlets/format/CDCountryListFormField.java b/src/com/silverwrist/venice/servlets/format/CDCountryListFormField.java index dda4985..57b48c6 100644 --- a/src/com/silverwrist/venice/servlets/format/CDCountryListFormField.java +++ b/src/com/silverwrist/venice/servlets/format/CDCountryListFormField.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 @@ -25,6 +25,11 @@ import com.silverwrist.venice.core.Country; public class CDCountryListFormField extends CDPickListFormField { + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + public CDCountryListFormField(String name, String caption, String caption2, boolean required, List country_list) { @@ -38,6 +43,11 @@ public class CDCountryListFormField extends CDPickListFormField } // end constructor + /*-------------------------------------------------------------------------------- + * Overrides from class CDPickListFormField + *-------------------------------------------------------------------------------- + */ + protected void renderChoice(Writer out, RenderData rdat, Object obj, String my_value) throws IOException { Country c = (Country)obj; @@ -48,6 +58,11 @@ public class CDCountryListFormField extends CDPickListFormField } // end renderChoice + /*-------------------------------------------------------------------------------- + * Implementations from interface CDFormField + *-------------------------------------------------------------------------------- + */ + public CDFormField duplicate() { return new CDCountryListFormField(this); diff --git a/src/com/silverwrist/venice/servlets/format/CDLocaleListFormField.java b/src/com/silverwrist/venice/servlets/format/CDLocaleListFormField.java new file mode 100644 index 0000000..17dd8d2 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/CDLocaleListFormField.java @@ -0,0 +1,106 @@ +/* + * 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 java.io.Writer; +import java.io.IOException; +import com.silverwrist.util.StringUtil; + +public class CDLocaleListFormField extends CDPickListFormField +{ + /*-------------------------------------------------------------------------------- + * Private list implementation + *-------------------------------------------------------------------------------- + */ + + static class LocaleList extends AbstractList + { + private Locale[] array; // local array + + LocaleList() + { + array = Locale.getAvailableLocales(); + + } // end constructor + + protected void finalize() throws Throwable + { + array = null; + super.finalize(); + + } // end finalize + + public Object get(int index) + { + return array[index]; + + } // end get + + public int size() + { + return array.length; + + } // end size + + } // end class LocaleList + + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + + public CDLocaleListFormField(String name, String caption, String caption2, boolean required) + { + super(name,caption,caption2,required,new LocaleList()); + + } // end constructor + + protected CDLocaleListFormField(CDLocaleListFormField other) + { + super(other); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Overrides from class CDPickListFormField + *-------------------------------------------------------------------------------- + */ + + protected void renderChoice(Writer out, RenderData rdat, Object obj, String my_value) throws IOException + { + Locale l = (Locale)obj; + out.write("\n"); + + } // end renderChoice + + /*-------------------------------------------------------------------------------- + * Implementations from interface CDFormField + *-------------------------------------------------------------------------------- + */ + + public CDFormField duplicate() + { + return new CDLocaleListFormField(this); + + } // end duplicate + +} // end class CDLocaleListFormField diff --git a/src/com/silverwrist/venice/servlets/format/CDPickListFormField.java b/src/com/silverwrist/venice/servlets/format/CDPickListFormField.java index bd67ee9..0590f56 100644 --- a/src/com/silverwrist/venice/servlets/format/CDPickListFormField.java +++ b/src/com/silverwrist/venice/servlets/format/CDPickListFormField.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 @@ -25,8 +25,18 @@ import com.silverwrist.util.StringUtil; public abstract class CDPickListFormField extends CDBaseFormField { + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + private List choices; + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + protected CDPickListFormField(String name, String caption, String caption2, boolean required, List choices) { @@ -42,9 +52,19 @@ public abstract class CDPickListFormField extends CDBaseFormField } // end constructor + /*-------------------------------------------------------------------------------- + * Abstract method declarations + *-------------------------------------------------------------------------------- + */ + protected abstract void renderChoice(Writer out, RenderData rdat, Object obj, String my_value) throws IOException; + /*-------------------------------------------------------------------------------- + * Overrides from class CDBaseFormField + *-------------------------------------------------------------------------------- + */ + protected void renderActualField(Writer out, RenderData rdat) throws IOException { out.write("