diff --git a/doc/buttons/bn_go.png b/doc/buttons/bn_go.png deleted file mode 100644 index dac836f..0000000 Binary files a/doc/buttons/bn_go.png and /dev/null differ diff --git a/doc/buttons/bn_hide.png b/doc/buttons/bn_hide.png deleted file mode 100644 index 7739c59..0000000 Binary files a/doc/buttons/bn_hide.png and /dev/null differ diff --git a/doc/buttons/bn_post_go_next.png b/doc/buttons/bn_post_go_next.png deleted file mode 100644 index 4c87e8e..0000000 Binary files a/doc/buttons/bn_post_go_next.png and /dev/null differ diff --git a/doc/buttons/bn_post_reload.png b/doc/buttons/bn_post_reload.png deleted file mode 100644 index a3e1c39..0000000 Binary files a/doc/buttons/bn_post_reload.png and /dev/null differ diff --git a/doc/buttons/bn_scribble.png b/doc/buttons/bn_scribble.png deleted file mode 100644 index c7c4ef3..0000000 Binary files a/doc/buttons/bn_scribble.png and /dev/null differ diff --git a/doc/design-goals.html b/doc/design-goals.html index 63d2ff4..a8645ae 100644 --- a/doc/design-goals.html +++ b/doc/design-goals.html @@ -51,7 +51,7 @@
  • Output uses XML and gets formatted into [X]HTML via XSLT; "themeable"/"skinnable" interface
  • Conferencing/other functionality available via XML-RPC or SOAP calls
  • News page creation (see Slash, Squishdot, Scoop)
  • -
  • Member trust metrics (see Slashdot "karma," Scoop "mojo," Advogato distributed trust metric)
  • +
  • Member trust metrics (see Slashdot "karma," Scoop "mojo," Advogato distributed trust metric) (Feature indefinitely shelved at the request of the EMinds community)
  • User diary pages (see Advogato, Kuro5hin)
  • Moderated discussions (see Slash, Scoop)
  • Collaborative database facility (see Wiki, Everything2)
  • diff --git a/etc/erbo.dict b/etc/erbo.dict index d0c9b0f..7a6d8e7 100644 --- a/etc/erbo.dict +++ b/etc/erbo.dict @@ -1,8 +1,105 @@ +advogato +ain't +anla'shok +bajor +bios +boitano +boromax +can't +cartman +checkouts +couldn't +crewmember +crewmembers +deflector +deflectors +delenn +didn't +dilithium +docking +doesn't +don't eminds +entil'zha +eps erbo +fett +followup +franklin +fucking +hairstyle +hairstyles +hasn't +he'd +html +i'd +i'll +i'm +i've +inducers +it'd +khan +kubla +kyle +lafou +ma'am maddog +marillion +minbar +minbari +mr +mustn't +nacelle +nacelles +navigational +ops +padd +peachy +planitia +planum +psi +refit +refitting +replicator +repost +reposted +rom +runabout +salchow +salchows +sarcastically +silverwrist snarf snarfage +snazzy +sourceforge +spaceport +spellchecker +spellchucker +spelunker +spelunkers +st +starbase +starfleet +starship +straightening +stunted +terrence +they're +turbolift +tuzanor +umbilical +umbilicals +url utne +valen +veni +we'll +we're webb webbme +won't +wouldn't +you'd +you'll +you're diff --git a/etc/web.xml b/etc/web.xml index c4e65e7..89401ff 100644 --- a/etc/web.xml +++ b/etc/web.xml @@ -142,6 +142,14 @@ com.silverwrist.venice.servlets.ConfDisplay + + postmessage + + Posting messages to a conference. + + com.silverwrist.venice.servlets.PostMessage + + @@ -200,6 +208,11 @@ /confdisp + + postmessage + /post + + testformdata diff --git a/setup/database.sql b/setup/database.sql index d9c47de..e2b242e 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -416,6 +416,10 @@ INSERT INTO refaudit (type, descr) VALUES (307, 'Delete Topic'), (308, 'Set Topic Frozen'), (309, 'Set Topic Archive'), + (310, 'Post Message'), + (311, 'Hide Message'), + (312, 'Scribble Message'), + (313, 'Nuke Message'), (9999999, 'DUMMY'); # The ISO 3166 two-letter country codes. Source is diff --git a/src/com/silverwrist/venice/core/TopicContext.java b/src/com/silverwrist/venice/core/TopicContext.java index c3e36dd..b95e37a 100644 --- a/src/com/silverwrist/venice/core/TopicContext.java +++ b/src/com/silverwrist/venice/core/TopicContext.java @@ -64,4 +64,9 @@ public interface TopicContext public abstract void fixSeen() throws DataException; + public abstract List getMessages(int low, int high) throws DataException, AccessError; + + public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text) + throws DataException, AccessError; + } // end interface TopicContext diff --git a/src/com/silverwrist/venice/core/TopicMessageContext.java b/src/com/silverwrist/venice/core/TopicMessageContext.java new file mode 100644 index 0000000..6e8a01a --- /dev/null +++ b/src/com/silverwrist/venice/core/TopicMessageContext.java @@ -0,0 +1,64 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Community System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core; + +import java.util.Date; + +public interface TopicMessageContext +{ + public abstract long getPostID(); + + public abstract long getParentPostID(); + + public abstract int getPostNumber(); + + public abstract int getNumLines(); + + public abstract int getCreatorUID(); + + public abstract String getCreatorName() throws DataException; + + public abstract Date getPostDate(); + + public abstract boolean isHidden(); + + public abstract boolean isScribbled(); + + public abstract boolean isNuked(); + + public abstract Date getScribbleDate(); + + public abstract String getPseud(); + + public abstract String getBodyText() throws DataException; + + public abstract boolean hasAttachment(); + + public abstract boolean canHide(); + + public abstract boolean canScribble(); + + public abstract boolean canNuke(); + + public abstract void setHidden(boolean flag) throws DataException, AccessError; + + public abstract void scribble() throws DataException, AccessError; + + public abstract void nuke() throws DataException, AccessError; + +} // end interface TopicMessageContext diff --git a/src/com/silverwrist/venice/core/impl/ConferenceBackend.java b/src/com/silverwrist/venice/core/impl/ConferenceBackend.java index a04866a..14fdcdc 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceBackend.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceBackend.java @@ -17,8 +17,10 @@ */ package com.silverwrist.venice.core.impl; -import java.sql.*; +import java.sql.Connection; +import java.sql.SQLException; import java.util.Date; +import com.silverwrist.venice.core.DataException; public interface ConferenceBackend extends SIGBackend { @@ -32,4 +34,14 @@ public interface ConferenceBackend extends SIGBackend public abstract String realConfAlias(); + public abstract boolean userCanScribble(); + + public abstract boolean userCanNuke(); + + public abstract boolean userCanRead(); + + public abstract boolean userCanPost(); + + public abstract void touchUpdate(Connection conn, java.util.Date date) throws DataException; + } // end interface ConferenceBackend diff --git a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java index bc48d4a..2830939 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceCoreData.java @@ -932,6 +932,37 @@ class ConferenceCoreData implements ConferenceData } // end createNewTopic + public boolean canScribblePosts(int level) + { + return (level>=nuke_level); + + } // end canScribblePosts + + public boolean canNukePosts(int level) + { + return (level>=nuke_level); + + } // end canNukePosts + + public synchronized void touchUpdate(Connection conn, java.util.Date date) throws DataException + { + try + { // update the last update date + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("UPDATE confs SET lastupdate = '"); + sql.append(SQLUtil.encodeDate(date)).append("' WHERE confid = ").append(confid).append(';'); + stmt.executeUpdate(sql.toString()); + last_update = date; + + } // end try + catch (SQLException e) + { // convert the SQLException + throw new DataException("Database error updating conference: " + e.getMessage(),e); + + } // end catch + + } // end touchUpdate + /*-------------------------------------------------------------------------------- * External static operations (usable only from within package) *-------------------------------------------------------------------------------- diff --git a/src/com/silverwrist/venice/core/impl/ConferenceData.java b/src/com/silverwrist/venice/core/impl/ConferenceData.java index 6765d21..3e38d3e 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceData.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceData.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core.impl; +import java.sql.Connection; import java.util.Date; import java.util.List; import com.silverwrist.venice.core.DataException; @@ -79,4 +80,10 @@ public interface ConferenceData extends ReferencedData public abstract ReturnTopicInfo createNewTopic(SIGBackend sig, String title, String pseud, String body, int body_lines) throws DataException; + public abstract boolean canScribblePosts(int level); + + public abstract boolean canNukePosts(int level); + + public abstract void touchUpdate(Connection conn, Date date) throws DataException; + } // end interface ConferenceData diff --git a/src/com/silverwrist/venice/core/impl/ConferenceSIGContext.java b/src/com/silverwrist/venice/core/impl/ConferenceSIGContext.java index 80728c2..47e4e8b 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceSIGContext.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceSIGContext.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core.impl; +import java.sql.Connection; import java.util.Date; import java.util.List; import com.silverwrist.venice.core.DataException; @@ -91,4 +92,10 @@ public interface ConferenceSIGContext extends ReferencedData public abstract ReturnTopicInfo createNewTopic(SIGBackend sig, String title, String pseud, String body, int body_lines) throws DataException; + public abstract boolean canScribblePosts(int level); + + public abstract boolean canNukePosts(int level); + + public abstract void touchUpdate(Connection conn, Date date) throws DataException; + } // end interface ConferenceSIGContext diff --git a/src/com/silverwrist/venice/core/impl/ConferenceSIGContextImpl.java b/src/com/silverwrist/venice/core/impl/ConferenceSIGContextImpl.java index 91ab121..d5c146f 100644 --- a/src/com/silverwrist/venice/core/impl/ConferenceSIGContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/ConferenceSIGContextImpl.java @@ -618,4 +618,34 @@ class ConferenceSIGContextImpl implements ConferenceSIGContext } // end createNewTopic + public boolean canScribblePosts(int level) + { + ConferenceData c = getConferenceDataNE(); + if (c==null) + return false; + if (level. + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Community System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core.impl; + +import java.sql.*; +import java.util.*; +import org.apache.log4j.*; +import com.silverwrist.venice.db.*; +import com.silverwrist.venice.security.AuditRecord; +import com.silverwrist.venice.core.*; + +class TopicMessageUserContextImpl implements TopicMessageContext +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(TopicMessageUserContextImpl.class.getName()); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private EngineBackend engine; + private ConferenceBackend conf; + 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 boolean hidden; + private int scribble_uid; + private java.util.Date scribble_date; + private String pseud; + private int datalen; + private String filename; + private String mimetype; + private boolean nuked = false; + private String creator_cache = null; + private String text_cache = null; + + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + + protected TopicMessageUserContextImpl(EngineBackend engine, ConferenceBackend conf, DataPool datapool, + long postid, long parent, int num, int linecount, int creator_uid, + java.util.Date posted, boolean hidden, int scribble_uid, + java.util.Date scribble_date, String pseud, int datalen, + String filename, String mimetype) + { + this.engine = engine; + this.conf = conf; + this.datapool = datapool; + this.postid = postid; + this.parent = parent; + this.num = num; + this.linecount = linecount; + this.creator_uid = creator_uid; + this.posted = posted; + this.hidden = hidden; + this.scribble_uid = scribble_uid; + this.scribble_date = scribble_date; + this.pseud = pseud; + this.datalen = datalen; + this.filename = filename; + this.mimetype = mimetype; + + } // end constructor + + TopicMessageUserContextImpl(EngineBackend engine, ConferenceBackend conf, DataPool datapool, + long postid, long parent, int num, int linecount, int creator_uid, + java.util.Date posted, String pseud) + { + this.engine = engine; + this.conf = conf; + this.datapool = datapool; + this.postid = postid; + this.parent = parent; + this.num = num; + this.linecount = linecount; + this.creator_uid = creator_uid; + this.posted = posted; + this.hidden = false; + this.scribble_uid = 0; + this.scribble_date = null; + this.pseud = pseud; + this.datalen = 0; + this.filename = null; + this.mimetype = null; + + } // 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 + + private void refresh(Connection conn) throws SQLException + { + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("SELECT p.hidden, p.scribble_uid, p.scribble_date, p.pseud, " + + "a.datalen, a.filename, a.mimetype FROM posts p LEFT JOIN " + + "postattach a ON p.postid = a.postid WHERE p.postid = "); + sql.append(postid).append(';'); + ResultSet rs = stmt.executeQuery(sql.toString()); + if (rs.next()) + { // update a variety of fields + hidden = rs.getBoolean(1); + scribble_uid = rs.getInt(2); + scribble_date = SQLUtil.getFullDateTime(rs,3); + pseud = rs.getString(4); + datalen = rs.getInt(5); + filename = rs.getString(6); + mimetype = rs.getString(7); + + } // end if + else + { // the post has been nuked - update accordingly + linecount = 0; + creator_uid = -1; + posted = null; + hidden = false; + scribble_uid = -1; + scribble_date = null; + pseud = null; + datalen = 0; + filename = null; + mimetype = null; + nuked = true; + creator_cache = null; + text_cache = null; + + } // end else + + } // end refresh + + /*-------------------------------------------------------------------------------- + * 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 + if (nuked) + return null; // post nuked! + Connection conn = null; + + try + { // use a database connection to get the user name + conn = datapool.getConnection(); + refresh(conn); + if (nuked) + return null; // post nuked! + 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 hidden; + + } // end isHidden + + public boolean isScribbled() + { + return (scribble_date!=null); + + } // end isScribbled + + public boolean isNuked() + { + return nuked; + + } // end isNuked + + public java.util.Date getScribbleDate() + { + return scribble_date; + + } // end getScribbleDate + + public String getPseud() + { + return pseud; + + } // return pseud + + public String getBodyText() throws DataException + { + if (text_cache==null) + { // we don't have the body text yet, go get it + Connection conn = null; + if (nuked) + return null; // post nuked! + + try + { // use a database connection to get the body text + conn = datapool.getConnection(); + refresh(conn); + + if (nuked) + return null; // post nuked! + + 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) + ";"); + if (rs.next()) + text_cache = rs.getString(1); + else + return "Data Missing"; // FUTURE: throw an exception? + + } // end if + else // for scribbled posts, we return the scribbler's name only + text_cache = quickGetUserName(conn,scribble_uid); + + } // 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 getBodyText + + public boolean hasAttachment() + { + return (mimetype!=null); + + } // end hasAttachment + + public boolean canHide() + { + return ((creator_uid==conf.realUID()) || conf.userCanHide()); + + } // end canHide + + public boolean canScribble() + { + return ((creator_uid==conf.realUID()) || conf.userCanScribble()); + + } // end canScribble + + public boolean canNuke() + { + return conf.userCanNuke(); + + } // end canNuke + + public void setHidden(boolean flag) throws DataException, AccessError + { + if ((creator_uid!=conf.realUID()) && !(conf.userCanHide())) + { // we can't change the hidden status! + logger.error("trying to set hidden status of post w/o permission!"); + throw new AccessError("You are not permitted to change the hidden status of this message."); + + } // end if + + if (nuked || (scribble_date!=null)) + return; // changing the status of a nuked or scribbled post is futile + + Connection conn = null; + AuditRecord ar = null; + + try + { // open up a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // lock the tables we reference + stmt.executeUpdate("LOCK TABLES posts WRITE, postattach READ;"); + try + { // first, make sure we have the right status for our post + refresh(conn); + if (nuked || (scribble_date!=null)) + return; // changing the status of a nuked or scribbled post is futile + if (hidden==flag) + return; // this is a no-op + + // update the "hidden" flag in the database + StringBuffer sql = new StringBuffer("UPDATE posts SET hidden = "); + sql.append(flag ? '1' : '0').append(" WHERE postid = ").append(postid).append(';'); + stmt.executeUpdate(sql.toString()); + + hidden = flag; // store flag + + } // end try + finally + { // unlock the tables before we go + Statement ulk_stmt = conn.createStatement(); + ulk_stmt.executeUpdate("UNLOCK TABLES;"); + + } // end finally + + // 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"); + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error setting hidden status: " + e.getMessage(),e); + throw new DataException("unable to set hidden status: " + 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 setHidden + + public void scribble() throws DataException, AccessError + { + if ((creator_uid!=conf.realUID()) && !(conf.userCanScribble())) + { // we can't scribble this post + logger.error("trying to scribble post w/o permission!"); + throw new AccessError("You are not permitted to scribble this message."); + + } // end if + + if (nuked || (scribble_date!=null)) + return; // scribbling a nuked or scribbled post is futile + + Connection conn = null; + AuditRecord ar = null; + + try + { // open up a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // lock the tables we reference + stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE;"); + try + { // first, make sure we have the right status for our post + refresh(conn); + if (nuked || (scribble_date!=null)) + return; // scribbling a nuked or scribbled post is futile + + // First, set the appropriate "scribbled" information in the "header". + StringBuffer sql = new StringBuffer("UPDATE posts SET linecount = 0, hidden = 0, scribble_uid = "); + sql.append(conf.realUID()).append(", scribble_date = '"); + java.util.Date now = new java.util.Date(); + final String scribble_pseud = "(Scribbled)"; // TODO: configurable option + sql.append(SQLUtil.encodeDate(now)).append("', pseud = '").append(scribble_pseud); + sql.append("' WHERE postid = ").append(postid).append(';'); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + 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) + ";"); + if (rs.next()) + { // use this data to overwrite the post with X's + int len = rs.getInt(1); + if (len>0) + { // construct the "rubout" statement and execute it + sql.setLength(0); + sql.append("UPDATE postdata SET data = '"); + while (len>0) + { // generate a string of X's the length of the post + sql.append('X'); + len--; + + } // end while + + sql.append("' WHERE postid = ").append(postid).append(';'); + stmt.executeUpdate(sql.toString()); + + } // end if + // else not much need to do a rubout + + } // end if + // else don't try...we're deleting the row anyway + + // Delete the actual post data row. + sql.setLength(0); + sql.append("DELETE FROM postdata WHERE postid = ").append(postid).append(';'); + stmt.executeUpdate(sql.toString()); + + // Delete the attachment data row. + // FUTURE: can we do an overwrite on the attachment the way we did on the post data? + sql.setLength(0); + sql.append("DELETE FROM postattach WHERE postid = ").append(postid).append(';'); + stmt.executeUpdate(sql.toString()); + + // Update our internal data fields. + linecount = 0; + hidden = false; + scribble_uid = conf.realUID(); + scribble_date = now; + pseud = scribble_pseud; + text_cache = null; + + } // end try + finally + { // unlock the tables before we go + Statement ulk_stmt = conn.createStatement(); + ulk_stmt.executeUpdate("UNLOCK TABLES;"); + + } // end finally + + // 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)); + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error scribbling post: " + e.getMessage(),e); + throw new DataException("unable to scribble message: " + 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 scribble + + public void nuke() throws DataException, AccessError + { + if (!(conf.userCanNuke())) + { // we can't scribble this post + logger.error("trying to nuke post w/o permission!"); + throw new AccessError("You are not permitted to nuke this message."); + + } // end if + + if (nuked) + return; // nuking a nuked post is futile + + Connection conn = null; + AuditRecord ar = null; + + try + { // open up a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // lock the tables we reference + stmt.executeUpdate("LOCK TABLES posts WRITE, postdata WRITE, postattach WRITE, postdogear WRITE;"); + + try + { // first, make sure we have the right status for our post + refresh(conn); + if (nuked) + 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) + ";"); + + // Update our internal variables. + linecount = 0; + creator_uid = -1; + posted = null; + hidden = false; + scribble_uid = -1; + scribble_date = null; + pseud = null; + datalen = 0; + filename = null; + mimetype = null; + nuked = true; + creator_cache = null; + text_cache = null; + + } // end try + finally + { // unlock the tables before we go + Statement ulk_stmt = conn.createStatement(); + ulk_stmt.executeUpdate("UNLOCK TABLES;"); + + } // end finally + + // 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)); + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error nuking post: " + e.getMessage(),e); + throw new DataException("unable to nuke message: " + 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 nuke + + /*-------------------------------------------------------------------------------- + * External static operations + *-------------------------------------------------------------------------------- + */ + + static List loadMessageRange(EngineBackend engine, ConferenceBackend conf, DataPool datapool, int topicid, + 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) + "]"); + + Vector rc = new Vector(); + Connection conn = null; // pooled database connection + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // run a query to get all the posts in a particular topic + StringBuffer sql = + new StringBuffer("SELECT p.postid, p.parent, p.num, p.linecount, p.creator_uid, p.posted, " + + "p.hidden, p.scribble_uid, p.scribble_date, p.pseud, a.datalen, a.filename, " + + "a.mimetype FROM posts p LEFT JOIN postattach a ON p.postid = a.postid " + + "WHERE p.topicid = "); + sql.append(topicid).append(" AND p.num >= ").append(post_low).append(" AND p.num <= "); + sql.append(post_high).append(" ORDER BY p.num ASC;"); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + ResultSet rs = stmt.executeQuery(sql.toString()); + + while (rs.next()) + { // create implementation objects and shove them into the return vector + TopicMessageContext val = + new TopicMessageUserContextImpl(engine,conf,datapool,rs.getLong(1),rs.getLong(2),rs.getInt(3), + rs.getInt(4),rs.getInt(5),SQLUtil.getFullDateTime(rs,6), + rs.getBoolean(7),rs.getInt(8),SQLUtil.getFullDateTime(rs,9), + rs.getString(10),rs.getInt(11),rs.getString(12),rs.getString(13)); + rc.add(val); + + } // end while + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error reading message entries: " + e.getMessage(),e); + throw new DataException("unable to retrieve messages: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return new ReadOnlyVector(rc); // wrap the return vector + + } // end loadMessageRange + +} // end class TopicMessageUserContextImpl diff --git a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java index da31a20..9331c16 100644 --- a/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java +++ b/src/com/silverwrist/venice/core/impl/TopicUserContextImpl.java @@ -21,6 +21,7 @@ import java.sql.*; import java.util.*; import org.apache.log4j.*; import com.silverwrist.venice.db.*; +import com.silverwrist.venice.htmlcheck.*; import com.silverwrist.venice.security.AuditRecord; import com.silverwrist.venice.core.*; @@ -109,11 +110,13 @@ class TopicUserContextImpl implements TopicContext private static ResultSet queryByTopic(Statement stmt, int topicid, int uid) throws SQLException { StringBuffer sql = - new StringBuffer("SELECT t.topicid, t.num, t.creator_uid, t.top_message, t.frozen, t.archived, " - + "t.createdate, t.lastupdate, t.name, IFNULL(s.hidden,0) AS hidden, " - + "(t.top_message - IFNULL(s.last_message,-1)) AS unread FROM topics t " - + "LEFT JOIN topicsettings s ON t.topicid = s.topicid AND s.uid = "); - sql.append(uid).append(" WHERE t.topicid = ").append(topicid).append(';'); + new StringBuffer("SELECT topics.topicid, topics.num, topics.creator_uid, topics.top_message, " + + "topics.frozen, topics.archived, topics.createdate, topics.lastupdate, " + + "topics.name, IFNULL(topicsettings.hidden,0) AS hidden, " + + "(topics.top_message - IFNULL(topicsettings.last_message,-1)) AS unread " + + "FROM topics LEFT JOIN topicsettings ON topics.topicid = topicsettings.topicid " + + "AND topicsettings.uid = "); + sql.append(uid).append(" WHERE topics.topicid = ").append(topicid).append(';'); if (logger.isDebugEnabled()) logger.debug("SQL: " + sql.toString()); return stmt.executeQuery(sql.toString()); @@ -134,8 +137,29 @@ class TopicUserContextImpl implements TopicContext } // end if + private void refresh(Connection conn) throws SQLException + { + Statement stmt = conn.createStatement(); + + // perform a requery of the database + ResultSet rs = queryByTopic(stmt,topicid,conf.realUID()); + if (rs.next()) + { // update the fields that are capable of changing + top_message = rs.getInt(4); + frozen = rs.getBoolean(5); + archived = rs.getBoolean(6); + lastupdate = SQLUtil.getFullDateTime(rs,8); + hidden = rs.getBoolean(10); + unread = rs.getInt(11); + + } // end if + else // this topic must have been deleted - fsck it + makeDeleted(); + + } // end refresh + /*-------------------------------------------------------------------------------- - * Implementatuions from interface TopicContext + * Implementations from interface TopicContext *-------------------------------------------------------------------------------- */ @@ -148,22 +172,7 @@ class TopicUserContextImpl implements TopicContext try { // get a database connection conn = datapool.getConnection(); - Statement stmt = conn.createStatement(); - - // perform a requery of the database - ResultSet rs = queryByTopic(stmt,topicid,conf.realUID()); - if (rs.next()) - { // update the fields that are capable of changing - top_message = rs.getInt(4); - frozen = rs.getBoolean(5); - archived = rs.getBoolean(6); - lastupdate = SQLUtil.getFullDateTime(rs,8); - hidden = rs.getBoolean(10); - unread = rs.getInt(11); - - } // end if - else // this topic must have been deleted - fsck it - makeDeleted(); + refresh(conn); } // end try catch (SQLException e) @@ -524,8 +533,8 @@ class TopicUserContextImpl implements TopicContext } // end try catch (SQLException e) { // turn SQLException into data exception - logger.error("DB error setting topic data: " + e.getMessage(),e); - throw new DataException("unable to set topic hidden status: " + e.getMessage(),e); + logger.error("DB error setting topic user data: " + e.getMessage(),e); + throw new DataException("unable to set unread messages: " + e.getMessage(),e); } // end catch finally @@ -543,6 +552,213 @@ class TopicUserContextImpl implements TopicContext } // end fixSeen + public List getMessages(int low, int high) throws DataException, AccessError + { + if (!(conf.userCanRead())) + { // they can't read messages in this topic! + logger.error("trying to read postings w/o permission!"); + throw new AccessError("You do not have permission to read messages in this conference."); + + } // end if + + // reorder parameters so they come in the correct order! + if (low<=high) + return TopicMessageUserContextImpl.loadMessageRange(engine,conf,datapool,topicid,low,high); + else + return TopicMessageUserContextImpl.loadMessageRange(engine,conf,datapool,topicid,high,low); + + } // end getMessages + + public TopicMessageContext postNewMessage(long parent, String pseud, String text) + throws DataException, AccessError + { + if (!(conf.userCanPost())) + { // they can't post in this topic! + logger.error("trying to post w/o permission!"); + throw new AccessError("You do not have permission to post messages in this conference."); + + } // end if + + if (deleted) + { // the topic has been deleted + logger.error("can't post to a deleted topic!"); + throw new AccessError("You cannot post to a topic that has been deleted."); + + } // end if + + if (frozen && !(conf.userCanHide())) + { // can't post to a frozen topic! + logger.error("can't post to a frozen topic!"); + throw new AccessError("The topic is frozen, and you do not have permission to post to it."); + + } // end if + + if (archived && !(conf.userCanHide())) + { // can't post to a frozen topic! + logger.error("can't post to an archived topic!"); + throw new AccessError("The topic is archived, and you do not have permission to post to it."); + + } // end if + + // preprocess the two arguments through HTML checkers + HTMLChecker pseud_ch = engine.createCheckerObject(engine.HTMLC_POST_PSEUD); + HTMLChecker text_ch = engine.createCheckerObject(engine.HTMLC_POST_BODY); + try + { // run both arguments through the HTML checker + pseud_ch.append(pseud); + pseud_ch.finish(); + text_ch.append(text); + text_ch.finish(); + + } // end try + catch (AlreadyFinishedException e) + { // this isn't right... + throw new InternalStateError("HTMLChecker erroneously throwing AlreadyFinishedException",e); + + } // end catch + + String real_pseud, real_text; + int text_linecount; + try + { // retrieve the processed values + real_pseud = pseud_ch.getValue(); + real_text = text_ch.getValue(); + text_linecount = text_ch.getLines(); + + } // end try + catch (NotYetFinishedException e) + { // this isn't right either! + throw new InternalStateError("HTMLChecker erroneously throwing NotYetFinishedException",e); + + } // end catch + + int new_post_num; + long new_post_id; + java.util.Date posted_date; + Connection conn = null; + AuditRecord ar = null; + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // 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;"); + + try + { // refresh our current status and recheck allowed status + refresh(conn); + if (deleted) + { // the topic has been deleted + logger.error("can't post to a deleted topic!"); + throw new AccessError("You cannot post to a topic that has been deleted."); + + } // end if + + if (frozen && !(conf.userCanHide())) + { // can't post to a frozen topic! + logger.error("can't post to a frozen topic!"); + throw new AccessError("The topic is frozen, and you do not have permission to post to it."); + + } // end if + + if (archived && !(conf.userCanHide())) + { // can't post to a frozen topic! + logger.error("can't post to an archived topic!"); + throw new AccessError("The topic is archived, and you do not have permission to post to it."); + + } // end if + + // Determine what the new post number is. + new_post_num = top_message + 1; + + // Add the post "header" to the posts table. + StringBuffer sql = new StringBuffer("INSERT INTO posts (parent, topicid, num, linecount, creator_uid, " + + "posted, pseud) VALUES ("); + sql.append(parent).append(", ").append(topicid).append(", ").append(new_post_num).append(", "); + sql.append(text_linecount).append(", ").append(conf.realUID()).append(", '"); + posted_date = new java.util.Date(); + sql.append(SQLUtil.encodeDate(posted_date)).append("', '").append(real_pseud).append("');"); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + stmt.executeUpdate(sql.toString()); + + // Retrieve the new post ID. + ResultSet rs = stmt.executeQuery("SELECT LAST_INSERT_ID();"); + if (!(rs.next())) + throw new InternalStateError("postMessage(): Unable to get new post ID!"); + new_post_id = rs.getLong(1); + + // Touch the topic values to reflect the added post. + sql.setLength(0); + sql.append("UPDATE topics SET top_message = ").append(new_post_num).append(", lastupdate = '"); + sql.append(SQLUtil.encodeDate(posted_date)).append("' WHERE topicid = ").append(topicid).append(';'); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + stmt.executeUpdate(sql.toString()); + + // insert the post data + sql.setLength(0); + sql.append("INSERT INTO postdata (postid, data) VALUES (").append(new_post_id).append(", '"); + sql.append(real_text).append("');"); + stmt.executeUpdate(sql.toString()); + + // mark that we posted to the conference + conf.touchUpdate(conn,posted_date); + conf.touchPost(conn,posted_date); + + // fill in our own local variables to reflect the update + top_message = new_post_num; + lastupdate = posted_date; + + } // end try + finally + { // make sure we unlock the tables when we're done + Statement ulk_stmt = conn.createStatement(); + ulk_stmt.executeUpdate("UNLOCK TABLES;"); + + } // end finally + + // record what we did in an audit record + ar = new AuditRecord(AuditRecord.POST_MESSAGE,conf.realUID(),conf.userRemoteAddress(), + conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",topic=" + + String.valueOf(topicid) + ",post=" + String.valueOf(new_post_id), + "pseud=" + real_pseud); + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error posting message: " + e.getMessage(),e); + throw new DataException("unable to post message: " + 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 + + // return the new message context + return new TopicMessageUserContextImpl(engine,conf,datapool,new_post_id,parent,new_post_num, + text_linecount,conf.realUID(),posted_date,real_pseud); + + } // end postMessage + /*-------------------------------------------------------------------------------- * External operations usable only from within the package *-------------------------------------------------------------------------------- diff --git a/src/com/silverwrist/venice/htmlcheck/dict/DictNode.java b/src/com/silverwrist/venice/htmlcheck/dict/DictNode.java index 5cb05e2..b27642e 100644 --- a/src/com/silverwrist/venice/htmlcheck/dict/DictNode.java +++ b/src/com/silverwrist/venice/htmlcheck/dict/DictNode.java @@ -25,7 +25,7 @@ class DictNode */ private static final int[] permute_tab = - { 7, 21, 12, 9, 1, 13, 18, 10, 5, 25, 23, 11, 16, 3, 6, 14, 24, 4, 8, 2, 15, 20, 19, 22, 17, 26 }; + { 8, 22, 13, 10, 2, 14, 19, 11, 6, 26, 24, 12, 17, 4, 7, 15, 25, 5, 9, 3, 16, 21, 20, 23, 18, 27 }; /*-------------------------------------------------------------------------------- * Attributes @@ -53,6 +53,8 @@ class DictNode { if (ltr=='-') return 0; + else if (ltr=='\'') + return 1; else return permute_tab[ltr - 'a']; diff --git a/src/com/silverwrist/venice/htmlcheck/dict/TreeLexicon.java b/src/com/silverwrist/venice/htmlcheck/dict/TreeLexicon.java index 4ee9044..13e057c 100644 --- a/src/com/silverwrist/venice/htmlcheck/dict/TreeLexicon.java +++ b/src/com/silverwrist/venice/htmlcheck/dict/TreeLexicon.java @@ -41,6 +41,62 @@ public class TreeLexicon implements ModSpellingDictionary } // end constructor + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + + private boolean checkSimple(String word) + { + DictNode cur = root; + for (int i=0; i'z')) && (ch!='-') && (ch!='\'')) + return false; + cur = cur.getRef(ch); + if (cur==null) + return false; + + } // end for + + return cur.isTerminal(); + + } // end checkSimple + + private boolean checkHyphenates(String word) + { + if (word.indexOf('-')<0) + return false; // non-hyphenated + + int st = 0; + int p; + boolean ok = true; + do + { // break the word up into hyphenated compounds + p = word.indexOf('-',st); + String frag; + if (p>=0) + { // get the middle fragment of the word + frag = word.substring(st,p); + st = p + 1; + + } // end if + else // get it from the end + frag = word.substring(st); + + // check this fragment... + if (frag.length()<=1) + ok = true; // fragments of length 0 or 1 are always OK + else // anything else goes through checkSimple + ok = checkSimple(frag); + + } while (ok && (p>=0)); + + return ok; + + } // end checkHyphenates + /*-------------------------------------------------------------------------------- * Implementations from interface SpellingDictionary *-------------------------------------------------------------------------------- @@ -54,20 +110,22 @@ public class TreeLexicon implements ModSpellingDictionary public synchronized boolean checkWord(String word) { + if (word.length()<=1) + return true; // words of length 1 get a free pass String real_word = word.toLowerCase(); - DictNode cur = root; - for (int i=0; i'z')) && (ch!='-')) - return false; - cur = cur.getRef(ch); - if (cur==null) - return false; + if (checkSimple(real_word)) + return true; // word is in lexicon - we're OK to go - } // end for + if ((real_word.indexOf('\'')==(real_word.length()-2)) && (real_word.charAt(real_word.length()-1)=='s')) + { // drop the apostrophe-s from the end of the word and retry + String base = real_word.substring(0,real_word.length()-2); + if (checkSimple(base)) + return true; + return checkHyphenates(base); - return cur.isTerminal(); + } // end if + else // try hyphenated forms + return checkHyphenates(real_word); } // end checkWord @@ -83,7 +141,7 @@ public class TreeLexicon implements ModSpellingDictionary for (int i=0; i'z')) && (ch!='-')) + if (((ch<'a') || (ch>'z')) && (ch!='-') && (ch!='\'')) return; // advance down through trie diff --git a/src/com/silverwrist/venice/htmlcheck/impl/HTMLCheckerImpl.java b/src/com/silverwrist/venice/htmlcheck/impl/HTMLCheckerImpl.java index c0b4575..3a667ec 100644 --- a/src/com/silverwrist/venice/htmlcheck/impl/HTMLCheckerImpl.java +++ b/src/com/silverwrist/venice/htmlcheck/impl/HTMLCheckerImpl.java @@ -79,6 +79,13 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic private static final short ST_PAREN = 4; private static final short ST_TAGQUOTE = 5; + /*-------------------------------------------------------------------------------- + * Internal constants + *-------------------------------------------------------------------------------- + */ + + private static final int MARGIN_SLOP = 5; + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- @@ -127,17 +134,17 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic private static final boolean isWordChar(char ch) { - return (Character.isUpperCase(ch) || Character.isLowerCase(ch) || (ch=='-')); + return (Character.isUpperCase(ch) || Character.isLowerCase(ch) || (ch=='-') || (ch=='\'')); } // end isWordChar - private static final int getRunLength(StringBuffer buf) + private static final int getRunLength(StringBuffer buf, int start) { - boolean word_char = isWordChar(buf.charAt(0)); + boolean word_char = isWordChar(buf.charAt(start)); int l = 1; - while (l0) { // find the length of the initial string of word or non-word characters int sublen = getRunLength(temp_buffer); if (isWordChar(temp_buffer.charAt(0))) - { // we need to check the word...but first, we must eliminate leading hyphens + { // we need to check the word...but first, we must eliminate leading hyphens and apostrophes int hyph_count = 0; - while ((hyph_count=0)) hyph_count++; emitFromStartOfTempBuffer(hyph_count); sublen -= hyph_count; - // now determine how many hyphens there are at the end of the word... + // now determine how many hyphens/apostrophes there are at the end of the word... int word_len = sublen; hyph_count = 0; - while ((word_len>0) && (temp_buffer.charAt(word_len-1)=='-')) - { // decrement word length, increment hyphen count + while ((word_len>0) && (hyph_apos.indexOf(temp_buffer.charAt(word_len-1))>=0)) + { // decrement word length, increment hyphen/apostrophe count hyph_count++; word_len--; @@ -458,13 +481,39 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic } // end if - // now emit the rest of the hyphens + // now emit the rest of the hyphens/apostrophes emitFromStartOfTempBuffer(hyph_count); } // end if - else // just emit this many characters, line-breaking where required + else + { // just emit this many characters, line-breaking where required + if ((sublen==temp_buffer.length()) && !first && (sublen<=MARGIN_SLOP)) + { // This is intended to handle a small run of non-word characters at the end of a string (i.e. + // followed by whitespace) that should stay on the same line with its preceding word, to + // eliminate "funnies" in punctuation formatting. + emitString(temp_buffer.toString(),config.getOutputFilters(),true); + temp_buffer.setLength(0); + break; + + } // end if + + // This is kind of the inverse of the above check; if we have a small run of non-word + // characters at the START of a word (preceded by whitespace and followed by at least + // one word character), then ensure that we can keep that word and its prefixing non-word + // characters on the same line (again, avoiding "funnies" in formatting). + if ((sublen=20)) // TODO: configurable + first = last - 20; + else + first = last - (ur+2); + last--; + + } // end if + else + { // we have at least one parameter... + try + { // convert it to an integer and range-limit it + first = Integer.parseInt(foo); + if (first<0) + first = 0; + else if (first>=topic.getTotalMessages()) + first = topic.getTotalMessages() - 1; + + } // end try + catch (NumberFormatException nfe) + { // we could not translate the parameter to a number + throw new ValidationException("Message parameter is invalid."); + + } // end catch + + foo = request.getParameter("p2"); + if (StringUtil.isStringEmpty(foo)) + last = first; // just specify ONE post... + else + { // OK, we have an actual "last message" parameter... + try + { // convert it to an integer and range-limit it + last = Integer.parseInt(foo); + if ((last<0) || (last>=topic.getTotalMessages())) + last = topic.getTotalMessages() - 1; + + } // end try + catch (NumberFormatException nfe) + { // we could not translate the parameter to a number + throw new ValidationException("Message parameter is invalid."); + + } // end catch + + } // end else + + } // end else + + return new PostInterval(first,last); + + } // end getInterval + /*-------------------------------------------------------------------------------- * Overrides from class HttpServlet *-------------------------------------------------------------------------------- @@ -287,7 +428,39 @@ public class ConfDisplay extends VeniceServlet if (logger.isDebugEnabled()) logger.debug("MODE: display messages in topic"); - // TODO: handle this somehow + try + { // determine what the post interval is we want to display + PostInterval piv = getInterval(request,topic); + boolean read_new = !(StringUtil.isStringEmpty(request.getParameter("rnm"))); + boolean show_adv = !(StringUtil.isStringEmpty(request.getParameter("shac"))); + + // create the post display + TopicPosts tpos = new TopicPosts(request,sig,conf,topic,piv.getFirst(),piv.getLast(), + read_new,show_adv); + content = tpos; + page_title = topic.getName() + ": " + String.valueOf(topic.getTotalMessages()) + " Total; " + + String.valueOf(tpos.getNewMessages()) + " New; Last: " + + rdat.formatDateForDisplay(topic.getLastUpdateDate()); + + } // end try + catch (ValidationException ve) + { // there's an error in the parameters somewhere + page_title = "Error"; + content = new ErrorBox(null,ve.getMessage(),"top"); + + } // end catch + catch (DataException de) + { // there was a database error retrieving topics + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error listing messages: " + de.getMessage(),"top"); + + } // end catch + catch (AccessError ae) + { // we were unable to retrieve the topic list + page_title = "Access Error"; + content = new ErrorBox(page_title,ae.getMessage(),"top"); + + } // end catch } // end if else diff --git a/src/com/silverwrist/venice/servlets/PostMessage.java b/src/com/silverwrist/venice/servlets/PostMessage.java new file mode 100644 index 0000000..7e73f83 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/PostMessage.java @@ -0,0 +1,414 @@ +/* + * 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; + +import java.io.*; +import java.util.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.log4j.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.ValidationException; +import com.silverwrist.venice.core.*; +import com.silverwrist.venice.servlets.format.*; + +public class PostMessage extends VeniceServlet +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(PostMessage.class.getName()); + + /*-------------------------------------------------------------------------------- + * Internal functions + *-------------------------------------------------------------------------------- + */ + + private static SIGContext getSIGParameter(ServletRequest request, UserContext user) + throws ValidationException, DataException + { + String str = request.getParameter("sig"); + if (str==null) + { // no SIG parameter - bail out now! + logger.error("SIG parameter not specified!"); + throw new ValidationException("No SIG specified."); + + } // end if + + try + { // turn the string into a SIGID, and thence to a SIGContext + int sigid = Integer.parseInt(str); + return user.getSIGContext(sigid); + + } // end try + catch (NumberFormatException nfe) + { // error in Integer.parseInt + logger.error("Cannot convert SIG parameter '" + str + "'!"); + throw new ValidationException("Invalid SIG parameter."); + + } // end catch + + } // end getSIGParameter + + private static ConferenceContext getConferenceParameter(ServletRequest request, SIGContext sig) + throws ValidationException, DataException, AccessError + { + String str = request.getParameter("conf"); + if (str==null) + { // no conference parameter - bail out now! + logger.error("Conference parameter not specified!"); + throw new ValidationException("No conference specified."); + + } // end if + + try + { // turn the string into a ConfID, and thence to a ConferenceContext + int confid = Integer.parseInt(str); + return sig.getConferenceContext(confid); + + } // end try + catch (NumberFormatException nfe) + { // error in Integer.parseInt + logger.error("Cannot convert conference parameter '" + str + "'!"); + throw new ValidationException("Invalid conference parameter."); + + } // end catch + + } // end getConferenceParameter + + private static TopicContext getTopicParameter(ServletRequest request, ConferenceContext conf) + throws ValidationException, DataException, AccessError + { + String str = request.getParameter("top"); + if (StringUtil.isStringEmpty(str)) + { // no topic parameter - bail out now! + logger.error("Topic parameter not specified!"); + throw new ValidationException("No topic specified."); + + } // end if + + try + { // turn the string into a TopicID, and thence to a TopicContext + short topicid = Short.parseShort(str); + return conf.getTopic(topicid); + + } // end try + catch (NumberFormatException nfe) + { // error in Integer.parseInt + logger.error("Cannot convert topic parameter '" + str + "'!"); + throw new ValidationException("Invalid topic parameter."); + + } // end catch + + } // end getTopicParameter + + private static int getPostNumber(ServletRequest request) throws ValidationException + { + String str = request.getParameter("sd"); + if (StringUtil.isStringEmpty(str)) + throw new ValidationException("Invalid parameter."); + try + { // get the number of posts we think he topic has + return Integer.parseInt(str); + + } // end try + catch (NumberFormatException nfe) + { // not a good integer... + throw new ValidationException("Invalid parameter."); + + } // end catch + + } // end getPostNumber + + /*-------------------------------------------------------------------------------- + * Overrides from class HttpServlet + *-------------------------------------------------------------------------------- + */ + + public String getServletInfo() + { + String rc = "PostMessage servlet - Handles posting messages to a conference\n" + + "Part of the Venice Web Communities System\n"; + return rc; + + } // end getServletInfo + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + UserContext user = getUserContext(request); + RenderData rdat = createRenderData(request,response); + String page_title = null; + Object content = null; + SIGContext sig = null; // SIG context + ConferenceContext conf = null; // conference context + TopicContext topic = null; // topic context + + try + { // this outer try is to catch ValidationException + try + { // all commands require a SIG parameter + sig = getSIGParameter(request,user); + changeMenuSIG(request,sig); + if (logger.isDebugEnabled()) + logger.debug("found SIG #" + String.valueOf(sig.getSIGID())); + + } // end try + catch (DataException de) + { // error looking up the SIG + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error finding SIG: " + de.getMessage(),"top"); + + } // end catch + + if (content==null) + { // we got the SIG parameter OK + try + { // all commands require a conference parameter + conf = getConferenceParameter(request,sig); + if (logger.isDebugEnabled()) + logger.debug("found conf #" + String.valueOf(conf.getConfID())); + + } // end try + catch (DataException de) + { // error looking up the conference + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error finding conference: " + de.getMessage(),"top"); + + } // end catch + + } // end if + + if (content==null) + { // we got the conference parameter OK + try + { // now we need a topic parameter + topic = getTopicParameter(request,conf); + if (logger.isDebugEnabled()) + logger.debug("found topic #" + String.valueOf(topic.getTopicID())); + + } // end try + catch (DataException de) + { // error looking up the conference + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error finding topic: " + de.getMessage(),"top"); + + } // end catch + + } // end if + + } // end try + catch (ValidationException ve) + { // these all get handled in pretty much the same way + page_title = "Error"; + content = new ErrorBox(null,ve.getMessage(),"top"); + + } // end catch + catch (AccessError ae) + { // these all get handled in pretty much the same way + page_title = "Access Error"; + content = new ErrorBox(page_title,ae.getMessage(),"top"); + + } // end catch + + if (content==null) + { // make sure we've got some post data + String raw_postdata = request.getParameter("pb"); + if (StringUtil.isStringEmpty(raw_postdata)) + { // don't allow zero-size posts + rdat.nullResponse(); + return; + + } // end if + + final String yes = "Y"; + + // now decide what to do based on which button got clicked + if (isImageButtonClicked(request,"cancel")) + { // canceled posting - take us back to familiar ground + rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + + String.valueOf(conf.getConfID()) + "&top=" + + String.valueOf(topic.getTopicNumber())); + return; + + } // end if ("Cancel") + else if (isImageButtonClicked(request,"preview")) + { // previewing the post! + try + { // generate a preview view + content = new PostPreview(getVeniceEngine(),sig,conf,topic,request.getParameter("pseud"), + raw_postdata,request.getParameter("next"),getPostNumber(request), + yes.equals(request.getParameter("attach"))); + page_title = "Previewing Post"; + + } // end try + catch (ValidationException ve) + { // there was some sort of a parameter error in the display (getPostNumber can throw this) + page_title = "Error"; + content = new ErrorBox(null,ve.getMessage(),"top"); + + } // end catch + + } // end else if ("Preview & Spellcheck") + else if (isImageButtonClicked(request,"post")) + { // post the message, and then reload the same topic + try + { // first, check against slippage + int pn = getPostNumber(request); + if (pn==topic.getTotalMessages()) + { // no slippage - post the message!!! + TopicMessageContext msg = topic.postNewMessage(0,request.getParameter("pseud"),raw_postdata); + if (yes.equals(request.getParameter("attach"))) + { // we have an attachment to upload... + // TODO: do something to upload the attachment + rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + + String.valueOf(conf.getConfID()) + "&top=" + + String.valueOf(topic.getTopicNumber()) + "&rnm=1"); + return; + + } // end if + else + { // no attachment - jump back to the topic + rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + + String.valueOf(conf.getConfID()) + "&top=" + + String.valueOf(topic.getTopicNumber()) + "&rnm=1"); + return; + + } // end else + + } // end if + else + { // slippage detected - show the slippage display + content = new PostSlippage(getVeniceEngine(),sig,conf,topic,pn,request.getParameter("next"), + request.getParameter("pseud"),raw_postdata, + yes.equals(request.getParameter("attach"))); + page_title = "Slippage or Double-Click Detected"; + + } // end else + + } // end try + catch (ValidationException ve) + { // there was some sort of a parameter error in the display + page_title = "Error"; + content = new ErrorBox(null,ve.getMessage(),"top"); + + } // end catch + catch (DataException de) + { // there was a database error posting the message + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error posting message: " + de.getMessage(),"top"); + + } // end catch + catch (AccessError ae) + { // we were unable to retrieve the topic list + page_title = "Access Error"; + content = new ErrorBox(page_title,ae.getMessage(),"top"); + + } // end catch + + } // end else if ("Post & Reload") + else if (isImageButtonClicked(request,"postnext")) + { // post the message, and then go to the "next" topic + try + { // first, check against slippage + int pn = getPostNumber(request); + if (pn==topic.getTotalMessages()) + { // no slippage - post the message! + TopicMessageContext msg = topic.postNewMessage(0,request.getParameter("pseud"),raw_postdata); + + short next; + try + { // attempt to get the value of the "next topic" parameter + String foo = request.getParameter("next"); + if (StringUtil.isStringEmpty(foo)) + next = topic.getTopicNumber(); + else + next = Short.parseShort(foo); + + } // end try + catch (NumberFormatException nfe) + { // just default me + next = topic.getTopicNumber(); + + } // end catch + + if (yes.equals(request.getParameter("attach"))) + { // we have an attachment to upload... + // TODO: jump somewhere we can upload the attachment! + rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + + String.valueOf(conf.getConfID()) + "&top=" + String.valueOf(next) + "&rnm=1"); + return; + + } // end if + else + { // no attachment - jump to the next topic + rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + + String.valueOf(conf.getConfID()) + "&top=" + String.valueOf(next) + "&rnm=1"); + return; + + } // end else + + } // end if + else + { // slippage detected - show the slippage display + content = new PostSlippage(getVeniceEngine(),sig,conf,topic,pn,request.getParameter("next"), + request.getParameter("pseud"),raw_postdata, + yes.equals(request.getParameter("attach"))); + page_title = "Slippage or Double-Click Detected"; + + } // end else + + } // end try + catch (ValidationException ve) + { // there was some sort of a parameter error in the display + page_title = "Error"; + content = new ErrorBox(null,ve.getMessage(),"top"); + + } // end catch + catch (DataException de) + { // there was a database error posting the message + page_title = "Database Error"; + content = new ErrorBox(page_title,"Database error posting message: " + de.getMessage(),"top"); + + } // end catch + catch (AccessError ae) + { // we were unable to retrieve the topic list + page_title = "Access Error"; + content = new ErrorBox(page_title,ae.getMessage(),"top"); + + } // end catch + + } // end else if ("Post & Go Next") + else + { // unknown button clicked + page_title = "Internal Error"; + logger.error("no known button click on PostMessage.doPost"); + content = new ErrorBox(page_title,"Unknown command button pressed","top"); + + } // end else + + } // end if (got all parameters oK) + + BaseJSPData basedat = new BaseJSPData(page_title,"post",content); + basedat.transfer(getServletContext(),rdat); + + } // end doPost + +} // end class PostMessage diff --git a/src/com/silverwrist/venice/servlets/format/PostPreview.java b/src/com/silverwrist/venice/servlets/format/PostPreview.java new file mode 100644 index 0000000..92e1984 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/PostPreview.java @@ -0,0 +1,190 @@ +/* + * 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.util.StringUtil; +import com.silverwrist.venice.htmlcheck.*; +import com.silverwrist.venice.core.*; + +public class PostPreview implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.PostPreview"; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private SIGContext sig; + private ConferenceContext conf; + private TopicContext topic; + private String next; + private String pseud; + private String data; + private String preview; + private int num_errors; + private int msgs; + private boolean attach; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public PostPreview(VeniceEngine engine, SIGContext sig, ConferenceContext conf, TopicContext topic, + String pseud, String data, String next, int msgs, boolean attach) + { + this.sig = sig; + this.conf = conf; + this.topic = topic; + this.next = next; + this.msgs = msgs; + this.attach = attach; + + try + { // sanitize the pseud data + HTMLChecker check = engine.getEscapingChecker(); + check.append(pseud); + check.finish(); + this.pseud = check.getValue(); + + // sanitize the post box data + check.reset(); + check.append(data); + check.finish(); + this.data = check.getValue(); + + // now generate the preview + check = engine.getPreviewChecker(); + check.append(data); + check.finish(); + this.preview = check.getValue(); + this.num_errors = check.getCounter("spelling"); + + } // end try + catch (HTMLCheckerException e) + { // this is a bad issue... + throw new InternalStateError("spurious HTMLCheckerException thrown"); + + } // end catch + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static PostPreview retrieve(ServletRequest request) + { + return (PostPreview)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "preview.jsp"; + + } // end getTargetJSPName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public int getNumSpellingErrors() + { + return num_errors; + + } // end getNumSpellingErrors + + public String getPreviewData() + { + return preview; + + } // end getPreviewData + + public int getSIGID() + { + return sig.getSIGID(); + + } // end getSIGID + + public int getConfID() + { + return conf.getConfID(); + + } // end getConfID + + public int getTopicNumber() + { + return topic.getTopicNumber(); + + } // end getTopicNumber + + public int getTotalMessages() + { + return msgs; + + } // end getTotalMessages + + public String getNextVal() + { + return next; + + } // end getNextVal + + public String getPseud() + { + return pseud; + + } // end getPseud + + public boolean attachChecked() + { + return attach; + + } // end attachChecked + + public String getBodyText() + { + return data; + + } // end getBodyText + +} // end class PostPreview diff --git a/src/com/silverwrist/venice/servlets/format/PostSlippage.java b/src/com/silverwrist/venice/servlets/format/PostSlippage.java new file mode 100644 index 0000000..3b6fa18 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/PostSlippage.java @@ -0,0 +1,216 @@ +/* + * 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.util.StringUtil; +import com.silverwrist.venice.htmlcheck.*; +import com.silverwrist.venice.core.*; + +public class PostSlippage implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.PostSlippage"; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private SIGContext sig; + private ConferenceContext conf; + private TopicContext topic; + private List messages; + private String next; + private String pseud; + private String text; + private boolean attach; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public PostSlippage(VeniceEngine engine, SIGContext sig, ConferenceContext conf, TopicContext topic, + int lastval, String next, String pseud, String text, boolean attach) + throws DataException, AccessError + { + this.sig = sig; + this.conf = conf; + this.topic = topic; + this.messages = topic.getMessages(lastval,topic.getTotalMessages()-1); + this.next = next; + this.attach = attach; + + try + { // run the text and pseud through an HTML checker to escape them + HTMLChecker ch = engine.getEscapingChecker(); + ch.append(pseud); + ch.finish(); + this.pseud = ch.getValue(); + ch.reset(); + ch.append(text); + ch.finish(); + this.text = text; + + } // end try + catch (HTMLCheckerException e) + { // this shouldn't happen + throw new InternalStateError("spurious HTMLCheckerException thrown"); + + } // end catch + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static PostSlippage retrieve(ServletRequest request) + { + return (PostSlippage)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "slippage.jsp"; + + } // end getTargetJSPName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public static String getPosterName(TopicMessageContext msg) + { + try + { // have to guard agains a DataException here + return msg.getCreatorName(); + + } // end try + catch (DataException de) + { // just return "unknown" on failure + return "(unknown)"; + + } // end catch + + } // end getPosterName + + public static String getMessageBodyText(TopicMessageContext msg) + { + try + { // have to guard against a DataException here + return msg.getBodyText(); + + } // end try + catch (DataException de) + { // just return an error message + return "(Unable to retrieve message data: " + StringUtil.encodeHTML(de.getMessage()) + ")"; + + } // end catch + + } // end getMessageBodyText + + public int getSIGID() + { + return sig.getSIGID(); + + } // end getSIGID + + public int getConfID() + { + return conf.getConfID(); + + } // end getConfID + + public int getTopicNumber() + { + return topic.getTopicNumber(); + + } // end getTopicNumber + + public String getTopicName() + { + return topic.getName(); + + } // end getTopicName + + public String getIdentifyingData() + { + return "Slippage posting to topic " + String.valueOf(topic.getTopicID()); + + } // end getIdentifyingData + + public int getTotalMessages() + { + return topic.getTotalMessages(); + + } // end getTotalMessages + + public Iterator getMessageIterator() + { + return messages.iterator(); + + } // end getMessageIterator + + public String getNextVal() + { + return next; + + } // end getNextVal + + public String getPseud() + { + return pseud; + + } // end getPseud + + public boolean attachChecked() + { + return attach; + + } // end attachChecked + + public String getBodyText() + { + return text; + + } // end getBodyText + +} // end class PostSlippage diff --git a/src/com/silverwrist/venice/servlets/format/TopicListing.java b/src/com/silverwrist/venice/servlets/format/TopicListing.java index a258590..ab456ca 100644 --- a/src/com/silverwrist/venice/servlets/format/TopicListing.java +++ b/src/com/silverwrist/venice/servlets/format/TopicListing.java @@ -106,6 +106,14 @@ public class TopicListing implements JSPRender } // end getConfID + public String getLocator() + { + StringBuffer buf = new StringBuffer("sig="); + buf.append(sig.getSIGID()).append("&conf=").append(conf.getConfID()); + return buf.toString(); + + } // end getLocator + public String getConfName() { return conf.getName(); @@ -118,6 +126,15 @@ public class TopicListing implements JSPRender } // end canDoReadNew + public String getNextLocator() + { + StringBuffer buf = new StringBuffer("sig="); + buf.append(sig.getSIGID()).append("&conf=").append(conf.getConfID()).append("&top="); + buf.append(visit_order.getNext()); + return buf.toString(); + + } // end getNextLocator + public boolean canCreateTopic() { return conf.canCreateTopic(); diff --git a/src/com/silverwrist/venice/servlets/format/TopicPosts.java b/src/com/silverwrist/venice/servlets/format/TopicPosts.java new file mode 100644 index 0000000..6859e13 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/TopicPosts.java @@ -0,0 +1,354 @@ +/* + * 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.util.StringUtil; +import com.silverwrist.venice.core.*; + +public class TopicPosts implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.TopicPosts"; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private SIGContext sig; + private ConferenceContext conf; + private TopicContext topic; + private int first; + private int last; + private boolean show_advanced; + private int unread; + private List messages; + private TopicVisitOrder visit_order; + private String cache_locator = null; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public TopicPosts(HttpServletRequest request, SIGContext sig, ConferenceContext conf, TopicContext topic, + int first, int last, boolean read_new, boolean show_advanced) + throws DataException, AccessError + { + this.sig = sig; + this.conf = conf; + this.topic = topic; + this.first = first; + this.last = last; + this.show_advanced = show_advanced; + this.unread = topic.getUnreadMessages(); + if (read_new) + topic.setUnreadMessages(0); + this.messages = topic.getMessages(first,last); + this.visit_order = TopicVisitOrder.retrieve(request.getSession(true),conf.getConfID()); + visit_order.visit(topic.getTopicNumber()); + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static TopicPosts retrieve(ServletRequest request) + { + return (TopicPosts)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "posts.jsp"; + + } // end getTargetJSPName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public static String getPosterName(TopicMessageContext msg) + { + try + { // have to guard agains a DataException here + return msg.getCreatorName(); + + } // end try + catch (DataException de) + { // just return "unknown" on failure + return "(unknown)"; + + } // end catch + + } // end getPosterName + + public static String getMessageBodyText(TopicMessageContext msg) + { + try + { // have to guard against a DataException here + return msg.getBodyText(); + + } // end try + catch (DataException de) + { // just return an error message + return "(Unable to retrieve message data: " + StringUtil.encodeHTML(de.getMessage()) + ")"; + + } // end catch + + } // end getMessageBodyText + + public int getSIGID() + { + return sig.getSIGID(); + + } // end getSIGID + + public int getConfID() + { + return conf.getConfID(); + + } // end getConfID + + public int getTopicNumber() + { + return topic.getTopicNumber(); + + } // end getTopicNumber + + public int getNextTopicNumber() + { + return visit_order.getNext(); + + } // end getNextTopicNumber + + public String getConfLocator() + { + StringBuffer buf = new StringBuffer("sig="); + buf.append(sig.getSIGID()).append("&conf=").append(conf.getConfID()); + return buf.toString(); + + } // end getConfLocator + + public String getLocator() + { + if (cache_locator==null) + { // build up the standard locator + StringBuffer buf = new StringBuffer("sig="); + buf.append(sig.getSIGID()).append("&conf=").append(conf.getConfID()).append("&top="); + buf.append(topic.getTopicNumber()); + cache_locator = buf.toString(); + + } // end if + + return cache_locator; + + } // end getLocator + + public String getNextLocator() + { + StringBuffer buf = new StringBuffer("sig="); + buf.append(sig.getSIGID()).append("&conf=").append(conf.getConfID()).append("&top="); + buf.append(visit_order.getNext()); + return buf.toString(); + + } // end getNextLocator + + public String getIdentifyingData() + { + StringBuffer buf = new StringBuffer("Posts "); + buf.append(first).append(" through ").append(last).append(" in topic #").append(topic.getTopicID()); + return buf.toString(); + + } // end getIdentifyingData + + public String getTopicName() + { + return topic.getName(); + + } // end getTopicName + + public int getTotalMessages() + { + return topic.getTotalMessages(); + + } // end getTotalMessages + + public int getNewMessages() + { + return unread; + + } // end getNewMessages + + public Date getLastUpdate() + { + return topic.getLastUpdateDate(); + + } // end getLastUpdate + + public boolean isTopicHidden() + { + return topic.isHidden(); + + } // end isTopicHidden + + public boolean canDoNextTopic() + { + return visit_order.isNext(); + + } // end canDoNextTopic + + public boolean canFreezeTopic() + { + return topic.canFreeze(); + + } // end canFreezeTopic + + public boolean isTopicFrozen() + { + return topic.isFrozen(); + + } // end isTopicFrozen + + public boolean canArchiveTopic() + { + return topic.canArchive(); + + } // end canArchiveTopic + + public boolean isTopicArchived() + { + return topic.isArchived(); + + } // end isTopicArchived + + public boolean canDeleteTopic() + { + return false; // TODO: fix me + + } // end canDeleteTopic + + public boolean canScrollUp() + { + return (first>0); + + } // end canScrollUp + + public String getScrollUpLocator() + { + int new_first = first - 20; // TODO: configurable + int new_last = last - 1; + if (new_first<0) + { // normalize so we start at 0 + new_last += (-new_first); + new_first = 0; + + } // end if + + StringBuffer buf = new StringBuffer("p1="); + buf.append(new_first).append("&p2=").append(new_last); + return buf.toString(); + + } // end getScrollUpLocator + + public boolean canScrollDown() + { + return ((topic.getTotalMessages() - (1 + last))>=20); // TODO: configurable + + } // end canScrollDown + + public String getScrollDownLocator() + { + StringBuffer buf = new StringBuffer("p1="); + buf.append(last+1).append("&p2=").append(last+20); // TODO: configurable + return buf.toString(); + + } // end getScrollDownLocator + + public boolean canScrollToEnd() + { + return ((topic.getTotalMessages() - (1 + last))>0); + + } // end canScrollToEnd + + public String getScrollToEndLocator() + { + int my_last = topic.getTotalMessages(); + StringBuffer buf = new StringBuffer("p1="); + buf.append(my_last-20).append("&p2=").append(my_last-1); // TODO: configurable + return buf.toString(); + + } // end getScrollToEndLocator + + public Iterator getMessageIterator() + { + return messages.iterator(); + + } // end getMessageIterator() + + public boolean emitBreakLinePoint(int msg) + { + return (msg==(topic.getTotalMessages()-unread)); + + } // end emitBreakLinePoint + + public boolean showAdvanced() + { + return show_advanced && (last==first); + + } // end showAdvanced + + public boolean displayPostBox() + { + boolean flag1 = conf.canPostToConference(); + boolean flag2 = (topic.isFrozen() ? topic.canFreeze() : true); + boolean flag3 = (topic.isArchived() ? topic.canArchive() : true); + return flag1 && flag2 && flag3; + + } // end displayPostBox + + public String getDefaultPseud() + { + return conf.getDefaultPseud(); + + } // end getDefaultPseud + +} // end class TopicPosts diff --git a/web/format/posts.jsp b/web/format/posts.jsp new file mode 100644 index 0000000..f86c349 --- /dev/null +++ b/web/format/posts.jsp @@ -0,0 +1,327 @@ +<%-- + 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.*" %> +<% + TopicPosts data = TopicPosts.retrieve(request); + Variables.failIfNull(data); + RenderData rdat = RenderConfig.createRenderData(application,request,response); +%> +<% if (rdat.useHTMLComments()) { %><% } %> +<% + String tmp; + if (data.isTopicArchived()) + tmp = "(Archived) "; + else if (data.isTopicFrozen()) + tmp = "(Frozen) "; + else + tmp = ""; + rdat.writeContentHeader(out,data.getTopicName(),tmp + String.valueOf(data.getTotalMessages()) + + " Total; " + String.valueOf(data.getNewMessages()) + " New; Last: " + + rdat.formatDateForDisplay(data.getLastUpdate())); +%> + + + + + + + + + + +
    + <% if (rdat.useHTMLComments()) { %><% } %> + ">" ALT="Topic List" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% if (data.isTopicHidden()) { %> + ">" ALT="Show Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } else { %> + ">" ALT="Hide Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } // end if %> +    + <% if (data.canDoNextTopic()) { %> + ">" ALT="Next Topic" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% if (data.getNewMessages()>0) { %> + <%-- TODO: this doesn't do Keep New yet --%> + ">" ALT="Next & Keep New" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% } // end if %> + <% } // end if %> + + <% if (rdat.useHTMLComments()) { %><% } %> + <% if (data.canFreezeTopic()) { %> +    + <% if (data.isTopicFrozen()) { %> + ">" ALT="Unfreeze Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } else { %> + ">" ALT="Freeze Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } // end if %> + <% } // end if %> + <% if (data.canArchiveTopic()) { %> +    + <% if (data.isTopicArchived()) { %> + ">" ALT="Unarchive Topic" WIDTH=80 + HEIGHT=24 BORDER=0> + <% } else { %> + ">" ALT="Archive Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } // end if %> + <% } // end if %> + <% if (data.canDeleteTopic()) { %> +    + ">" ALT="Delete Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } // end if %> +
     
    + <% if (rdat.useHTMLComments()) { %><% } %> +
    "> + + + +    + " NAME="Go" ALT="Go" + ALIGN=BOTTOM WIDTH=80 HEIGHT=24 BORDER=0> +
    +
    <%= rdat.getStdFontTag(null,2) %> + <% if (rdat.useHTMLComments()) { %><% } %> + <%-- TODO: the number "20" should be configurable --%> + [  + ">View All + <% if (data.canScrollUp()) { %> +  |  + ">Scroll Up 20 + <% } // end if %> + <% if (data.canScrollDown()) { %> +  |  + ">Scroll Down 20 + <% } // end if %> + <% if (data.canScrollDown()) { %> +  |  + ">Scroll To End + <% } // end if %> +  |  + Bottom +  ] +
    + +<% if (rdat.useHTMLComments()) { %><% } %> +<% + Iterator it = data.getMessageIterator(); + String last_post = rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&shac=1&p1=" + + String.valueOf(data.getTotalMessages() - 1)); + boolean can_line = false; +%> +<% while (it.hasNext()) { %> + <% + TopicMessageContext msg = (TopicMessageContext)(it.next()); + String poster = data.getPosterName(msg); + %> + <% if (can_line && data.emitBreakLinePoint(msg.getPostNumber())) { %>
    <% } %> + <% if (data.showAdvanced()) { %> +
    +
    + <% } // end if %> + <%= rdat.getStdFontTag(null,2) %> + "><%= msg.getPostNumber() %> of + <%= data.getTotalMessages() - 1 %> + <%= rdat.getStdFontTag(null,1) %><<%= "TODO" %>>
    + <%= msg.getPseud() %> + ( + " TARGET="_blank"><%= poster %>, + <%= rdat.formatDateForDisplay(msg.getPostDate()) %> + ) + <%-- TODO: paperclip goes here if we have an attachment --%> +

    + <% if (msg.isScribbled()) { %> + + (Scribbled by <%= data.getMessageBodyText(msg) %> on + <%= rdat.formatDateForDisplay(msg.getScribbleDate()) %>) + + <% } else if (msg.isHidden() && !(data.showAdvanced())) { %> + + ">(Hidden + Message: <%= msg.getNumLines() %> <% if (msg.getNumLines()==1) { %>Line<% } else { %>Lines<% } %> + + <% } else { %> +

    <%= data.getMessageBodyText(msg) %>
    + <% } // end if %> + <% if (data.showAdvanced()) { %> +
    + <% if (!(msg.isScribbled())) { %> + <% if (msg.canHide()) { %> + <% if (msg.isHidden()) { %> + ">" ALT="Show" WIDTH=80 HEIGHT=24 BORDER=0>

    + <% } else { %> + ">" ALT="Hide" WIDTH=80 HEIGHT=24 BORDER=0>

    + <% } // end if %> + <% } // end if (can hide) %> + <% if (msg.canScribble()) { %> + ">" ALT="Scribble" WIDTH=80 HEIGHT=24 + BORDER=0>

    + <% } // end if (can scribble) %> + <% } // end if (not already scribbled) %> + <% if (msg.canNuke()) { %> + ">" ALT="Nuke" WIDTH=80 HEIGHT=24 + BORDER=0>

    + <% } // end if (can nuke) %> +


    + <% } // end if (showing advanced controls) %> + <% can_line = true; %> +<% } // end while %> +<% if (rdat.useHTMLComments()) { %><% } %> + + + + + + + + + + +
     <%= rdat.getStdFontTag(null,2) %> + <% if (rdat.useHTMLComments()) { %><% } %> + <%-- TODO: the number "20" should be configurable --%> + [  + ">View All + <% if (data.canScrollUp()) { %> +  |  + ">Scroll Up 20 + <% } // end if %> + <% if (data.canScrollDown()) { %> +  |  + ">Scroll Down 20 + <% } // end if %> + <% if (data.canScrollDown()) { %> +  |  + ">Scroll To End + <% } // end if %> +  |  + Top +  ] +
    + <% if (rdat.useHTMLComments()) { %><% } %> + ">" ALT="Topic List" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% if (data.isTopicHidden()) { %> + ">" ALT="Show Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } else { %> + ">" ALT="Hide Topic" WIDTH=80 HEIGHT=24 + BORDER=0> + <% } // end if %> +    + <% if (data.canDoNextTopic()) { %> + ">" ALT="Next Topic" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% if (data.getNewMessages()>0) { %> + <%-- TODO: this doesn't do Keep New yet --%> + ">" ALT="Next & Keep New" WIDTH=80 HEIGHT=24 + BORDER=0> +    + <% } // end if %> + <% } // end if %> +  
    + +<% if (data.displayPostBox()) { %> +
    <%= rdat.getStdFontTag(null,3) %>Post Message in "<%= data.getTopicName() %>": +
    +
    "> + + + + <% if (data.canDoNextTopic()) { %> + + <% } // end if %> + + + + + + + + + +
    + <%= rdat.getStdFontTag(null,2) %>Your name/header:
    + + <%= rdat.getStdFontTag(null,2) %> Attach a file +
    <%= rdat.getStdFontTag(null,2) %>Message:<%= rdat.getStdFontTag(null,2) %> + HTML Guide +
    + +
    + " ALT="Preview" NAME="preview" + WIDTH=80 HEIGHT=24 BORDER=0> +    + " ALT="Post & Reload" + NAME="post" WIDTH=80 HEIGHT=24 BORDER=0> + <% if (data.canDoNextTopic()) { %> +    + " ALT="Post & Go Next" + NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0> + <% } // end if %> +
    +
    +<% } else if (data.isTopicArchived()) { %> +
    <%= rdat.getStdFontTag(null,2) %>This is an Archived Topic
    +<% } 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 new file mode 100644 index 0000000..c8c52e5 --- /dev/null +++ b/web/format/preview.jsp @@ -0,0 +1,81 @@ +<%-- + 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.*" %> +<% + PostPreview data = PostPreview.retrieve(request); + Variables.failIfNull(data); + RenderData rdat = RenderConfig.createRenderData(application,request,response); +%> +<% rdat.writeContentHeader(out,"Previewing Post",null); %> +<%= rdat.getStdFontTag(null,3) %> + <% if (data.getNumSpellingErrors()==0) { %> + Your post did not contain any spelling errors. + <% } else if (data.getNumSpellingErrors()==1) { %> + There was 1 spelling error in your post. + <% } else { %> + There were <%= data.getNumSpellingErrors() %> spelling errors in your post. + <% } // end if %> + +

    <%= data.getPreviewData() %>

    + +
    "> + + + + <% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %> + + <% } // end if %> + + + + + + + + + +
    + <%= rdat.getStdFontTag(null,2) %>Your name/header:
    + + <%= rdat.getStdFontTag(null,2) %>CHECKED<% } %> > Attach a file +
    <%= rdat.getStdFontTag(null,2) %>Message:<%= rdat.getStdFontTag(null,2) %> + HTML Guide +
    + +
    + " ALT="Preview" NAME="preview" + WIDTH=80 HEIGHT=24 BORDER=0> +    + " ALT="Post & Reload" + NAME="post" WIDTH=80 HEIGHT=24 BORDER=0> + <% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %> +    + " ALT="Post & Go Next" + NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0> + <% } // end if %> +    + " ALT="Cancel" + NAME="cancel" WIDTH=80 HEIGHT=24 BORDER=0> +
    +
    +<% rdat.writeFooter(out); %> diff --git a/web/format/slippage.jsp b/web/format/slippage.jsp new file mode 100644 index 0000000..8313042 --- /dev/null +++ b/web/format/slippage.jsp @@ -0,0 +1,107 @@ +<%-- + 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.*" %> +<% + PostSlippage data = PostSlippage.retrieve(request); + Variables.failIfNull(data); + RenderData rdat = RenderConfig.createRenderData(application,request,response); +%> +<% if (rdat.useHTMLComments()) { %><% } %> +<% rdat.writeContentHeader(out,"Slippage or Double-Click Detected",null); %> +<%= rdat.getStdFontTag(null,2) %> + The following posts slipped in while you were typing. You may choose to edit your message and + re-post, just post it as is, or cancel your posting altogether. + + +<% if (rdat.useHTMLComments()) { %><% } %> +<% Iterator it = data.getMessageIterator(); %> +<% while (it.hasNext()) { %> + <% + TopicMessageContext msg = (TopicMessageContext)(it.next()); + String poster = data.getPosterName(msg); + %> + <%= rdat.getStdFontTag(null,2) %> + <%= msg.getPostNumber() %> of <%= data.getTotalMessages() - 1 %> + <%= rdat.getStdFontTag(null,1) %><<%= "TODO" %>>
    + <%= msg.getPseud() %> + ( + " TARGET="_blank"><%= poster %>, + <%= rdat.formatDateForDisplay(msg.getPostDate()) %> + ) +

    + <% if (msg.isScribbled()) { %> + + (Scribbled by <%= data.getMessageBodyText(msg) %> on + <%= rdat.formatDateForDisplay(msg.getScribbleDate()) %>) + + <% } else { %> +

    <%= data.getMessageBodyText(msg) %>
    + <% } // end if %> +<% } // end while %> +<% if (rdat.useHTMLComments()) { %><% } %> + +
    <%= rdat.getStdFontTag(null,3) %>Post Message in "<%= data.getTopicName() %>": +
    +
    "> + + + + <% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %> + + <% } // end if %> + + + + + + + + + +
    + <%= rdat.getStdFontTag(null,2) %>Your name/header:
    + + <%= rdat.getStdFontTag(null,2) %>CHECKED<% } %> > Attach a file +
    <%= rdat.getStdFontTag(null,2) %>Message:<%= rdat.getStdFontTag(null,2) %> + HTML Guide +
    + +
    + " ALT="Preview" NAME="preview" + WIDTH=80 HEIGHT=24 BORDER=0> +    + " ALT="Post & Reload" + NAME="post" WIDTH=80 HEIGHT=24 BORDER=0> + <% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %> +    + " ALT="Post & Go Next" + NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0> + <% } // end if %> +    + " ALT="Cancel" + NAME="cancel" WIDTH=80 HEIGHT=24 BORDER=0> +
    +
    +<% rdat.writeFooter(out); %> + + diff --git a/web/format/topics.jsp b/web/format/topics.jsp index 9e3008b..bdc7ce4 100644 --- a/web/format/topics.jsp +++ b/web/format/topics.jsp @@ -24,8 +24,7 @@ TopicListing data = TopicListing.retrieve(request); Variables.failIfNull(data); RenderData rdat = RenderConfig.createRenderData(application,request,response); - String self = "confdisp?sig=" + String.valueOf(data.getSIGID()) + "&conf=" - + String.valueOf(data.getConfID()); + String self = "confdisp?" + data.getLocator(); String tmp; %> <% if (rdat.useHTMLComments()) { %><% } %> @@ -36,14 +35,14 @@ SRC="<%= rdat.getFullImagePath("bn_conference_list.gif") %>" ALT="Conference List" WIDTH=80 HEIGHT=24 BORDER=0>   <% if (data.canCreateTopic()) { %> - <% tmp = "confops?sig=" + String.valueOf(data.getSIGID()) + "&conf=" - + String.valueOf(data.getConfID()) + "&cmd=T"; %> - " - ALT="Add Topic" WIDTH=80 HEIGHT=24 BORDER=0>   + <% tmp = rdat.getEncodedServletPath("confops?" + data.getLocator() + "&cmd=T"); %> + " ALT="Add Topic" + WIDTH=80 HEIGHT=24 BORDER=0>   <% } // end if %> <% if (data.canDoReadNew()) { %> - " - ALT="Read New" WIDTH=80 HEIGHT=24 BORDER=0>   + ">" ALT="Read New" WIDTH=80 HEIGHT=24 + BORDER=0>   <% } // end if %> " ALT="Manage" WIDTH=80 HEIGHT=24 BORDER=0>   @@ -59,52 +58,61 @@ <% tmp = self + "&sort=" + String.valueOf(data.isSort(ConferenceContext.SORT_NUMBER) ? -ConferenceContext.SORT_NUMBER : ConferenceContext.SORT_NUMBER); %> - # + # <%= rdat.getStdFontTag(null,2) %> <% tmp = self + "&sort=" + String.valueOf(data.isSort(ConferenceContext.SORT_NAME) ? -ConferenceContext.SORT_NAME : ConferenceContext.SORT_NAME); %> - Topic Name + Topic Name <%= rdat.getStdFontTag(null,2) %> <% tmp = self + "&sort=" + String.valueOf(data.isSort(ConferenceContext.SORT_UNREAD) ? -ConferenceContext.SORT_UNREAD : ConferenceContext.SORT_UNREAD); %> - New + New <%= rdat.getStdFontTag(null,2) %> <% tmp = self + "&sort=" + String.valueOf(data.isSort(ConferenceContext.SORT_TOTAL) ? -ConferenceContext.SORT_TOTAL : ConferenceContext.SORT_TOTAL); %> - Total + Total <%= rdat.getStdFontTag(null,2) %> <% tmp = self + "&sort=" + String.valueOf(data.isSort(ConferenceContext.SORT_DATE) ? -ConferenceContext.SORT_DATE : ConferenceContext.SORT_DATE); %> - Last Response + Last Response <%= rdat.getStdFontTag(null,2) %>  <% Iterator it = data.getTopicIterator(); %> <% while (it.hasNext()) { %> - <% TopicContext topic = (TopicContext)(it.next()); %> + <% + TopicContext topic = (TopicContext)(it.next()); + tmp = self + "&top=" + String.valueOf(topic.getTopicNumber()) + "&rnm=1"; + %> <%= rdat.getStdFontTag(null,2) %> - <%= topic.getTopicNumber() %> + <%= topic.getTopicNumber() %>   <%= rdat.getStdFontTag(null,2) %> - <%= topic.getName() %> + <%= topic.getName() %> + <% if (topic.isArchived() && !(data.isView(ConferenceContext.DISPLAY_ARCHIVED))) { %> + (archived) + <% } else if (topic.isFrozen()) { %> + (frozen) + <% } // end if %> <%= rdat.getStdFontTag(null,2) %> - <%= topic.getUnreadMessages() %> + <%= topic.getUnreadMessages() %> <%= rdat.getStdFontTag(null,2) %> - <%= topic.getTotalMessages() %> + "><%= topic.getTotalMessages() %> <%= rdat.getStdFontTag(null,2) %> - <%= rdat.formatDateForDisplay(topic.getLastUpdateDate()) %> + <%= rdat.formatDateForDisplay(topic.getLastUpdateDate()) %> <% } // end while (more topics in enumeration) %> diff --git a/web/images/bn_archive_topic.gif b/web/images/bn_archive_topic.gif new file mode 100644 index 0000000..fabfe17 Binary files /dev/null and b/web/images/bn_archive_topic.gif differ diff --git a/web/images/bn_delete_topic.gif b/web/images/bn_delete_topic.gif new file mode 100644 index 0000000..18b5ab3 Binary files /dev/null and b/web/images/bn_delete_topic.gif differ diff --git a/web/images/bn_freeze_topic.gif b/web/images/bn_freeze_topic.gif new file mode 100644 index 0000000..b9b5cb8 Binary files /dev/null and b/web/images/bn_freeze_topic.gif differ diff --git a/web/images/bn_go.gif b/web/images/bn_go.gif new file mode 100644 index 0000000..d878819 Binary files /dev/null and b/web/images/bn_go.gif differ diff --git a/web/images/bn_hide.gif b/web/images/bn_hide.gif new file mode 100644 index 0000000..21f49f5 Binary files /dev/null and b/web/images/bn_hide.gif differ diff --git a/web/images/bn_hide_topic.gif b/web/images/bn_hide_topic.gif new file mode 100644 index 0000000..1261a7b Binary files /dev/null and b/web/images/bn_hide_topic.gif differ diff --git a/web/images/bn_next_keep_new.gif b/web/images/bn_next_keep_new.gif new file mode 100644 index 0000000..23c1039 Binary files /dev/null and b/web/images/bn_next_keep_new.gif differ diff --git a/web/images/bn_next_topic.gif b/web/images/bn_next_topic.gif new file mode 100644 index 0000000..17ea4c6 Binary files /dev/null and b/web/images/bn_next_topic.gif differ diff --git a/web/images/bn_nuke.gif b/web/images/bn_nuke.gif new file mode 100644 index 0000000..bed1d6c Binary files /dev/null and b/web/images/bn_nuke.gif differ diff --git a/web/images/bn_post_go_next.gif b/web/images/bn_post_go_next.gif new file mode 100644 index 0000000..612ac73 Binary files /dev/null and b/web/images/bn_post_go_next.gif differ diff --git a/web/images/bn_post_reload.gif b/web/images/bn_post_reload.gif new file mode 100644 index 0000000..3a3bd31 Binary files /dev/null and b/web/images/bn_post_reload.gif differ diff --git a/web/images/bn_scribble.gif b/web/images/bn_scribble.gif new file mode 100644 index 0000000..6978473 Binary files /dev/null and b/web/images/bn_scribble.gif differ diff --git a/web/images/bn_show.gif b/web/images/bn_show.gif new file mode 100644 index 0000000..5b2970c Binary files /dev/null and b/web/images/bn_show.gif differ diff --git a/web/images/bn_show_topic.gif b/web/images/bn_show_topic.gif new file mode 100644 index 0000000..6f3e856 Binary files /dev/null and b/web/images/bn_show_topic.gif differ diff --git a/web/images/bn_topic_list.gif b/web/images/bn_topic_list.gif new file mode 100644 index 0000000..2c55fe2 Binary files /dev/null and b/web/images/bn_topic_list.gif differ diff --git a/web/images/bn_unarchive_topic.gif b/web/images/bn_unarchive_topic.gif new file mode 100644 index 0000000..3b24cdc Binary files /dev/null and b/web/images/bn_unarchive_topic.gif differ diff --git a/web/images/bn_unfreeze_topic.gif b/web/images/bn_unfreeze_topic.gif new file mode 100644 index 0000000..9100a9d Binary files /dev/null and b/web/images/bn_unfreeze_topic.gif differ