* 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -51,7 +51,7 @@
<LI>Output uses XML and gets formatted into [X]HTML via XSLT; "themeable"/"skinnable" interface</LI>
<LI>Conferencing/other functionality available via XML-RPC or SOAP calls</LI>
<LI>News page creation (see Slash, Squishdot, Scoop)</LI>
<LI>Member trust metrics (see Slashdot "karma," Scoop "mojo," Advogato distributed trust metric)</LI>
<LI>Member trust metrics (see Slashdot "karma," Scoop "mojo," Advogato distributed trust metric) <B><EM>(Feature indefinitely shelved at the request of the EMinds community)</EM></B></LI>
<LI>User diary pages (see Advogato, Kuro5hin)</LI>
<LI>Moderated discussions (see Slash, Scoop)</LI>
<LI>Collaborative database facility (see Wiki, Everything2)</LI>

View File

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

View File

@ -142,6 +142,14 @@
<servlet-class>com.silverwrist.venice.servlets.ConfDisplay</servlet-class>
</servlet>
<servlet>
<servlet-name>postmessage</servlet-name>
<description>
Posting messages to a conference.
</description>
<servlet-class>com.silverwrist.venice.servlets.PostMessage</servlet-class>
</servlet>
<!-- the following are test servlets, they should go away -->
<servlet>
@ -200,6 +208,11 @@
<url-pattern>/confdisp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>postmessage</servlet-name>
<url-pattern>/post</url-pattern>
</servlet-mapping>
<!-- the following are test servlets, they should go away -->
<servlet-mapping>
<servlet-name>testformdata</servlet-name>

