* landed support for reading topics and posting followup messages to a topic -

the basis of the conferencing engine is now firmly in place
* tweaks to the HTML Checker to make it better at breaking lines without
  leaving stranded punctuation at the beginning or end of a line
* also modified dictionary to better handle possessives and hyphenates
* as always, miscellaneous tweaks and bugfizes as I spot them
This commit is contained in:
Eric J. Bowersox
2001-02-07 21:12:38 +00:00
parent 8bcc80ddd7
commit 66b7fea53b
50 changed files with 3304 additions and 75 deletions

View File

@@ -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

View File

@@ -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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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

View File

@@ -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

View File

@@ -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)
*--------------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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<this.level)
return c.canScribblePosts(this.level);
else
return c.canScribblePosts(level);
} // end canScribblePosts
public boolean canNukePosts(int level)
{
ConferenceData c = getConferenceDataNE();
if (c==null)
return false;
if (level<this.level)
return c.canNukePosts(this.level);
else
return c.canNukePosts(level);
} // end canNukePosts
public void touchUpdate(Connection conn, java.util.Date date) throws DataException
{
getConferenceData().touchUpdate(conn,date);
} // end touchUpdate
} // end class ConferenceSIGContextImpl

View File

@@ -1047,6 +1047,42 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end realConfAlias
public boolean userCanScribble()
{
ConferenceSIGContext c = getConferenceDataNE();
if (c==null)
return false;
return c.canScribblePosts(level);
} // end userCanScribble
public boolean userCanNuke()
{
ConferenceSIGContext c = getConferenceDataNE();
if (c==null)
return false;
return c.canNukePosts(level);
} // end userCanNuke
public boolean userCanRead()
{
return canReadConference();
} // end userCanRead
public boolean userCanPost()
{
return canPostToConference();
} // end userCanPost
public void touchUpdate(Connection conn, java.util.Date date) throws DataException
{
getConferenceData().touchUpdate(conn,date);
} // end getConferenceData
/*--------------------------------------------------------------------------------
* Static functions usable only from within the package
*--------------------------------------------------------------------------------

View File

@@ -0,0 +1,706 @@
/*
* 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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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 = "<EM><B>(Scribbled)</B></EM>"; // 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

View File

@@ -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
*--------------------------------------------------------------------------------

View File

@@ -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'];

View File

@@ -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<word.length(); i++)
{ // loop through the characters and move down the trie
char ch = word.charAt(i);
if (((ch<'a') || (ch>'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<real_word.length(); i++)
{ // loop through the characters and move down the trie
char ch = real_word.charAt(i);
if (((ch<'a') || (ch>'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<real_word.length(); i++)
{ // move through the word - bail out if bogus chars
char ch = real_word.charAt(i);
if (((ch<'a') || (ch>'z')) && (ch!='-'))
if (((ch<'a') || (ch>'z')) && (ch!='-') && (ch!='\''))
return;
// advance down through trie

View File

@@ -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 (l<buf.length())
{ // see if there's a break from word characters to non-word characters
if (isWordChar(buf.charAt(l))!=word_char)
while ((start+l)<buf.length())
{ // see if there's a break from word characters to non-word characters, or vice versa
if (isWordChar(buf.charAt(start+l))!=word_char)
break;
l++;
@@ -145,7 +152,13 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
return l;
} // end getStringRunLength
} // end getRunLength
private static final int getRunLength(StringBuffer buf)
{
return getRunLength(buf,0);
} // end getRunLength
private void copyRewriters(Vector dest, List source)
{
@@ -343,6 +356,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
{ // calculate where the next line break is
int line_break = temp_buffer.toString().indexOf('\n');
int output_len = line_break;
boolean force_line_break = false;
if (output_len<0)
output_len = temp_buffer.length();
@@ -351,6 +365,8 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
int remain_space = (int)(config.getWordWrapLength() - columns);
if (remain_space<output_len)
output_len = remain_space;
if (output_len<=0) // this means that NONE of the whitespace would fit on this line...
force_line_break = true; // we need a line break to fill in!
} // end if
@@ -366,9 +382,14 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
temp_buffer.setLength(0);
} // end if
else // no more line breaks on this line - clear out the buffer
else
{ // no more line breaks on this line - clear out the buffer
if (force_line_break)
emitLineBreak(); // notice we can only force a line break if we didn't have one in the text
temp_buffer.setLength(0);
} // end else
} // end while (still data in temp buffer)
} // end doFlushWhitespace
@@ -418,23 +439,25 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end if
boolean first = true;
while (temp_buffer.length()>0)
{ // 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<sublen) && (temp_buffer.charAt(hyph_count)=='-'))
final String hyph_apos = "-\'";
while ((hyph_count<sublen) && (hyph_apos.indexOf(temp_buffer.charAt(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<temp_buffer.length()) && first && (sublen<=MARGIN_SLOP))
{ // get the length of the word and ensure they can be kept together
int fw_len = getRunLength(temp_buffer,sublen);
ensureSpaceOnLine(sublen+fw_len);
} // end if
emitFromStartOfTempBuffer(sublen);
} // end else
first = false;
} // end while
} // end doFlushString
@@ -884,7 +933,8 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end if
parse(str); // parse things
if (str!=null)
parse(str); // parse things
} // end append

View File

@@ -54,5 +54,9 @@ public interface Audit
public static final int DELETE_TOPIC = 307;
public static final int TOPIC_FREEZE = 308;
public static final int TOPIC_ARCHIVE = 309;
public static final int POST_MESSAGE = 310;
public static final int HIDE_MESSAGE = 311;
public static final int SCRIBBLE_MESSAGE = 312;
public static final int NUKE_MESSAGE = 313;
} // end interface Audit

View File

@@ -29,6 +29,47 @@ import com.silverwrist.venice.servlets.format.*;
public class ConfDisplay extends VeniceServlet
{
/*--------------------------------------------------------------------------------
* Internal class used to get post number defaults
*--------------------------------------------------------------------------------
*/
static class PostInterval
{
private int first;
private int last;
public PostInterval(int f, int l)
{
if (f<=l)
{ // the sort is good
first = f;
last = l;
} // end if
else
{ // reverse the order
first = l;
last = f;
} // end else
} // end constructor
public int getFirst()
{
return first;
} // end getFirst
public int getLast()
{
return last;
} // end getLast
} // end class PostInterval
/*--------------------------------------------------------------------------------
* Static data members
*--------------------------------------------------------------------------------
@@ -183,6 +224,106 @@ public class ConfDisplay extends VeniceServlet
} // end getViewSortDefaults
private static PostInterval getInterval(ServletRequest request, TopicContext topic)
throws ValidationException
{
int first, last;
String foo = request.getParameter("pxg");
if (!(StringUtil.isStringEmpty(foo)))
{ // we have a Go box parameter - try and decode it
try
{ // look for a range specifier
int p = foo.indexOf('-');
if (p<0)
{ // single post number - try and use it
first = Integer.parseInt(foo);
last = first;
} // end if
else if (p==0)
{ // "-number" - works like "0-number"
last = Integer.parseInt(foo.substring(1));
first = 0;
} // end if
else if (p==(foo.length()-1))
{ // "number-" - works like "number-end"
first = Integer.parseInt(foo.substring(0,p));
last = topic.getTotalMessages() - 1;
} // end else if
else
{ // two numbers to decode
first = Integer.parseInt(foo.substring(0,p));
last = Integer.parseInt(foo.substring(p+1));
} // end else
return new PostInterval(first,last);
} // end try
catch (NumberFormatException nfe)
{ // if numeric conversion fails, just fall out and try to redisplay the other way
} // end catch
} // end if
foo = request.getParameter("p1");
if (StringUtil.isStringEmpty(foo))
{ // no range specified - cook up a default one
last = topic.getTotalMessages();
int ur = topic.getUnreadMessages();
if ((ur==0) || (ur>=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

View File

@@ -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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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

View File

@@ -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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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

View File

@@ -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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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 "<EM>(Unable to retrieve message data: " + StringUtil.encodeHTML(de.getMessage()) + ")</EM>";
} // 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

View File

@@ -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();

View File

@@ -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 <http://www.mozilla.org/MPL/>.
*
* 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 <erbo@silcom.com>,
* 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 "<EM>(Unable to retrieve message data: " + StringUtil.encodeHTML(de.getMessage()) + ")</EM>";
} // 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