diff --git a/INSTALL b/INSTALL index b7e47eb..630a867 100644 --- a/INSTALL +++ b/INSTALL @@ -4,9 +4,8 @@ INSTALLATION INSTRUCTIONS Software requirements: You must install the following software on your system: - * Java 2 SDK version 1.2.2. Venice is normally run with the Blackdown - JDK port, available at http://www.blackdown.org. Version 1.3.0 is - being tested, and will likely be required sometime in the future. + * Java 2 SDK version 1.3.0/1.3.1. Venice is normally run with the Blackdown + JDK port, available at http://www.blackdown.org. * Java API for XML Parsing (JAXP) version 1.0.1, available from http://java.sun.com. * Java Secure Socket Extension (JSSE) version 1.0.2, available from @@ -15,6 +14,8 @@ Software requirements: * JavaMail API version 1.2, available from http://java.sun.com. * JavaBeans Activation Framework (JAF) version 1.0.1, available from http://java.sun.com. (Required by JavaMail.) + * Java Advanced Imaging (JAI) version 1.1.1, available from + http://java.sun.com. * Apache Jakarta LOG4J library 1.1.x, available from http://jakarta.apache.org. * MySQL 3.23.x (get the most recent 3.23 version), available from diff --git a/TODO b/TODO index 17a863c..d93f3a8 100644 --- a/TODO +++ b/TODO @@ -47,10 +47,6 @@ Lots! - Unimplemented functions on the Top page: Customize Sideboxes -- Not everybody likes purple. Provide a way to change the default colors. - Probably via entries in render-config.xml. Of course, if we go to a - different rendering system eventually, we won't need this. - - Javadocs. Lots of javadocs. - Continue pressing on. "Failure is not an option." diff --git a/etc/web.xml b/etc/web.xml index 3c43bdc..fb352fe 100644 --- a/etc/web.xml +++ b/etc/web.xml @@ -206,6 +206,22 @@ com.silverwrist.venice.servlets.PostShortcut + + imageretrieve + + Retrieves images from the database image store and displays them. + + com.silverwrist.venice.servlets.ImageRetrieve + + + + userphoto + + Changes the photo in a user's profile (uploads a new one). + + com.silverwrist.venice.servlets.UserPhoto + + @@ -309,6 +325,16 @@ /go/* + + imageretrieve + /imagedata/* + + + + userphoto + /userphoto + + testformdata diff --git a/setup/database.sql b/setup/database.sql index 7def33c..4a9fb43 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -414,6 +414,16 @@ CREATE TABLE adverts ( linkurl VARCHAR(255) ); +# Storage space for uploaded images. +CREATE TABLE imagestore ( + imgid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + typecode SMALLINT DEFAULT 0, + ownerid INT, + mimetype VARCHAR(128) NOT NULL, + length INT NOT NULL, + data MEDIUMBLOB +); + ############################################################################## # Set table access rights ############################################################################## diff --git a/src/com/silverwrist/util/image/ImageLengthPair.java b/src/com/silverwrist/util/image/ImageLengthPair.java new file mode 100644 index 0000000..c2b9e28 --- /dev/null +++ b/src/com/silverwrist/util/image/ImageLengthPair.java @@ -0,0 +1,61 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.util.image; + +import java.io.InputStream; + +public final class ImageLengthPair +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private int length; + private InputStream data; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public ImageLengthPair(int length, InputStream data) + { + this.length = length; + this.data = data; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public final int getLength() + { + return length; + + } // end getLength + + public final InputStream getData() + { + return data; + + } // end getData + +} // end class ImageLengthPair diff --git a/src/com/silverwrist/util/image/ImageNormalizer.java b/src/com/silverwrist/util/image/ImageNormalizer.java new file mode 100644 index 0000000..f151d01 --- /dev/null +++ b/src/com/silverwrist/util/image/ImageNormalizer.java @@ -0,0 +1,153 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.util.image; + +import java.awt.Dimension; +import java.awt.image.*; +import java.awt.image.renderable.ParameterBlock; +import java.io.*; +import javax.media.jai.*; +import com.sun.media.jai.codec.*; + +public class ImageNormalizer +{ + /*-------------------------------------------------------------------------------- + * External static operations + *-------------------------------------------------------------------------------- + */ + + public static ImageLengthPair normalizeImage(InputStream raw_data, int width, int height, String out_format) + throws ImageNormalizerException + { + PlanarImage img1; + try + { // Start by loading the image we're working with. + SeekableStream istm = new ForwardSeekableStream(raw_data); + RenderedOp rop1 = JAI.create("stream",istm); + img1 = rop1.getRendering(); + + } // end try + catch (RuntimeException re) + { // unable to get the rendering here! + throw new ImageNormalizerException("Image data not a valid image format",re); + + } // end catch + + try + { // Compute the scaling factors required to get the image down to the appropriate size, then choose + // the smaller of the two to use as the final scaling factor. + Float scale_width = null, scale_height = null; + if (img1.getWidth()>width) + scale_width = new Float((float)width / (float)(img1.getWidth())); + if (img1.getHeight()>height) + scale_height = new Float((float)height / (float)(img1.getHeight())); + Float scale = null; + if (scale_width!=null) + { // we can scale by width, how about height? + if (scale_height!=null) + { // yes, height too...pick the smaller of the two + if (scale_width.floatValue(). + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.util.image; + +import java.io.PrintStream; +import java.io.PrintWriter; + +public class ImageNormalizerException extends Exception +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private Throwable inner = null; // internal "root cause" exception + + /*-------------------------------------------------------------------------------- + * Constructors + *-------------------------------------------------------------------------------- + */ + + public ImageNormalizerException() + { + super(); + + } // end constructor + + public ImageNormalizerException(String msg) + { + super(msg); + + } // end constructor + + public ImageNormalizerException(Throwable t) + { + super(t.getMessage()); + inner = t; + + } // end constructor + + public ImageNormalizerException(String msg, Throwable t) + { + super(msg); + inner = t; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Overrides from class Throwable + *-------------------------------------------------------------------------------- + */ + + public void printStackTrace() + { + this.printStackTrace(System.err); + + } // end printStackTrace + + public void printStackTrace(PrintStream s) + { + super.printStackTrace(s); + if (inner!=null) + { // print the inner stack trace + s.print("Root cause: "); + inner.printStackTrace(s); + + } // end if + + } // end printStackTrace + + public void printStackTrace(PrintWriter s) + { + super.printStackTrace(s); + if (inner!=null) + { // print the inner stack trace + s.print("Root cause: "); + inner.printStackTrace(s); + + } // end if + + } // end printStackTrace + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public Throwable getException() + { + return inner; + + } // end getException + +} // end class ImageNormalizerException diff --git a/src/com/silverwrist/venice/core/BinaryData.java b/src/com/silverwrist/venice/core/BinaryData.java new file mode 100644 index 0000000..6486798 --- /dev/null +++ b/src/com/silverwrist/venice/core/BinaryData.java @@ -0,0 +1,32 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core; + +import java.io.InputStream; + +public interface BinaryData +{ + public abstract String getMIMEType(); + + public abstract String getFilename(); + + public abstract int getLength(); + + public abstract InputStream getData() throws DataException; + +} // end interface BinaryData diff --git a/src/com/silverwrist/venice/core/ContactInfo.java b/src/com/silverwrist/venice/core/ContactInfo.java index a2a5003..0520917 100644 --- a/src/com/silverwrist/venice/core/ContactInfo.java +++ b/src/com/silverwrist/venice/core/ContactInfo.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core; +import java.io.InputStream; import java.util.Date; import java.sql.Connection; @@ -92,6 +93,8 @@ public interface ContactInfo public abstract void setPhotoURL(String addr); + public abstract void setPhotoData(String prefix, String mimetype, int length, InputStream data); + public abstract String getURL(); public abstract void setURL(String addr); diff --git a/src/com/silverwrist/venice/core/VeniceEngine.java b/src/com/silverwrist/venice/core/VeniceEngine.java index e92f7f9..a306510 100644 --- a/src/com/silverwrist/venice/core/VeniceEngine.java +++ b/src/com/silverwrist/venice/core/VeniceEngine.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core; +import java.awt.Dimension; import java.util.BitSet; import java.util.List; import org.w3c.dom.Document; @@ -81,4 +82,8 @@ public interface VeniceEngine extends SearchMode public abstract Advertisement selectAd(); + public abstract BinaryData loadImage(int id) throws DataException; + + public abstract Dimension getUserPhotoSize(); + } // end interface VeniceEngine diff --git a/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java b/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java index e029efa..86d46f2 100644 --- a/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java +++ b/src/com/silverwrist/venice/core/impl/ContactInfoImpl.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core.impl; +import java.io.*; import java.sql.*; import java.util.*; import org.apache.log4j.*; @@ -25,6 +26,74 @@ import com.silverwrist.venice.db.*; class ContactInfoImpl implements ContactInfo, Stashable { + /*-------------------------------------------------------------------------------- + * "Hook" classes used to store an image in the image store and modify the image + * URL prior to saving contact info + *-------------------------------------------------------------------------------- + */ + + abstract class ImageHook + { + protected String mimetype; + protected int length; + protected InputStream data; + + protected ImageHook(String mimetype, int length, InputStream data) + { + this.mimetype = mimetype; + this.length = length; + this.data = data; + + } // end constructor + + public abstract String doImage(Connection conn) throws SQLException; + + } // end class ImageHook + + class NewImageHook extends ImageHook + { + private String prefix; + private short type; + private int ownerid; + + public NewImageHook(String prefix, short type, int ownerid, String mimetype, int length, InputStream data) + { + super(mimetype,length,data); + this.prefix = prefix; + this.type = type; + this.ownerid = ownerid; + + } // end constructor + + public String doImage(Connection conn) throws SQLException + { + int id = ImageStore.storeNewImage(conn,type,ownerid,mimetype,length,data); + return prefix + id; + + } // end doImage + + } // end class NewImageHook + + class ExistingImageHook extends ImageHook + { + private int imgid; + + public ExistingImageHook(int imgid, String mimetype, int length, InputStream data) + { + super(mimetype,length,data); + this.imgid = imgid; + + } // end constructor + + public String doImage(Connection conn) throws SQLException + { + ImageStore.replaceImage(conn,imgid,mimetype,length,data); + return null; + + } // end doImage + + } // end class ExistingImageHook + /*-------------------------------------------------------------------------------- * Static data members *-------------------------------------------------------------------------------- @@ -64,6 +133,7 @@ class ContactInfoImpl implements ContactInfo, Stashable private int owner_sigid; // SIGID this contact record is in (-1 for none) private java.util.Date last_update; // date of last update private boolean is_modified = false; // have we modified this ContactInfo? + private ImageHook image_hook = null; // image hook object /*-------------------------------------------------------------------------------- * Constructors @@ -502,10 +572,32 @@ class ContactInfoImpl implements ContactInfo, Stashable photo_url = addr.substring(0,255); else photo_url = addr; + image_hook = null; is_modified = true; } // end setPhotoURL + public void setPhotoData(String prefix, String mimetype, int length, InputStream data) + { + if ((photo_url!=null) && (photo_url.startsWith(prefix))) + { // extract the image ID and create an image hook object + int img_id = Integer.parseInt(photo_url.substring(prefix.length())); + image_hook = new ExistingImageHook(img_id,mimetype,length,data); + + } // end if + else + { // figure out how to create the image hook object + if (owner_sigid>=0) + image_hook = new NewImageHook(prefix,ImageStore.TYPE_SIG_LOGO,owner_sigid,mimetype,length,data); + else + image_hook = new NewImageHook(prefix,ImageStore.TYPE_USER_PHOTO,owner_uid,mimetype,length,data); + + } // end else + + is_modified = true; + + } // end setPhotoData + public String getURL() { return url; @@ -617,6 +709,15 @@ class ContactInfoImpl implements ContactInfo, Stashable Statement stmt = conn.createStatement(); StringBuffer buf; + if (image_hook!=null) + { // call the image hook to store an image and get a new photo URL (where applicable) + String new_photo_url = image_hook.doImage(conn); + if (new_photo_url!=null) + photo_url = new_photo_url; + image_hook = null; + + } // end if + if (contactid>=0) { // this involves updating an existing record buf = new StringBuffer("UPDATE contacts SET given_name = "); diff --git a/src/com/silverwrist/venice/core/impl/ImageStore.java b/src/com/silverwrist/venice/core/impl/ImageStore.java new file mode 100644 index 0000000..7374549 --- /dev/null +++ b/src/com/silverwrist/venice/core/impl/ImageStore.java @@ -0,0 +1,296 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.core.impl; + +import java.io.*; +import java.sql.*; +import java.util.*; +import org.apache.log4j.*; +import com.silverwrist.venice.db.*; +import com.silverwrist.venice.core.*; + +class ImageStore implements BinaryData +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + public static final short TYPE_USER_PHOTO = 1; + public static final short TYPE_SIG_LOGO = 2; + + private static Category logger = Category.getInstance(ImageStore.class); + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private DataPool datapool; + private int imgid; + private String type; + private int length; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + protected ImageStore(DataPool datapool, int imgid, String type, int length) + { + this.datapool = datapool; + this.imgid = imgid; + this.type = type; + this.length = length; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Implementations from interface BinaryData + *-------------------------------------------------------------------------------- + */ + + public String getMIMEType() + { + return type; + + } // end getMIMEType + + public String getFilename() + { + return null; + + } // end getFilename + + public int getLength() + { + return length; + + } // end getLength + + public InputStream getData() throws DataException + { + Connection conn = null; + InputStream rc = null; + + try + { // open up a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + // Create the SQL we need to retrieve the image. + StringBuffer sql = new StringBuffer("SELECT data FROM imagestore WHERE imgid = "); + sql.append(imgid).append(';'); + + // Execute the query! + ResultSet rs = stmt.executeQuery(sql.toString()); + if (!(rs.next())) + { // there is no attachment data! + logger.error("no image data to get"); + throw new DataException("There is no image data present."); + + } // end if + + // Since the InputStream we get from JDBC will probably go away when the connection is dropped, we + // need to make a temporary copy of it, to a ByteArrayOutputStream. + InputStream sqldata = rs.getBinaryStream(1); + ByteArrayOutputStream copy = new ByteArrayOutputStream(length); + byte[] buffer = new byte[4096]; + int rd = sqldata.read(buffer); + while (rd>=0) + { // write, then read again + if (rd>0) + copy.write(buffer,0,rd); + rd = sqldata.read(buffer); + + } // end while + + // Close both our streams, making sure we create the stream we need for the return value. + sqldata.close(); + rc = new ByteArrayInputStream(copy.toByteArray()); + copy.close(); + + } // end try + catch (SQLException e) + { // turn this into a DataException + logger.error("DB error retrieving image: " + e.getMessage(),e); + throw new DataException("unable to retrieve image data: " + e.getMessage(),e); + + } // end catch + catch (IOException e) + { // turn this into a DataException too + logger.error("I/O error copying image data: " + e.getMessage(),e); + throw new DataException("unable to retrieve image data: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + return rc; + + } // end getData + + /*-------------------------------------------------------------------------------- + * Static operations + *-------------------------------------------------------------------------------- + */ + + static ImageStore loadImageByID(DataPool datapool, int id) throws DataException + { + if (logger.isDebugEnabled()) + logger.debug("loadImageByID # " + id); + + Connection conn = null; // pooled database connection + + try + { // get a database connection + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + + StringBuffer sql = new StringBuffer("SELECT mimetype, length FROM imagestore WHERE imgid = "); + sql.append(id).append(';'); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + ResultSet rs = stmt.executeQuery(sql.toString()); + + if (rs.next()) // create an object reference and return it + return new ImageStore(datapool,id,rs.getString(1),rs.getInt(2)); + + return null; // no such image + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error reading image entry: " + e.getMessage(),e); + throw new DataException("unable to retrieve image entry: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end loadImageByID + + static int storeNewImage(Connection conn, short type, int owner, String mime, int length, InputStream data) + throws SQLException + { + // Create the SQL statement that inserts the image into the store. + StringBuffer sql = + new StringBuffer("INSERT INTO imagestore (typecode, ownerid, mimetype, length, data) VALUES ("); + sql.append(type).append(", ").append(owner).append(", '").append(mime).append("', ").append(length); + sql.append(", ?);"); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + + // Prepare the statement, set the BLOB parameter, and execute it. + PreparedStatement stmt = conn.prepareStatement(sql.toString()); + stmt.setBinaryStream(1,data,length); + stmt.executeUpdate(); + + // Get the ID of the new image and return it. + Statement stmt2 = conn.createStatement(); + ResultSet rs = stmt2.executeQuery("SELECT LAST_INSERT_ID();"); + if (!(rs.next())) + throw new InternalStateError("storeNewImage(): Unable to get new image ID!"); + return rs.getInt(1); + + } // end storeNewImage + + static int storeNewImage(DataPool datapool, short type, int owner, String mime, int length, InputStream data) + throws DataException + { + if (logger.isDebugEnabled()) + logger.debug("storeNewImage: type " + type + ", owner " + owner + ", mime " + mime + ", len " + length); + + Connection conn = null; // pooled database connection + + try + { // get a database connection + conn = datapool.getConnection(); + return storeNewImage(conn,type,owner,mime,length,data); + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error storing image entry: " + e.getMessage(),e); + throw new DataException("unable to store image entry: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end storeNewImage + + static void replaceImage(Connection conn, int imgid, String mime, int length, InputStream data) + throws SQLException + { + // Create the SQL statement that inserts the image into the store. + StringBuffer sql = new StringBuffer("UPDATE imagestore SET mimetype = '"); + sql.append(mime).append("', length = ").append(length).append(", data = ? WHERE imgid = ").append(imgid); + sql.append(';'); + if (logger.isDebugEnabled()) + logger.debug("SQL: " + sql.toString()); + + // Prepare the statement, set the BLOB parameter, and execute it. + PreparedStatement stmt = conn.prepareStatement(sql.toString()); + stmt.setBinaryStream(1,data,length); + stmt.executeUpdate(); + + } // end replaceImage + + static void replaceImage(DataPool datapool, int imgid, String mime, int length, InputStream data) + throws DataException + { + if (logger.isDebugEnabled()) + logger.debug("replaceImage: imgid " + imgid + ", mime " + mime + ", len " + length); + + Connection conn = null; // pooled database connection + + try + { // get a database connection + conn = datapool.getConnection(); + replaceImage(conn,imgid,mime,length,data); + + } // end try + catch (SQLException e) + { // turn SQLException into data exception + logger.error("DB error storing image entry: " + e.getMessage(),e); + throw new DataException("unable to store image entry: " + e.getMessage(),e); + + } // end catch + finally + { // make sure we release the connection before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + } // end replaceImage + +} // end class ImageStore diff --git a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java index 262794c..8298c3c 100644 --- a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java +++ b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java @@ -17,6 +17,7 @@ */ package com.silverwrist.venice.core.impl; +import java.awt.Dimension; import java.sql.*; import java.util.*; import org.apache.log4j.*; @@ -388,6 +389,8 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"; private static final int AUTH_STRING_LEN = 32; + private static final Dimension DEFAULT_DIM_USERPHOTO = new Dimension(100,100); + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- @@ -1580,6 +1583,18 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end selectAd + public BinaryData loadImage(int id) throws DataException + { + return ImageStore.loadImageByID(datapool,id); + + } // end loadImage + + public Dimension getUserPhotoSize() + { + return DEFAULT_DIM_USERPHOTO; + + } // end getUserPhotoSize + /*-------------------------------------------------------------------------------- * Implementations from interface EngineBackend *-------------------------------------------------------------------------------- diff --git a/src/com/silverwrist/venice/servlets/ImageRetrieve.java b/src/com/silverwrist/venice/servlets/ImageRetrieve.java new file mode 100644 index 0000000..5334e1e --- /dev/null +++ b/src/com/silverwrist/venice/servlets/ImageRetrieve.java @@ -0,0 +1,98 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets; + +import java.io.*; +import java.util.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.log4j.*; +import com.silverwrist.venice.core.*; +import com.silverwrist.venice.servlets.format.*; + +public class ImageRetrieve extends VeniceServlet +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(ImageRetrieve.class); + + /*-------------------------------------------------------------------------------- + * Overrides from class HttpServlet + *-------------------------------------------------------------------------------- + */ + + public String getServletInfo() + { + String rc = "ImageRetrieve servlet - Displays images from the database image store\n" + + "Part of the Venice Web Communities System\n"; + return rc; + + } // end getServletInfo + + /*-------------------------------------------------------------------------------- + * Overrides from class VeniceServlet + *-------------------------------------------------------------------------------- + */ + + protected VeniceContent doVeniceGet(HttpServletRequest request, VeniceEngine engine, + UserContext user, RenderData rdat) + throws ServletException, IOException, VeniceServletResult + { + int imgid; // image ID to retrieve. + + try + { // parse the image ID + imgid = Integer.parseInt(request.getPathInfo().substring(1)); + + } // end try + catch (NumberFormatException nfe) + { // invalid image URL + return new ErrorBox(null,"Invalid image URL.",null); + + } // end catch + + // the parameters of the response + String type, filename; + int length; + InputStream data; + + try + { // load the image from the database + BinaryData image = engine.loadImage(imgid); + + type = image.getMIMEType(); + filename = image.getFilename(); + length = image.getLength(); + data = image.getData(); + + } // end try + catch (DataException de) + { // unable to get the user name + return new ErrorBox("Database Error","Database error retrieving image: " + de.getMessage(),null); + + } // end catch + + // now we want to send that data back to the user! + throw new SendFileResult(type,filename,length,data); + + } // end doVeniceGet + +} // end class ImageRetrieve diff --git a/src/com/silverwrist/venice/servlets/UserDisplay.java b/src/com/silverwrist/venice/servlets/UserDisplay.java index fc2fc19..a4972be 100644 --- a/src/com/silverwrist/venice/servlets/UserDisplay.java +++ b/src/com/silverwrist/venice/servlets/UserDisplay.java @@ -62,7 +62,7 @@ public class UserDisplay extends VeniceServlet UserProfile prof = user.getProfile(uname); changeMenuTop(request); setMyLocation(request,"user" + request.getPathInfo()); - return new UserProfileData(prof); + return new UserProfileData(engine,prof); } // end try catch (DataException de) @@ -134,7 +134,7 @@ public class UserDisplay extends VeniceServlet logger.debug("redisplaying profile window"); changeMenuTop(request); setMyLocation(request,"user" + request.getPathInfo()); - return new UserProfileData(prof); + return new UserProfileData(engine,prof); } // end doVenicePost diff --git a/src/com/silverwrist/venice/servlets/UserPhoto.java b/src/com/silverwrist/venice/servlets/UserPhoto.java new file mode 100644 index 0000000..697a7d3 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/UserPhoto.java @@ -0,0 +1,173 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets; + +import java.io.*; +import java.net.URLEncoder; +import java.util.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.log4j.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.util.ServletMultipartHandler; +import com.silverwrist.util.ServletMultipartException; +import com.silverwrist.util.image.*; +import com.silverwrist.venice.core.*; +import com.silverwrist.venice.servlets.format.*; + +public class UserPhoto extends VeniceServlet +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(UserPhoto.class); + + /*-------------------------------------------------------------------------------- + * Overrides from class HttpServlet + *-------------------------------------------------------------------------------- + */ + + public String getServletInfo() + { + String rc = "UserPhoto servlet - changes the user photo for a user\n" + + "Part of the Venice Web Communities System\n"; + return rc; + + } // end getServletInfo + + /*-------------------------------------------------------------------------------- + * Overrides from class VeniceServlet + *-------------------------------------------------------------------------------- + */ + + protected VeniceContent doVeniceGet(HttpServletRequest request, VeniceEngine engine, + UserContext user, RenderData rdat) + throws ServletException, IOException, VeniceServletResult + { + String tgt = request.getParameter("tgt"); // target location + if (tgt==null) + tgt = "top"; // go back to the Top screen if nothing else + + try + { // create the display + return new UserPhotoData(engine,user,rdat,tgt); + + } // end try + catch (DataException de) + { // error getting at the data stream from the attachment + return new ErrorBox("Database Error","Database error generating display: " + de.getMessage(),null); + + } // end catch + + } // end doVeniceGet + + protected VeniceContent doVenicePost(HttpServletRequest request, ServletMultipartHandler mphandler, + VeniceEngine engine, UserContext user, RenderData rdat) + throws ServletException, IOException, VeniceServletResult + { + // Get target URL for the operation + if (mphandler.isFileParam("tgt")) + { // bogus target URL + logger.error("Internal Error: 'tgt' should be a normal param"); + return new ErrorBox(null,"Internal Error: 'tgt' should be a normal param","top"); + + } // end if + + String tgt = mphandler.getValue("tgt"); + if (tgt==null) + tgt = "top"; // go back to the Top screen if nothing else + + if (isImageButtonClicked(mphandler,"cancel")) + throw new RedirectResult("account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + if (isImageButtonClicked(mphandler,"upload")) + { // uploading the image here! + // also check on file parameter status + if (!(mphandler.isFileParam("thepic"))) + { // bogus file parameter + logger.error("Internal Error: 'thepic' should be a file param"); + return new ErrorBox(null,"Internal Error: 'thepic' should be a file param", + "account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end if + + if (!(mphandler.getContentType("thepic").startsWith("image/"))) + { // must be an image type we uploaded! + logger.error("Error: 'thepic' not an image type"); + return new ErrorBox(null,"You did not upload an image file. Try again.", + "userphoto?tgt=" + URLEncoder.encode(tgt)); + + } // end if + + try + { // get the real picture (normalized to 100x100 size) + ImageLengthPair real_pic = ImageNormalizer.normalizeImage(mphandler.getFileContentStream("thepic"), + engine.getUserPhotoSize(),"jpeg"); + + // set the user photo data! + ContactInfo ci = user.getContactInfo(); + ci.setPhotoData(request.getContextPath() + "/imagedata/","image/jpeg",real_pic.getLength(), + real_pic.getData()); + user.putContactInfo(ci); + + // Jump back to the profile form. + throw new RedirectResult("account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end try + catch (ServletMultipartException smpe) + { // the servlet multipart parser screwed up + logger.error("Servlet multipart error:",smpe); + return new ErrorBox(null,"Internal Error: " + smpe.getMessage(), + "account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end catch + catch (ImageNormalizerException ine) + { // the image was not valid + logger.error("Image normalizer error:",ine); + return new ErrorBox(null,ine.getMessage(),"userphoto?tgt=" + URLEncoder.encode(tgt)); + + } // end catch + catch (DataException de) + { // error in the database! + logger.error("DataException:",de); + return new ErrorBox("Database Error","Database error storing user photo: " + de.getMessage(), + "account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end catch + catch (EmailException ee) + { // email exception (WTF?) + logger.error("Email exception (shouldn't happen):",ee); + return new ErrorBox(null,"Internal Error: " + ee.getMessage(), + "account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end catch + + } // end if + else + { // the button must be wrong! + logger.error("no known button click on UserPhoto.doPost"); + return new ErrorBox("Internal Error","Unknown command button pressed", + "account?cmd=P&tgt=" + URLEncoder.encode(tgt)); + + } // end else + + } // end doVenicePost + +} // end class UserPhoto diff --git a/src/com/silverwrist/venice/servlets/VeniceServlet.java b/src/com/silverwrist/venice/servlets/VeniceServlet.java index 7775ed4..fd5b9a3 100644 --- a/src/com/silverwrist/venice/servlets/VeniceServlet.java +++ b/src/com/silverwrist/venice/servlets/VeniceServlet.java @@ -292,6 +292,13 @@ public abstract class VeniceServlet extends HttpServlet } // end isImageButtonClicked + protected static final boolean isImageButtonClicked(ServletMultipartHandler mphandler, String name) + { + String val = mphandler.getValue(name + ".x"); + return (val!=null); + + } // end isImageButtonClicked + protected final void putUserContext(HttpServletRequest request, UserContext ctxt) { Variables.putUserContext(request.getSession(true),ctxt); diff --git a/src/com/silverwrist/venice/servlets/format/EditProfileDialog.java b/src/com/silverwrist/venice/servlets/format/EditProfileDialog.java index 19055d6..a3d4c19 100644 --- a/src/com/silverwrist/venice/servlets/format/EditProfileDialog.java +++ b/src/com/silverwrist/venice/servlets/format/EditProfileDialog.java @@ -17,6 +17,8 @@ */ package com.silverwrist.venice.servlets.format; +import java.io.*; +import java.net.URLEncoder; import java.util.*; import com.silverwrist.util.LocaleFactory; import com.silverwrist.util.StringUtil; @@ -25,6 +27,66 @@ import com.silverwrist.venice.core.*; public class EditProfileDialog extends ContentDialog { + /*-------------------------------------------------------------------------------- + * The photo URL control class. + *-------------------------------------------------------------------------------- + */ + + static class CDUserPhotoControl extends CDBaseFormField + { + private String linkURL; + + public CDUserPhotoControl(String name, String caption, String linkURL) + { + super(name,caption,"(click to change)",false); + + } // end constructor + + protected CDUserPhotoControl(CDUserPhotoControl other) + { + super(other); + this.linkURL = other.linkURL; + + } // end constructor + + protected void renderActualField(Writer out, RenderData rdat) throws IOException + { + if (isEnabled()) + out.write(""); + String photo = getValue(); + if (StringUtil.isStringEmpty(photo)) + photo = rdat.getFullImagePath("photo_not_avail.gif"); + out.write("\"\""); + if (isEnabled()) + out.write(""); + + } // end renderActualField + + protected void validateContents(String value) throws ValidationException + { // this is a do-nothing value + } // end validateContents + + public CDFormField duplicate() + { + return new CDUserPhotoControl(this); + + } // end clone + + public void setLinkURL(String s) + { + linkURL = s; + + } // end setLinkURL + + } // end class CDUserPhotoControl + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private CDUserPhotoControl photo_control; + /*-------------------------------------------------------------------------------- * Constructors *-------------------------------------------------------------------------------- @@ -67,7 +129,8 @@ public class EditProfileDialog extends ContentDialog addFormField(new CDTextFormField("url","Home page","(URL)",false,32,255)); addFormField(new CDFormCategoryHeader("Personal")); addFormField(new CDTextFormField("descr","Personal description",null,false,32,255)); - // TODO: add photo selection/uploading method here + photo_control = new CDUserPhotoControl("photo","User Photo","userphoto"); + addFormField(photo_control); addFormField(new CDFormCategoryHeader("User Preferences")); addFormField(new CDLocaleListFormField("locale","Default locale","(for formatting dates/times)",true)); addFormField(new CDTimeZoneListFormField("tz","Default time zone",null,true)); @@ -79,6 +142,7 @@ public class EditProfileDialog extends ContentDialog protected EditProfileDialog(EditProfileDialog other) { super(other); + photo_control = (CDUserPhotoControl)modifyField("photo"); } // end constructor @@ -162,6 +226,8 @@ public class EditProfileDialog extends ContentDialog setFieldValue("pvt_email","Y"); setFieldValue("url",ci.getURL()); setFieldValue("descr",uc.getDescription()); + setFieldValue("photo",ci.getPhotoURL()); + photo_control.setLinkURL("userphoto?tgt=" + URLEncoder.encode(target)); setFieldValue("locale",uc.getLocale().toString()); setFieldValue("tz",uc.getTimeZone().getID()); diff --git a/src/com/silverwrist/venice/servlets/format/UserPhotoData.java b/src/com/silverwrist/venice/servlets/format/UserPhotoData.java new file mode 100644 index 0000000..6582530 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/UserPhotoData.java @@ -0,0 +1,126 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets.format; + +import java.awt.Dimension; +import javax.servlet.*; +import javax.servlet.http.*; +import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.core.*; + +public class UserPhotoData implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.UserPhotoData"; + + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private Dimension photo_dims; + private String photo_url; + private String target; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public UserPhotoData(VeniceEngine engine, UserContext user, RenderData rdat, String target) + throws DataException + { + photo_dims = engine.getUserPhotoSize(); + photo_url = user.getContactInfo().getPhotoURL(); + if (StringUtil.isStringEmpty(photo_url)) + photo_url = rdat.getFullImagePath("photo_not_avail.gif"); + this.target = target; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static UserPhotoData retrieve(ServletRequest request) + { + return (UserPhotoData)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface VeniceContent + *-------------------------------------------------------------------------------- + */ + + public String getPageTitle(RenderData rdat) + { + return "Set User Photo"; + + } // end getPageTitle + + public String getPageQID() + { + return null; + + } // end getPageQID + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "user_photo.jsp"; + + } // end getTargetJSPName + + /*-------------------------------------------------------------------------------- + * External operations + *-------------------------------------------------------------------------------- + */ + + public String getTarget() + { + return target; + + } // end getTarget + + public String getPhotoTag(RenderData rdat) + { + StringBuffer buf = new StringBuffer("\"\""); + return buf.toString(); + + } // end getPhotoTag + +} // end class UserPhotoData diff --git a/src/com/silverwrist/venice/servlets/format/UserProfileData.java b/src/com/silverwrist/venice/servlets/format/UserProfileData.java index e1e5c29..944054c 100644 --- a/src/com/silverwrist/venice/servlets/format/UserProfileData.java +++ b/src/com/silverwrist/venice/servlets/format/UserProfileData.java @@ -17,8 +17,10 @@ */ package com.silverwrist.venice.servlets.format; +import java.awt.Dimension; import javax.servlet.ServletRequest; import com.silverwrist.util.StringUtil; +import com.silverwrist.venice.core.VeniceEngine; import com.silverwrist.venice.core.UserProfile; public class UserProfileData implements JSPRender @@ -36,6 +38,7 @@ public class UserProfileData implements JSPRender *-------------------------------------------------------------------------------- */ + private VeniceEngine engine; private UserProfile prof; /*-------------------------------------------------------------------------------- @@ -43,8 +46,9 @@ public class UserProfileData implements JSPRender *-------------------------------------------------------------------------------- */ - public UserProfileData(UserProfile prof) + public UserProfileData(VeniceEngine engine, UserProfile prof) { + this.engine = engine; this.prof = prof; } // end constructor @@ -143,13 +147,17 @@ public class UserProfileData implements JSPRender } // end getFullName - public String getPhotoURL(RenderData rdat) + public String getPhotoTag(RenderData rdat) { + Dimension dim = engine.getUserPhotoSize(); + StringBuffer buf = new StringBuffer("\"\""); + return buf.toString(); - } // end getPhotoURL + } // end getPhotoTag } // end class UserProfileData diff --git a/web/format/user_photo.jsp b/web/format/user_photo.jsp new file mode 100644 index 0000000..63ec82e --- /dev/null +++ b/web/format/user_photo.jsp @@ -0,0 +1,42 @@ +<%-- + The contents of this file are subject to the Mozilla Public License Version 1.1 + (the "License"); you may not use this file except in compliance with the License. + You may obtain a copy of the License at . + + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + WARRANTY OF ANY KIND, either express or implied. See the License for the specific + language governing rights and limitations under the License. + + The Original Code is the Venice Web Communities System. + + The Initial Developer of the Original Code is Eric J. Bowersox , + for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + + Contributor(s): +--%> +<%@ page import = "java.util.*" %> +<%@ page import = "com.silverwrist.util.StringUtil" %> +<%@ page import = "com.silverwrist.venice.core.*" %> +<%@ page import = "com.silverwrist.venice.servlets.Variables" %> +<%@ page import = "com.silverwrist.venice.servlets.format.*" %> +<% + UserPhotoData data = UserPhotoData.retrieve(request); + Variables.failIfNull(data); + RenderData rdat = RenderConfig.createRenderData(application,request,response); +%> +<% rdat.writeContentHeader(out,"Change User Photo",null); %> +<%= rdat.getStdFontTag(ColorSelectors.CONTENT_FOREGROUND,2) %> +
"> + + <%= data.getPhotoTag(rdat) %> + New user photo:
+

+ " NAME="upload" ALT="Upload" + WIDTH=80 HEIGHT=24 BORDER=0>  + " NAME="cancel" ALT="Cancel" + WIDTH=80 HEIGHT=24 BORDER=0>
+

+ + + diff --git a/web/format/userprofile.jsp b/web/format/userprofile.jsp index db49081..86fcf2f 100644 --- a/web/format/userprofile.jsp +++ b/web/format/userprofile.jsp @@ -32,8 +32,7 @@
<%= rdat.getStdFontTag(ColorSelectors.CONTENT_FOREGROUND,1) %> -

+ <%= data.getPhotoTag(rdat) %>

<% Date tmpd = prof.getCreateDate(); %> <% if (tmpd!=null) { %>