View File

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

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,20 +137,8 @@ class TopicUserContextImpl implements TopicContext
} // end if
/*--------------------------------------------------------------------------------
* Implementatuions from interface TopicContext
*--------------------------------------------------------------------------------
*/
public void refresh() throws DataException
private void refresh(Connection conn) throws SQLException
{
if (logger.isDebugEnabled())
logger.debug("refreshing topic ID " + String.valueOf(topicid));
Connection conn = null; // pooled database connection
try
{ // get a database connection
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
// perform a requery of the database
@ -165,6 +156,24 @@ class TopicUserContextImpl implements TopicContext
else // this topic must have been deleted - fsck it
makeDeleted();
} // end refresh
/*--------------------------------------------------------------------------------
* Implementations from interface TopicContext
*--------------------------------------------------------------------------------
*/
public void refresh() throws DataException
{
if (logger.isDebugEnabled())
logger.debug("refreshing topic ID " + String.valueOf(topicid));
Connection conn = null; // pooled database connection
try
{ // get a database connection
conn = datapool.getConnection();
refresh(conn);
} // end try
catch (SQLException e)
{ // turn SQLException into data exception
@ -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,6 +933,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end if
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

327
web/format/posts.jsp Normal file
View File

@ -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 <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):
--%>
<%@ 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()) { %><!-- <%= data.getIdentifyingData() %> --><% } %>
<%
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()));
%>
<TABLE BORDER=0 WIDTH="100%" CELLPADDING=0 CELLSPACING=0>
<TR VALIGN=BOTTOM>
<TD NOWRAP ALIGN=LEFT>
<% if (rdat.useHTMLComments()) { %><!-- Topic user controls section --><% } %>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getConfLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_topic_list.gif") %>" ALT="Topic List" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% if (data.isTopicHidden()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_show_topic.gif") %>" ALT="Show Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } else { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_hide_topic.gif") %>" ALT="Hide Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } // end if %>
&nbsp;&nbsp;
<% if (data.canDoNextTopic()) { %>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getNextLocator() + "&rnm=1") %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_next_topic.gif") %>" ALT="Next Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% if (data.getNewMessages()>0) { %>
<%-- TODO: this doesn't do Keep New yet --%>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getNextLocator() + "&rnm=1") %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_next_keep_new.gif") %>" ALT="Next & Keep New" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% } // end if %>
<% } // end if %>
</TD>
<TD NOWRAP ALIGN=RIGHT>
<% if (rdat.useHTMLComments()) { %><!-- Topic admin controls section --><% } %>
<% if (data.canFreezeTopic()) { %>
&nbsp;&nbsp;
<% if (data.isTopicFrozen()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_unfreeze_topic.gif") %>" ALT="Unfreeze Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } else { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_freeze_topic.gif") %>" ALT="Freeze Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } // end if %>
<% } // end if %>
<% if (data.canArchiveTopic()) { %>
&nbsp;&nbsp;
<% if (data.isTopicArchived()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_unarchive_topic.gif") %>" ALT="Unarchive Topic" WIDTH=80
HEIGHT=24 BORDER=0></A>
<% } else { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_archive_topic.gif") %>" ALT="Archive Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } // end if %>
<% } // end if %>
<% if (data.canDeleteTopic()) { %>
&nbsp;&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_delete_topic.gif") %>" ALT="Delete Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } // end if %>
</TD>
</TR>
<TR><TD COLSPAN=2>&nbsp;</TD></TR>
<TR VALIGN=BOTTOM>
<TD NOWRAP ALIGN=LEFT>
<% if (rdat.useHTMLComments()) { %><!-- Go box --><% } %>
<FORM METHOD="GET" ACTION="<%= rdat.getEncodedServletPath("confdisp") %>">
<INPUT TYPE="HIDDEN" NAME="sig" VALUE="<%= data.getSIGID() %>">
<INPUT TYPE="HIDDEN" NAME="conf" VALUE="<%= data.getConfID() %>">
<INPUT TYPE="HIDDEN" NAME="top" VALUE="<%= data.getTopicNumber() %>">
<INPUT TYPE="TEXT" NAME="pxg" VALUE="" SIZE=6 MAXLENGTH=13>&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_go.gif") %>" NAME="Go" ALT="Go"
ALIGN=BOTTOM WIDTH=80 HEIGHT=24 BORDER=0>
</FORM>
</TD>
<TD NOWRAP ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<% if (rdat.useHTMLComments()) { %><!-- Upper navigation linkset --><% } %>
<%-- TODO: the number "20" should be configurable --%>
<A NAME="top">[</A>&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&p1=0&p2=-1") %>">View All</A>
<% if (data.canScrollUp()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollUpLocator()) %>">Scroll Up 20</A>
<% } // end if %>
<% if (data.canScrollDown()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollDownLocator()) %>">Scroll Down 20</A>
<% } // end if %>
<% if (data.canScrollDown()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollToEndLocator()) %>">Scroll To End</A>
<% } // end if %>
&nbsp;|&nbsp;
<A HREF="#bottom">Bottom</A>
&nbsp;]
</FONT></TD>
</TR>
</TABLE>
<% if (rdat.useHTMLComments()) { %><!-- Begin Actual Messages --><% } %>
<%
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())) { %><HR WIDTH="70%"><% } %>
<% if (data.showAdvanced()) { %>
<BR>
<TABLE WIDTH="100%" BORDER=0 CELLPADDING=0 CELLSPACING=0><TR VALIGN=TOP><TD NOWRAP ALIGN=LEFT>
<% } // end if %>
<%= rdat.getStdFontTag(null,2) %>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&shac=1&p1="
+ String.valueOf(msg.getPostNumber())) %>"><%= msg.getPostNumber() %></A> of
<A HREF="<%= last_post %>"><%= data.getTotalMessages() - 1 %></A>
<%= rdat.getStdFontTag(null,1) %>&lt;<%= "TODO" %>&gt;</FONT><BR>
<B><%= msg.getPseud() %></B>
(<EM>
<A HREF="<%= rdat.getEncodedServletPath("user/" + poster) %>" TARGET="_blank"><%= poster %></A>,
<%= rdat.formatDateForDisplay(msg.getPostDate()) %>
</EM>)
<%-- TODO: paperclip goes here if we have an attachment --%>
</FONT><P>
<% if (msg.isScribbled()) { %>
<TT><EM><B>
(Scribbled by <%= data.getMessageBodyText(msg) %> on
<%= rdat.formatDateForDisplay(msg.getScribbleDate()) %>)
</B></EM></TT>
<% } else if (msg.isHidden() && !(data.showAdvanced())) { %>
<TT><EM><B>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&shac=1&p1="
+ String.valueOf(msg.getPostNumber())) %>">(Hidden
Message: <%= msg.getNumLines() %> <% if (msg.getNumLines()==1) { %>Line<% } else { %>Lines<% } %></A>
</B></EM></TT>
<% } else { %>
<PRE><%= data.getMessageBodyText(msg) %></PRE>
<% } // end if %>
<% if (data.showAdvanced()) { %>
</TD><TD NOWRAP ALIGN=RIGHT>
<% if (!(msg.isScribbled())) { %>
<% if (msg.canHide()) { %>
<% if (msg.isHidden()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_show.gif") %>" ALT="Show" WIDTH=80 HEIGHT=24 BORDER=0></A><P>
<% } else { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_hide.gif") %>" ALT="Hide" WIDTH=80 HEIGHT=24 BORDER=0></A><P>
<% } // end if %>
<% } // end if (can hide) %>
<% if (msg.canScribble()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_scribble.gif") %>" ALT="Scribble" WIDTH=80 HEIGHT=24
BORDER=0></A><P>
<% } // end if (can scribble) %>
<% } // end if (not already scribbled) %>
<% if (msg.canNuke()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_nuke.gif") %>" ALT="Nuke" WIDTH=80 HEIGHT=24
BORDER=0></A><P>
<% } // end if (can nuke) %>
</TD></TR></TABLE><BR>
<% } // end if (showing advanced controls) %>
<% can_line = true; %>
<% } // end while %>
<% if (rdat.useHTMLComments()) { %><!-- End Actual Messages --><% } %>
<TABLE BORDER=0 WIDTH="100%" CELLPADDING=0 CELLSPACING=0>
<TR VALIGN=BOTTOM>
<TD NOWRAP ALIGN=LEFT>&nbsp;</TD>
<TD NOWRAP ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<% if (rdat.useHTMLComments()) { %><!-- Upper navigation linkset --><% } %>
<%-- TODO: the number "20" should be configurable --%>
<A NAME="bottom">[</A>&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&p1=0&p2=-1") %>">View All</A>
<% if (data.canScrollUp()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollUpLocator()) %>">Scroll Up 20</A>
<% } // end if %>
<% if (data.canScrollDown()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollDownLocator()) %>">Scroll Down 20</A>
<% } // end if %>
<% if (data.canScrollDown()) { %>
&nbsp;|&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getLocator() + "&"
+ data.getScrollToEndLocator()) %>">Scroll To End</A>
<% } // end if %>
&nbsp;|&nbsp;
<A HREF="#top">Top</A>
&nbsp;]
</FONT></TD>
</TR>
<TR VALIGN=BOTTOM>
<TD NOWRAP ALIGN=LEFT>
<% if (rdat.useHTMLComments()) { %><!-- Topic user controls section --><% } %>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getConfLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_topic_list.gif") %>" ALT="Topic List" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% if (data.isTopicHidden()) { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_show_topic.gif") %>" ALT="Show Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } else { %>
<A HREF="<%= rdat.getEncodedServletPath("TODO?" + data.getLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_hide_topic.gif") %>" ALT="Hide Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
<% } // end if %>
&nbsp;&nbsp;
<% if (data.canDoNextTopic()) { %>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getNextLocator() + "&rnm=1") %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_next_topic.gif") %>" ALT="Next Topic" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% if (data.getNewMessages()>0) { %>
<%-- TODO: this doesn't do Keep New yet --%>
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getNextLocator() + "&rnm=1") %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_next_keep_new.gif") %>" ALT="Next & Keep New" WIDTH=80 HEIGHT=24
BORDER=0></A>
&nbsp;&nbsp;
<% } // end if %>
<% } // end if %>
</TD>
<TD NOWRAP ALIGN=RIGHT>&nbsp;</TD>
</TR>
</TABLE>
<% if (data.displayPostBox()) { %>
<HR><%= rdat.getStdFontTag(null,3) %><B>Post Message in &quot;<%= data.getTopicName() %>&quot;:</B></FONT>
<BR>
<FORM METHOD="POST" ACTION="<%= rdat.getEncodedServletPath("post") %>">
<INPUT TYPE="HIDDEN" NAME="sig" VALUE="<%= data.getSIGID() %>">
<INPUT TYPE="HIDDEN" NAME="conf" VALUE="<%= data.getConfID() %>">
<INPUT TYPE="HIDDEN" NAME="top" VALUE="<%= data.getTopicNumber() %>">
<% if (data.canDoNextTopic()) { %>
<INPUT TYPE="HIDDEN" NAME="next" VALUE="<%= data.getNextTopicNumber() %>">
<% } // end if %>
<INPUT TYPE="HIDDEN" NAME="sd" VALUE="<%= data.getTotalMessages() %>">
<TABLE BORDER=0 CELLPADDING=0>
<TR><TD ALIGN=LEFT COLSPAN=2>
<%= rdat.getStdFontTag(null,2) %>Your name/header:</FONT><BR>
<INPUT TYPE="TEXT" NAME="pseud" SIZE=37 MAXLENGTH=255 VALUE="<%= data.getDefaultPseud() %>">
<%= rdat.getStdFontTag(null,2) %><INPUT TYPE="CHECKBOX" NAME="attach" VALUE="Y"> Attach a file</FONT>
</TD></TR>
<TR>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>Message:</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO" TARGET="_blank">HTML Guide</A>
</FONT></TD>
</TR>
<TR><TD ALIGN=LEFT COLSPAN=2>
<TEXTAREA NAME="pb" WRAP=SOFT ROWS=7 COLS=51></TEXTAREA>
</TD></TR>
<TR><TD ALIGN=CENTER COLSPAN=2>
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_preview.gif") %>" ALT="Preview" NAME="preview"
WIDTH=80 HEIGHT=24 BORDER=0>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_reload.gif") %>" ALT="Post & Reload"
NAME="post" WIDTH=80 HEIGHT=24 BORDER=0>
<% if (data.canDoNextTopic()) { %>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_go_next.gif") %>" ALT="Post & Go Next"
NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0>
<% } // end if %>
</TD></TR>
</TABLE>
</FORM>
<% } else if (data.isTopicArchived()) { %>
<DIV ALIGN=CENTER><%= rdat.getStdFontTag(null,2) %><B>This is an <EM>Archived</EM> Topic</B></DIV>
<% } else if (data.isTopicFrozen()) { %>
<DIV ALIGN=CENTER><%= rdat.getStdFontTag(null,2) %><B>This is a <EM>Frozen</EM> Topic</B></DIV>
<% } // end if %>
<% rdat.writeFooter(out); %>

81
web/format/preview.jsp Normal file
View File

@ -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 <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):
--%>
<%@ 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) %><B>
<% 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 %>
</B></FONT>
<P><PRE><%= data.getPreviewData() %></PRE><HR>
<FORM METHOD="POST" ACTION="<%= rdat.getEncodedServletPath("post") %>">
<INPUT TYPE="HIDDEN" NAME="sig" VALUE="<%= data.getSIGID() %>">
<INPUT TYPE="HIDDEN" NAME="conf" VALUE="<%= data.getConfID() %>">
<INPUT TYPE="HIDDEN" NAME="top" VALUE="<%= data.getTopicNumber() %>">
<% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %>
<INPUT TYPE="HIDDEN" NAME="next" VALUE="<%= data.getNextVal() %>">
<% } // end if %>
<INPUT TYPE="HIDDEN" NAME="sd" VALUE="<%= data.getTotalMessages() %>">
<TABLE BORDER=0 CELLPADDING=0>
<TR><TD ALIGN=LEFT COLSPAN=2>
<%= rdat.getStdFontTag(null,2) %>Your name/header:</FONT><BR>
<INPUT TYPE="TEXT" NAME="pseud" SIZE=37 MAXLENGTH=255 VALUE="<%= data.getPseud() %>">
<%= rdat.getStdFontTag(null,2) %><INPUT TYPE="CHECKBOX" NAME="attach"
VALUE="Y" <% if (data.attachChecked()) { %>CHECKED<% } %> > Attach a file</FONT>
</TD></TR>
<TR>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>Message:</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO" TARGET="_blank">HTML Guide</A>
</FONT></TD>
</TR>
<TR><TD ALIGN=LEFT COLSPAN=2>
<TEXTAREA NAME="pb" WRAP=SOFT ROWS=7 COLS=51><%= data.getBodyText() %></TEXTAREA>
</TD></TR>
<TR><TD ALIGN=CENTER COLSPAN=2>
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_preview.gif") %>" ALT="Preview" NAME="preview"
WIDTH=80 HEIGHT=24 BORDER=0>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_reload.gif") %>" ALT="Post & Reload"
NAME="post" WIDTH=80 HEIGHT=24 BORDER=0>
<% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_go_next.gif") %>" ALT="Post & Go Next"
NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0>
<% } // end if %>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_cancel.gif") %>" ALT="Cancel"
NAME="cancel" WIDTH=80 HEIGHT=24 BORDER=0>
</TD></TR>
</TABLE>
</FORM>
<% rdat.writeFooter(out); %>

107
web/format/slippage.jsp Normal file
View File

@ -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 <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):
--%>
<%@ 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()) { %><!-- <%= data.getIdentifyingData() %> --><% } %>
<% 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.
</FONT>
<% if (rdat.useHTMLComments()) { %><!-- Begin Slipped Messages --><% } %>
<% Iterator it = data.getMessageIterator(); %>
<% while (it.hasNext()) { %>
<%
TopicMessageContext msg = (TopicMessageContext)(it.next());
String poster = data.getPosterName(msg);
%>
<%= rdat.getStdFontTag(null,2) %>
<B><%= msg.getPostNumber() %></B> of <B><%= data.getTotalMessages() - 1 %></B>
<%= rdat.getStdFontTag(null,1) %>&lt;<%= "TODO" %>&gt;</FONT><BR>
<B><%= msg.getPseud() %></B>
(<EM>
<A HREF="<%= rdat.getEncodedServletPath("user/" + poster) %>" TARGET="_blank"><%= poster %></A>,
<%= rdat.formatDateForDisplay(msg.getPostDate()) %>
</EM>)
</FONT><P>
<% if (msg.isScribbled()) { %>
<TT><EM><B>
(Scribbled by <%= data.getMessageBodyText(msg) %> on
<%= rdat.formatDateForDisplay(msg.getScribbleDate()) %>)
</B></EM></TT>
<% } else { %>
<PRE><%= data.getMessageBodyText(msg) %></PRE>
<% } // end if %>
<% } // end while %>
<% if (rdat.useHTMLComments()) { %><!-- End Slipped Messages --><% } %>
<HR><%= rdat.getStdFontTag(null,3) %><B>Post Message in &quot;<%= data.getTopicName() %>&quot;:</B></FONT>
<BR>
<FORM METHOD="POST" ACTION="<%= rdat.getEncodedServletPath("post") %>">
<INPUT TYPE="HIDDEN" NAME="sig" VALUE="<%= data.getSIGID() %>">
<INPUT TYPE="HIDDEN" NAME="conf" VALUE="<%= data.getConfID() %>">
<INPUT TYPE="HIDDEN" NAME="top" VALUE="<%= data.getTopicNumber() %>">
<% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %>
<INPUT TYPE="HIDDEN" NAME="next" VALUE="<%= data.getNextVal() %>">
<% } // end if %>
<INPUT TYPE="HIDDEN" NAME="sd" VALUE="<%= data.getTotalMessages() %>">
<TABLE BORDER=0 CELLPADDING=0>
<TR><TD ALIGN=LEFT COLSPAN=2>
<%= rdat.getStdFontTag(null,2) %>Your name/header:</FONT><BR>
<INPUT TYPE="TEXT" NAME="pseud" SIZE=37 MAXLENGTH=255 VALUE="<%= data.getPseud() %>">
<%= rdat.getStdFontTag(null,2) %><INPUT TYPE="CHECKBOX" NAME="attach"
VALUE="Y" <% if (data.attachChecked()) { %>CHECKED<% } %> > Attach a file</FONT>
</TD></TR>
<TR>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>Message:</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO" TARGET="_blank">HTML Guide</A>
</FONT></TD>
</TR>
<TR><TD ALIGN=LEFT COLSPAN=2>
<TEXTAREA NAME="pb" WRAP=SOFT ROWS=7 COLS=51><%= data.getBodyText() %></TEXTAREA>
</TD></TR>
<TR><TD ALIGN=CENTER COLSPAN=2>
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_preview.gif") %>" ALT="Preview" NAME="preview"
WIDTH=80 HEIGHT=24 BORDER=0>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_reload.gif") %>" ALT="Post & Reload"
NAME="post" WIDTH=80 HEIGHT=24 BORDER=0>
<% if (!(StringUtil.isStringEmpty(data.getNextVal()))) { %>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_post_go_next.gif") %>" ALT="Post & Go Next"
NAME="postnext" WIDTH=80 HEIGHT=24 BORDER=0>
<% } // end if %>
&nbsp;&nbsp;
<INPUT TYPE="IMAGE" SRC="<%= rdat.getFullImagePath("bn_cancel.gif") %>" ALT="Cancel"
NAME="cancel" WIDTH=80 HEIGHT=24 BORDER=0>
</TD></TR>
</TABLE>
</FORM>
<% rdat.writeFooter(out); %>

View File

@ -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()) { %><!-- Topic list for conf #<%= data.getConfID() %> --><% } %>
@ -36,14 +35,14 @@
SRC="<%= rdat.getFullImagePath("bn_conference_list.gif") %>" ALT="Conference List" WIDTH=80 HEIGHT=24
BORDER=0></A>&nbsp;&nbsp;
<% if (data.canCreateTopic()) { %>
<% tmp = "confops?sig=" + String.valueOf(data.getSIGID()) + "&conf="
+ String.valueOf(data.getConfID()) + "&cmd=T"; %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>"><IMG SRC="<%= rdat.getFullImagePath("bn_add_topic.gif") %>"
ALT="Add Topic" WIDTH=80 HEIGHT=24 BORDER=0></A>&nbsp;&nbsp;
<% tmp = rdat.getEncodedServletPath("confops?" + data.getLocator() + "&cmd=T"); %>
<A HREF="<%= tmp %>"><IMG SRC="<%= rdat.getFullImagePath("bn_add_topic.gif") %>" ALT="Add Topic"
WIDTH=80 HEIGHT=24 BORDER=0></A>&nbsp;&nbsp;
<% } // end if %>
<% if (data.canDoReadNew()) { %>
<A HREF="TODO"><IMG SRC="<%= rdat.getFullImagePath("bn_read_new.gif") %>"
ALT="Read New" WIDTH=80 HEIGHT=24 BORDER=0></A>&nbsp;&nbsp;
<A HREF="<%= rdat.getEncodedServletPath("confdisp?" + data.getNextLocator()) %>"><IMG
SRC="<%= rdat.getFullImagePath("bn_read_new.gif") %>" ALT="Read New" WIDTH=80 HEIGHT=24
BORDER=0></A>&nbsp;&nbsp;
<% } // end if %>
<A HREF="TODO"><IMG SRC="<%= rdat.getFullImagePath("bn_manage.gif") %>"
ALT="Manage" WIDTH=80 HEIGHT=24 BORDER=0></A>&nbsp;&nbsp;
@ -59,52 +58,61 @@
<% tmp = self + "&sort="
+ String.valueOf(data.isSort(ConferenceContext.SORT_NUMBER) ? -ConferenceContext.SORT_NUMBER
: ConferenceContext.SORT_NUMBER); %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>">#</A>
<B><A HREF="<%= rdat.getEncodedServletPath(tmp) %>">#</A></B>
</FONT></TD>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>
<% tmp = self + "&sort="
+ String.valueOf(data.isSort(ConferenceContext.SORT_NAME) ? -ConferenceContext.SORT_NAME
: ConferenceContext.SORT_NAME); %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Topic Name</A>
<B><A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Topic Name</A></B>
</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<% tmp = self + "&sort="
+ String.valueOf(data.isSort(ConferenceContext.SORT_UNREAD) ? -ConferenceContext.SORT_UNREAD
: ConferenceContext.SORT_UNREAD); %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>">New</A>
<B><A HREF="<%= rdat.getEncodedServletPath(tmp) %>">New</A></B>
</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<% tmp = self + "&sort="
+ String.valueOf(data.isSort(ConferenceContext.SORT_TOTAL) ? -ConferenceContext.SORT_TOTAL
: ConferenceContext.SORT_TOTAL); %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Total</A>
<B><A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Total</A></B>
</FONT></TD>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>
<% tmp = self + "&sort="
+ String.valueOf(data.isSort(ConferenceContext.SORT_DATE) ? -ConferenceContext.SORT_DATE
: ConferenceContext.SORT_DATE); %>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Last Response</A>
<B><A HREF="<%= rdat.getEncodedServletPath(tmp) %>">Last Response</A></B>
</FONT></TD>
</TR>
<TR VALIGN=TOP><TD ALIGN=LEFT COLSPAN=5><%= rdat.getStdFontTag(null,2) %>&nbsp;</FONT></TD></TR>
<% 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";
%>
<TR VALIGN=TOP>
<TD ALIGN=LEFT WIDTH="1%"><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO"><%= topic.getTopicNumber() %></A>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>"><%= topic.getTopicNumber() %></A>&nbsp;&nbsp;
</FONT></TD>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO"><%= topic.getName() %></A>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>"><%= topic.getName() %></A>
<% if (topic.isArchived() && !(data.isView(ConferenceContext.DISPLAY_ARCHIVED))) { %>
<EM>(archived)</EM>
<% } else if (topic.isFrozen()) { %>
<EM>(frozen)</EM>
<% } // end if %>
</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO"><%= topic.getUnreadMessages() %></A>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>"><%= topic.getUnreadMessages() %></A>
</FONT></TD>
<TD ALIGN=RIGHT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO"><%= topic.getTotalMessages() %></A>
<A HREF="<%= rdat.getEncodedServletPath(self + "&top=" + String.valueOf(topic.getTopicNumber())
+ "&p1=0&p2=-1") %>"><%= topic.getTotalMessages() %></A>
</FONT></TD>
<TD ALIGN=LEFT><%= rdat.getStdFontTag(null,2) %>
<A HREF="TODO"><%= rdat.formatDateForDisplay(topic.getLastUpdateDate()) %></A>
<A HREF="<%= rdat.getEncodedServletPath(tmp) %>"><%= rdat.formatDateForDisplay(topic.getLastUpdateDate()) %></A>
</FONT></TD>
</TR>
<% } // end while (more topics in enumeration) %>

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web/images/bn_go.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

BIN
web/images/bn_hide.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

BIN
web/images/bn_nuke.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web/images/bn_scribble.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

BIN
web/images/bn_show.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 B