/* ======================================================================
The Bodington System Software License, Version 1.0
  
Copyright (c) 2001 The University of Leeds.  All rights reserved.
  
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES 
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO 
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
=========================================================

This software was originally created by the University of Leeds and may contain voluntary 
contributions from others.  For more information on the Bodington Open Source Project, please 
see http://bodington.org/

====================================================================== */

package org.bodington.servlet.facilities;

import org.apache.log4j.Logger;

import java.rmi.RemoteException;


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;


import org.bodington.servlet.*;
import org.bodington.database.PrimaryKey;
import org.bodington.server.*;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.UploadedFile;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.server.events.*;
import org.bodington.server.realm.Acl;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.util.DateFormatter;
import org.bodington.assessment.*;

public class PigeonHoleFacility extends Facility
	{
    
    private static Logger log = Logger.getLogger(PigeonHoleFacility.class);
	

	static final BigDecimal max_weight = new BigDecimal( "100.0" );
	
	/**
     * Facility subclasses can refuse copying of resources.
     * @return True if resources that use this facility can be copied.
     */
    public boolean canCopy( Resource resource )
    {
        return true;
    }
    
	public Resource newResource()
		{
		return new PigeonHole();
		}
	
	
	public boolean initResource( Request breq, Resource new_resource )
		throws Exception
		{
		if ( !(new_resource instanceof PigeonHole) )
			throw new Exception( "Technical problem: An incorrect type of resource was created." );
		
		PigeonHole ph;
		ph = (PigeonHole)new_resource;

		String param, strdate;
		java.util.Date date;
		java.sql.Timestamp sqldate=null;

		param=breq.getParameter( "maxmark" );
		ph.setMaximumMark( Integer.parseInt( param ) );


		strdate=breq.getParameter( "opening_date" );
		ph.setDateOpen( null );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					throw new Exception( "The opening date couldn't be understood." );
				else
					ph.setDateOpen( new java.sql.Timestamp( date.getTime() ) );
				}
			}
		strdate=breq.getParameter( "deadline" );
		ph.setDateDeadline( null );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					throw new Exception( "The deadline date couldn't be understood." );
				else
					ph.setDateDeadline( new java.sql.Timestamp( date.getTime() ) );
				}
			}
		strdate=breq.getParameter( "penalty" );
		ph.setDatePenalty( null );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					throw new Exception( "The penalty period date couldn't be understood." );
				else
					ph.setDatePenalty( new java.sql.Timestamp( date.getTime() ) );
				}
			}
		
		return true;  
		}
		

    /**
     * Use the properties of an existing Resource to initialise a newly created Resource. <p>
     * <i>(WebLearn modification (method added): 02/12/2003 Colin Tatham)</i>
     * @param original_resource The resource with properties to copy.
     * @param new_resource The resource to initialise.
     * @return True if initialisation was OK.
     * @exception BuildingServerException Thrown if there is any problem initialising the resource.
     *
     */
	public boolean initResource( Resource original_resource, Resource new_resource )
		throws BuildingServerException
         	{
	         if ( !(new_resource instanceof PigeonHole) )
		   throw new BuildingServerException( "Error: The wrong type of resource was created." );

		 PigeonHole original = (PigeonHole)original_resource;
		 PigeonHole new_pigeonhole = (PigeonHole)new_resource;

		 new_pigeonhole.setDateOpen(original.getDateOpen());
		 new_pigeonhole.setDateDeadline(original.getDateDeadline());
		 new_pigeonhole.setDatePenalty(original.getDatePenalty());
		 new_pigeonhole.setMaximumMark(original.getMaximumMark());

		 return true;
		}
	
	/**
	 * Setup the PigeonHole resource to a sensible state. We add Record, Mark 
	 * and Review Permissions to the owner for this.
	 */
	public boolean create(Request request, Connection connection,
        Resource resource)
    {
	    Permission grant[] = {Permission.RECORD, Permission.MARK, Permission.REVIEW};
        return Utils.grantPermissions(resource, grant);
    }


    /**
     * Copy the content of an existing Resource to another Resource. 
     * Content may be authored content or user data, and varies according to the resource type:<br />
     * Pigeon Hole<br />
     * authored : questions<br />
     * userdata : entries, responses (Not implemented.) <p>
     * <i>(WebLearn modification (method added): 02/12/2003 Colin Tatham)</i>
     * @param original_resource The resource with content to copy.
     * @param new_resource The resource to copy content to.
     * @param breq The building HTTP request, which is used to determine which type of content to copy.
     * @exception BuildingServerException Thrown if there is any problem copying the resource content.
     *
     */
        public void copyContent( Resource original_resource, Resource new_resource, Request breq )
                throws BuildingServerException
	        {
//	  authored : questions
//	  userdata : entries, responses? /** @todo  */

		 BuildingSession session = BuildingSessionManagerImpl.getSession( original_resource );
		 if ( !(session instanceof PigeonHoleSession) )
		   throw new BuildingServerException( "Unable to access appropriate resource session." );

		 try
		 {
		   if ( breq.getParameter( "authored" ) != null )
		   {
		     copyAuthoredContent( original_resource, new_resource );
//		     if ( breq.getParameter( "userdata" ) != null )
//                       copyUserContent( original_resource, new_resource );
		   }
		 }
		 catch (Exception ex)
		 {
		   throw new BuildingServerException( ex.getMessage() );
		 }
	}


    /**
     * Copy the authored content of an existing Resource to another Resource. <p>
     * <i>(WebLearn modification (method added): 02/12/2003 Colin Tatham)</i>
     * @param original_resource The resource with content to copy.
     * @param new_resource The resource to copy content to.
     * @exception BuildingServerException Thrown if there is any problem copying the resource content.
     * @exception RemoteException Thrown if there is any problem copying the resource content.
     *
     */
	private void copyAuthoredContent( Resource original_resource, Resource new_resource )
		throws BuildingServerException, RemoteException
		{
		 PigeonHoleSession session;
		 Vector questions;
		 PigeonHoleQuestion orig_question, new_question;

		 session = (PigeonHoleSession)BuildingSessionManagerImpl.getSession( original_resource );
		 questions = session.getPigeonHoleQuestionsInOrder();

		 for (int i=0; i<questions.size(); i++ )
		 {
		   orig_question = (PigeonHoleQuestion)questions.elementAt( i );
		   new_question = new PigeonHoleQuestion();
		   new_question.setResourceId( new_resource.getResourceId() );
		   new_question.setAvailable( orig_question.getAvailable() );
		   new_question.setNotes( orig_question.getNotes() );
		   new_question.setWeight( orig_question.getWeight() );
		   new_question.setOrdinal( orig_question.getOrdinal() );

		   new_question.save();
		 }
	}


	public boolean createCheck( Request breq, PrintWriter out )
		{
//		try
//			{
			int n;
			String param, strdate;
			java.util.Date now = new java.util.Date();
			java.util.Date[] date = new java.util.Date[3];
			String p[] = { "opening_date", "deadline", "penalty" };

			for ( int i=0; i<3; i++ )
				{
				strdate=breq.getParameter( p[i] );
				if ( strdate!=null )
					{
					strdate=strdate.trim();
					if ( strdate.length()>0 )
						{
						date[i]=DateParser.parse( strdate );
						}
					}
				if ( date[i]==null )
					{
					out.println( "You must enter valid dates in all three date fields." );
					return false;
					}
				}

			if ( date[0].after( date[1] ) || date[1].after( date[2] ) )
				{
				out.println( "The opening date cannot be after the deadline date and the deadline date cannot be after the penalty date." );
				return false;
				}
			
			if ( date[1].before( now ) || date[1].getYear() > (now.getYear()+100) )
				{
				out.println( "The deadline date must be in the future but not in the extreme distant future." );
				return false;
				}
			if ( date[2].before( now ) || date[2].getYear() > (now.getYear()+100) )
				{
				out.println( "The penalty date must be in the future but not in the extreme distant future." );
				return false;
				}

			param=breq.getParameter( "maxmark" );
			try
				{
				n = Integer.parseInt( param );
				}
			catch ( NumberFormatException nfex )
				{
				out.println( "You must enter a valid whole number in the maximum mark field." );
				return false;
				}
			if ( n<1 || n>100 )
				{
				out.println( "The maximum mark must be between 1 and 100." );
				return false;
				}
//			}
//		catch ( IOException ioex )
//			{
//			return false;
//			}
		return true;  
		}


	

	public void insert( Request req, PrintWriter out, String command, String insertname )
		throws ServletException, IOException
		{
		log.debug( Thread.currentThread().getName() + " PigeonHoleFacility insert()" );

		try
			{
			BuildingSession session;
			PigeonHoleSession ph_session;

			session = BuildingSessionManagerImpl.getSession( req.getResource() );
			if ( !(session instanceof PigeonHoleSession) )
		    	{
		    	out.println( "<HR>Technical problem: unable to access appropriate tool session.<HR>" );
		    	return;
		    	}
			ph_session = (PigeonHoleSession)session;
			

			if ( command.equalsIgnoreCase( "upload" ) )
				{
				upload( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrylist" ) )
				{
				entrylist( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrystatuslist" ) )
				{
				entrystatuslist( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "entrystatusconfirm" ) )
				{
				entrystatusconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markpaper" ) )
				{
				markpaper( req, out, ph_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "markitemedit" ) )
				{
				markitemedit( req, out, ph_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "newq" ) )
				{
				newq( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markdeleteq" ) )
				{
				markdeleteq( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "markeditqconfirm" ) )
				{
				markeditqconfirm( req, out, ph_session );
				return;
				}
	/*
			if ( command.equalsIgnoreCase( "remarkconfirm" ) )
				{
				remark( req, out, ph_session );
				return;
				}
	*/

			if ( command.equalsIgnoreCase( "markpaperconfirm" ) )
				{
				markpaperconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "adjustedmarkconfirm" ) )
				{
				adjustedmarkconfirm( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "overwrite" ) )
				{
				overwrite( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "reviewlink" ) )
				{
				reviewlink( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "reviewmark" ) )
				{
				reviewmark( req, out, ph_session );
				return;
				}

			if ( command.equalsIgnoreCase( "noupmessage" ) )
				{
				noupmessage( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifcanup" ) )
				{
				ifcanup( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifcanseemark" ) )
				{
				ifcanseemark( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "ifupentry" ) )
				{
				ifentry( req, out, ph_session );
				return;
				}
				
	//		if ( command.equalsIgnoreCase( "eventlog" ) )
	//			{
	//			eventlog( req, out, ph_session );
	//			return;
	//			}
				
			if ( command.equalsIgnoreCase( "pigeonmodify" ) )
				{
				pigeonmodify( req, out, ph_session );
				return;
				}
				
			if ( command.equalsIgnoreCase( "uplink" ) )
				{
				uplink( req, out, ph_session, insertname );
				return;
				}
				
			if ( command.equalsIgnoreCase( "pigeonfield" ) )
				{
				pigeonfield( req, out, ph_session, insertname );
				return;
				}
			}		
		catch ( BuildingServerException bsex )
			{
			out.println( bsex.toString() );
			return;
			}

		super.insert( req, out, command, insertname );
		}


	private void ifcanseemark( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( ph_session.canSeeMark() )
				req.setSwitchedOff(  false );
			else
				req.setSwitchedOff(  true );
			}
		catch ( Exception ex )
			{
			req.setSwitchedOff(  false );
			logException( null, "PigeonHoleFacility", "ifcanseemark", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	

	
	private void ifcanup( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( ph_session.canUpload() )
				req.setSwitchedOff(  false );
			else
				req.setSwitchedOff(  true );
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "ifcanup", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	

	
	private void noupmessage( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			if ( !ph_session.canUpload() )
				out.print( ph_session.denyUploadMessage() );
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "noupmessage", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	



	private void ifentry( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		try
			{
			PigeonHoleEntry entry=ph_session.getPigeonHoleEntry();
			if ( entry!=null )
				{
				PrimaryKey fid = entry.getUploadedFileId();
				if ( fid==null )
					{
					req.setSwitchedOff(  true );
					return;
					}
				UploadedFileSummary finfo = ph_session.getFileSummary( fid );
				req.setSwitchedOff(  finfo.isDeleted() );
				}
			else
				req.setSwitchedOff(  true );
				
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "ifentry", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		}	
	
	private void upload( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		int length, i;
		String name, param, value, avalue=null, bvalue=null, cvalue=null;
		String file, file_name, mime_type;
		PigeonHoleEntry entry;

		int l=req.getContentLength();
		
		if ( l>(2*1024*1024) )
			{
			out.println( "<CENTER><H1>Your file was NOT accepted because it is too big (greater than 2Mb).</H1></CENTER>" );
			return;
			}

		avalue=req.getParameter( "a" );
		bvalue=req.getParameter( "b" );
		cvalue=req.getParameter( "c" );
		if ( cvalue!=null )
			{
			//a form for deleting was included so we have to check
			// values.
			if ( avalue==null || bvalue!=null )
				{
				out.println( "<H1>File Rejected.</H1><P>  There is already a file " +
					"in the pigeon hole and your form selections suggest that " +
					"you may want to keep it.  You can't put two files in the " +
					"pigeon hole.</P>" );
				return;
				}
			}

		file = req.getParameter( "file" );
		file_name = req.getParameterFileName( "file" );

		if ( file_name == null || file_name.length() == 0 )
			{
			out.println( "Upload failed because no file name was supplied in the form." );
			return;
			}

		if ( file_name.indexOf( '/' )>=0 )
			{
			out.println( "File names may not contain any slash characters." );
			return;
			}

        file_name = UploadedFile.nameToURL( file_name );
		
		try
			{
			if ( file!=null && file_name!=null )
				{
				mime_type=req.getServletConfig().getServletContext().getMimeType( file_name );
				if ( mime_type==null )
				    mime_type="application/octet-stream";
				ph_session.transferFileForAssessment( file, file_name, mime_type );
				out.println( "<HR>Upload succeeded.<HR>" );
				}
			}
		catch ( Exception ex )
			{
			logException( out, "PigeonHoleFacility", "upload", 
			    "Technical problem: ex.toString()",
			    ex );
			}
		}


	
	private void overwrite( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHoleEntry entry;

		if ( out==null )
		    return;
		    
		try
			{
			entry=ph_session.getPigeonHoleEntry();

			//no entry, no point showing an overwrite form
			if ( entry==null )
				return;

			out.println( 
				"<INPUT TYPE=CHECKBOX NAME=a>I've thought about it and I want " +
				"to overwrite the current entry.<BR>" +
				"<INPUT TYPE=CHECKBOX NAME=b CHECKED>Actually, I want to keep the " +
				"the current entry." +
				"<INPUT TYPE=HIDDEN NAME=c VALUE=dummy>"
				);
			}
		catch ( Exception ex )
			{
			logException( null, "PigeonHoleFacility", "overwrite", 
			    "Technical error looking for an entry in the database.",
			    ex );
			return;
			}
		
		}


	private void reviewlink( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHoleEntry entry=new PigeonHoleEntry();
		try
			{
			entry=ph_session.getPigeonHoleEntry();
			if ( entry==null )
				{
				//no entry no link
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_RETURNED )
				{
				out.println( 
					"<UL><LI><A TARGET=docmain HREF=bs_template_pigeonreview.html>Review feedback and/or mark</A></LI></UL>" );
				return;
				}
			}
		catch ( Exception ex )
			{
			out.println( "<HR>Technical error looking for an entry in the database.<HR>"+ex );
			return;
			}
		}
	
	private void reviewmark( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException
		{
		PigeonHole ph;
		PigeonHoleEntry entry=new PigeonHoleEntry();
		boolean seen=false;

		try
			{
			ph = ph_session.getPigeonHole();
			entry=ph_session.getPigeonHoleEntry();
			if ( entry==null )
				{
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_RETURNED )
				{
				if ( entry.getAdjustedMark() != null )
					{
					seen=true;
					out.print( "<CENTER><H1>Mark = " );
					out.print( entry.getAdjustedMark().toString() );
					out.print( " out of " );
					out.print( ph.getMaximumMark() );
					out.println( "</H1></CENTER>" );
					}
				else
					out.println( "<CENTER><P>There is no mark for your work.</P></CENTER>" );
				if ( entry.getFeedback()!=null && entry.getFeedback().trim().length()>0 )
					{
					seen=true;
					out.println( "<H4>Feedback relating to the file you uploaded:</H4>" );
					out.println( "<TABLE CLASS=bs-table-opaque ALIGN=CENTER><TR><TD><I>" );
					out.println( entry.getFeedback() );
					out.println( "</I></TD></TR></TABLE>" );
					}
				else
					out.println( "<CENTER><P>There is no feedback for your work.</P></CENTER>" );

				if ( seen )
					{
        			AssessmentEvent event = new AssessmentEvent( 
        							AssessmentEvent.EVENT_VIEW_ASSESSMENT,
        							req.getResource().getResourceId(), 
        							entry.getUserId(), 
      								null,
        							new Integer( entry.getPigeonHoleEntryId().intValue() ), 
        							entry.getAdjustedMark()==null?null:new BigDecimal( entry.getAdjustedMark().toString() )    );
        			event.save();
					}
				return;
				}

			out.println( "<P>Feedback and/or a mark are not available.</P>" );
			}
		catch ( Exception ex )
			{
			out.println( "<HR>Technical error looking for an entry in the database.<HR>"+ex );
			return;
			}
		
		}
	
	
	
	
	private void entrylist(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER><TR><TD>Entry</TD><TD>Mark</TD><TD>Mod</TD></TR>" );
		
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
				
			out.print(  "<TR><TD><A TARGET=docmain HREF=" + 
						entry.getUserId() +
						"/bs_template_pigeonmarkpaper.html>" );

			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</A></TD>" );
			
			out.print( "<TD>" );
			if ( entry.getMark() != null )
				out.print( entry.getMark().toString() );
			else
				out.print( "~" );
			out.print( "</TD>" );
			
			out.print( "<TD>" );
			if ( entry.getAdjustedMark() != null )
				out.print( entry.getAdjustedMark().toString() );
			else
				out.print( "~" );
			out.print( "</TD></TR>" );
			}

		out.println( "</TABLE>" );
		}


	private void entrystatuslist(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER=1>");
                out.print(  "<TR VALIGN=TOP><TD>Set all entries" );
                out.println( "</TD><TD><SELECT NAME=\"status\">" );
                out.println("<OPTION VALUE=\"-1\">Set individually");
                for ( j=0; j<PigeonHoleEntry.status_message.length; j++ )
		{
			out.print( "<OPTION VALUE=\"" + j + "\"" );
			out.print( " >CHANGE ALL TO: " );
			out.println( PigeonHoleEntry.status_message[j] );
		}
                out.println("</SELECT>");
                out.println("</TD></TR>");
                out.println("</TABLE>");
                out.println("<TABLE BORDER=1>");
                out.println("<TR><TD>Name</TD><TD>Status</TD><TD>Lock</TD>" +
			         "<TD>If set to date dependent would user be able to upload now?</TD></TR>" );
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
				
			out.print(  "<TR VALIGN=TOP><TD>" );
				
			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</TD><TD><SELECT NAME=\"status" + entry.getPigeonHoleEntryId() + "\">" );
			for ( j=0; j<PigeonHoleEntry.status_message.length; j++ )
				{
				out.print( "<OPTION VALUE=\"" + j + "\"" );
				if ( j==entry.getStatus() )
					{
					out.print( " SELECTED>" );
					out.println( PigeonHoleEntry.status_message[j] );
					}
				else
					{
					out.println( " >CHANGE TO: " );
					out.println( PigeonHoleEntry.status_message[j] );
					}
				}

			status=entry.getStatus();
			entry.setStatus( PigeonHoleEntry.STATUS_UPLOAD_BY_DATE );
			if ( ph_session.canUpload( user.getUserId() ) )
                            out.println( "</SELECT></TD><TD><INPUT NAME=\"lock" + entry.getPigeonHoleEntryId() + "\" TYPE=CHECKBOX VALUE=\"TRUE\"></TD><TD>Yes</TD>" );
			else
			    out.println( "</SELECT></TD><TD><INPUT NAME=\"lock" + entry.getPigeonHoleEntryId() + "\" TYPE=CHECKBOX VALUE=\"TRUE\"></TD><TD>No, because " + 
								ph_session.denyUploadMessage( user.getUserId() ) +
								".</TD>" );
			entry.setStatus( status );

			out.println( "</TR>" );
			}

		out.println( "</TABLE>" );
		}

	private void entrystatusconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, j, status;
		boolean changeall=false;
                boolean locked = false;
		String param;
                String lock;
		Vector users;
		Hashtable entries;
		User user;
		PigeonHoleEntry entry;
		
		//check to see if change must be made to all entries
		
                param = req.getParameter("status");
                if (param != null)
                {
                    if (param.equalsIgnoreCase("-1")) 
                         changeall = false;
                    else changeall = true;
                }
                
		users = ph_session.getPigeonHoleUsers();
		entries = ph_session.getPigeonHoleEntries();
		
		out.println( "<TABLE BORDER><TR><TD>Name</TD><TD>Status</TD></TR>" );
		for ( i=0; i<users.size(); i++ )
			{
			user = (User)users.elementAt( i );
			entry = (PigeonHoleEntry)entries.get( user.getUserId() );
			
			if ( user==null || entry==null )
				{
				out.println( "<TR><TD COLSPAN=3>Problem fetching entry details.</TD></TR>" );
				continue;
				}
		
                        //check to see if this entry is locked
                        lock = req.getParameter("lock"+entry.getPigeonHoleEntryId().toString());
                        if (lock == null) locked=false; else locked=true;
                   
                        if ((changeall==false)||locked)
			param=req.getParameter( "status" + entry.getPigeonHoleEntryId().toString() );
                        else
                            param=req.getParameter("status");
               
			if ( param==null ) continue;
			
			try
				{
				status=Integer.parseInt( param );
				}
			catch ( Exception numex )
				{
				continue;
				}
			if ( status == entry.getStatus() ||
					status < 0 ||
					status > PigeonHoleEntry.STATUS_MAX )
				continue;
				
			// got this far so entry must need changing	
			
			ph_session.recordStatus( entry.getPigeonHoleEntryId(), status );
			
			out.print(  "<TR VALIGN=TOP><TD>" );
				
			if ( user.getName() == null )
				out.print( "no name" );
			else
				out.print( user.getName() );

			out.println( "</TD><TD>" );
			out.println( PigeonHoleEntry.status_message[status] );
			out.println( "</TD></TR>" );
			}

		out.println( "</TABLE>" );
		}



	private void newq( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		try
			{
			ph_session.createQuestion();
			}
		catch ( Exception ex )
			{
			out.println( "<HR>A technical problem occured while trying to create a new question.<HR>" + ex.getMessage() );
			}
		}	

	
	private void markdeleteq(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int n;
		String id, param, notparam;
		PrimaryKey qid;

		if ( !BuildingContext.getContext().checkPermission( "edit" ) )
			{
			out.println( "<PRE>You don't have permission to delete (edit) questions in this location of the building.</PRE>\n" );
			return;
			}

		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't delete question.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );
		qid= new PrimaryKey( Integer.parseInt( id ) );

		
		param=req.getParameter( "confirm" );
		if ( param==null ) param="";
		notparam=req.getParameter( "notconfirm" );
		if ( notparam==null ) notparam="";
		if ( param.length()<1 || notparam.length()>0 )
			{
			out.println( "<HR>Wrong checkboxes selected - question not deleted.<HR>" );
			return;
			}

		ph_session.removeQuestion( qid );
		}


	private void markeditqconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int n;
		String id, param;
		PrimaryKey qid;
		Hashtable questions;
		PigeonHoleQuestion q;
		
		if ( !BuildingContext.getContext().checkPermission( "edit" ) )
			{
			out.println( "<PRE>You don't have permission to edit questions in this location of the building.</PRE>\n" );
			return;
			}


		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );
		qid= new PrimaryKey( Integer.parseInt( id ) );

		
		questions = ph_session.getPigeonHoleQuestions();
		q=(PigeonHoleQuestion)questions.get( qid );

		if ( q==null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}

		param=req.getParameter( "ordinal" );
		try
			{
			q.setOrdinal( Integer.parseInt( param ) );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in ordinal field.<HR>" );
			return;
			}

		param=req.getParameter( "available" );
		try
			{
			int a = Integer.parseInt( param );
			if ( a<1 || a>100 )
				{
				out.println( "Available Mark must be between 1 and 100" );
				return;
				}
			q.setAvailable( a );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in available marks field.<HR>" );
			return;
			}

		param=req.getParameter( "weight" );
		try
			{
			BigDecimal big = new BigDecimal( param );
			if ( big.scale()>3 )
				{
				out.println( "The weighting will be rounded because a maximum of 3 decimal places are allowed." );
				big = big.setScale( 3, BigDecimal.ROUND_HALF_UP );
				}
			if ( big.compareTo( max_weight )>0 )
				{
				out.println( "A weight greater than 100.0 is not allowed - the weight has been adjusted to 100.0." );
				big = max_weight;
				}
			q.setWeight( big );
			}
		catch ( NumberFormatException ex )
			{
			out.println( "<HR>Invalid number in weight field.<HR>" );
			return;
			}


		
		ph_session.changeQuestionText( q.getPigeonHoleQuestionId(), 
										q.getNotesBigStringId(), 
										req.getParameter( "notes" ) );


		ph_session.changeQuestion( q.getPigeonHoleQuestionId(), q );
		
		out.println( "<HR>Question Saved<HR>" );
		}


	private void markitemedit(  Request req, PrintWriter out, PigeonHoleSession ph_session, String insertname )
		throws IOException, BuildingServerException
		{
		String id;
		Hashtable questions;
		PigeonHoleQuestion question;
		
		if ( !BuildingContext.getContext().checkPermission( "edit" ) )
			{
			out.println( "<PRE>You don't have permission to edit questions in this location of the building.</PRE>\n" );
			return;
			}


		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
		id=(String)req.getTemplateParameter( 0 );

		questions=ph_session.getPigeonHoleQuestions();
		if ( questions == null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}
			
		question = (PigeonHoleQuestion)questions.get( new PrimaryKey( Integer.parseInt( id ) ) );
		
		if ( question==null )
			{
			out.println( "<I>Can't display edit field.</I>" );
			return;
			}

		if ( insertname.equalsIgnoreCase( "ordinal" ) )
			{
			out.println( "<INPUT NAME=ordinal VALUE=\"" +question.getOrdinal() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "available" ) )
			{
			out.println( "<INPUT NAME=available VALUE=\"" +question.getAvailable() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "weight" ) )
			{
			out.println( "<INPUT NAME=weight VALUE=\"" +question.getWeight().toString() + "\">" );
			return;
			}
		if ( insertname.equalsIgnoreCase( "notes" ) )
			{
			out.print( "<SPAN CLASS=bs-textarea><TEXTAREA NAME=notes COLS=45 ROWS=8>" );
			out.print( question.getNotes() );
			out.println( "</TEXTAREA></SPAN>" );
			return;
			}
		}

	private void questionToHTML( PigeonHoleQuestion question, PrintWriter out, String style, PigeonHoleResponse response )
		throws IOException, BuildingServerException
		{

		out.println( "<TR><TD><B>Notes</B></TD></TR><TR><TD>" );
		out.println( question.getNotes() );
		out.println( "</TD></TR>" );

		if ( style.equalsIgnoreCase("mark") )
			{
			out.println( "<TR><TD><B>Mark</B></TD></TR><TR><TD>" );
			int a = question.getAvailable();
			int r=-1;

			if ( response!=null && response.getMark() != null )
				r = response.getMark().intValue();
				
			out.print( "<SELECT NAME=QR" );
			out.print( question.getPigeonHoleQuestionId().toString() );
			out.println( ">" );
			if ( r==-1 )
				out.println( "<OPTION VALUE=\"-1\" SELECTED>Not Marked" );
			else
				out.println( "<OPTION VALUE=\"-1\">Not Marked" );
				
			for ( int i=0; i<=a; i++ )
				{
				if ( r==i )
					out.println( "<OPTION VALUE=\"" + i + "\" SELECTED>" + i );
				else
					out.println( "<OPTION VALUE=\"" + i + "\">" + i );
				}
			out.println( "</SELECT>" );
			}
		else
			{
			if ( style.equalsIgnoreCase( "markreview" ) )
				{
				out.println( "<TR><TD><B>Mark</B></TD></TR><TR><TD>" );
				if ( response!=null && response.getMark() != null )
					{
					out.print( response.getMark().intValue() );
					out.print( " out of " );
					out.print( question.getAvailable() );
					out.println( "." );
					}
				else
					out.println( "No mark assigned." );
				out.println( "</TD></TR>" );
				}
			else
				{
				out.println( "<TR><TD><B>Available Marks</B></TD></TR><TR><TD>" );
				out.println( question.getAvailable() );
				out.println( "</TD></TR>" );
				}
			}
		
		if ( style.equalsIgnoreCase("edit") )
			{
			out.println( "<TR><TD><B>Weight</B></TD></TR><TR><TD>" );
			out.println( question.getWeight().toString() );
			out.println( "</TD></TR>" );
			}
		}
	


	private void markpaper(  Request req, PrintWriter out, PigeonHoleSession ph_session, String mode )
		throws IOException, BuildingServerException
		{
		int i;
		boolean edit, mark=false, canmark=false;
		String param;
		PrimaryKey uid;
		
		Vector questions_in_order;
		Hashtable responses=null;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry=null;
		PigeonHoleResponse response=null;
		Resource resource;

		edit = ( mode!=null && mode.equalsIgnoreCase( "edit" ) );
		mark = ( mode!=null && mode.equalsIgnoreCase( "mark" ) );

		if ( !edit && !mark )
			{
			out.println( "Unkown presentation format selected." );
			return;
			}
			
		if ( edit )
			{
			if ( !BuildingContext.getContext().checkPermission( "edit" ) )
				{
				out.println( "<PRE>You don't have permission to edit assessment categories here.</PRE>\n" );
				return;
				}
			uid=null;
			}

		if ( mark )
			{
			if ( !BuildingContext.getContext().checkPermission( "mark" ) )
				{
				out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
				return;
				}

			if ( req.getTemplateParameterCount()<1 )
				{
				out.println( "Invalid page address - no user specified" );
				return;
				}

			param=(String)req.getTemplateParameter( 0 );
			if ( param==null )
				{
				out.println( "Invalid page address - no user specified" );
				return;
				}
			uid = new PrimaryKey( Integer.parseInt( param ) );

			entry=ph_session.getPigeonHoleEntry( uid );
			
			if ( entry==null )
				{
				out.println( "<HR>No database entry for this student.</HR>" );
				return;
				}

			if ( entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_BY_DATE || 
				entry.getStatus() == PigeonHoleEntry.STATUS_UPLOAD_CHANGEABLE  )
				{
				out.println( "<HR><CENTER><H1>This student's work is not ready for assessment.</H1></CENTER>" );
				return;
				}
				
			canmark=true;
			if ( entry.getStatus() != PigeonHoleEntry.STATUS_UPLOAD_LOCKED &&
				 entry.getStatus() != PigeonHoleEntry.STATUS_UPLOAD_PROCESSED )
				{
				out.println( "<HR><CENTER><H1>Assessment of this student's work is completed.</H1></CENTER>" );
				canmark=false;
				}
				
			responses=ph_session.getPigeonHoleResponses( entry.getPigeonHoleEntryId() );
			if ( responses==null )
				responses = new Hashtable();
			
			if ( canmark )
				out.println( "<FORM ACTION=bs_template_pigeonmarkconfirm.html METHOD=POST>" );
			}
		
		resource = BuildingContext.getContext().getResource();

		questions_in_order = ph_session.getPigeonHoleQuestionsInOrder();
			
			
		for ( i=0; i<questions_in_order.size(); i++ )
			{
			q=(PigeonHoleQuestion)questions_in_order.elementAt( i );
				
			out.println( "<P></P><TABLE BORDER><TR><TD><H4>Category " + 
					(i+1) + " (" + q.getOrdinal() + ")</H4>" );
						
			if ( edit )
				{
				out.print( "<P><FORM METHOD=POST ACTION=\"" );
				out.print( req.getContextPath() );
				out.print( req.getServletPath() );
				out.print( resource.getFullName()  );
				out.print( q.getPigeonHoleQuestionId() + "/bs_template_markeditq.html\">" );
				out.println( "<INPUT TYPE=SUBMIT VALUE=\"Edit This Question\"></FORM></P>" );
				}
					
			out.println( "</TD></TR>" );
				
			if ( mark )
				{
				response = (PigeonHoleResponse)responses.get( q.getPigeonHoleQuestionId() );
				if ( canmark )
					questionToHTML( q, out, mode, response );
				else
					questionToHTML( q, out, "markreview", response );
				}
			else
				questionToHTML( q, out, mode, null );
				
			out.println( "</TABLE>" );
			}

		if ( i==0 )
			out.println( "<P>There are currently no assessment categories for this pigeon hole.</P>" );

		if ( mark )
			{
			if ( canmark )
				{
				out.println( "<H4>General Feedback</H4>" );
				out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=feedback>" );
				if ( entry.getFeedback()!=null )
					out.print( entry.getFeedback() );
				out.println( "</TEXTAREA></SPAN><BR>" );
				out.println( "<H4>Private Comments</H4>" );
				out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=comments>" );
				if ( entry.getComments()!=null )
					out.print( entry.getComments() );
				out.println( "</TEXTAREA></SPAN<<BR>" );
				out.println( "<INPUT TYPE=SUBMIT VALUE=\"Save Changes\">" );
				out.println( "</FORM>" );
				}
			else
				{
				if ( BuildingContext.getContext().checkPermission( "manage" )  && !ph_session.canSeeMark( entry.getUserId() ))
					{
					out.println( "<HR><H4>Assign Adjusted Mark for Release</H4>" );
					out.println( "<P>The final released mark may be adjusted.  E.g." );
					out.println( " a penalty for handing in late or extra marks " );
					out.println( " because the student was absent or suffered some  " );
					out.println( " other impediment.</P>" );
					out.println( "<FORM NAME=adjustedform METHOD=POST ACTION=bs_template_adjustedmarkconfirm.html>" );
					out.println( "<TABLE CLASS=bs-table-opaque><TR><TD><B>Uploaded:</B></TD><TD><H4>" );
					if ( entry.getWhenSaved()!=null )
						{
						String d=DateFormatter.formatDate( entry.getWhenSaved(), 1 );
						out.println( d );
						}
					else
						out.println( "No date." );
					out.println( "</H4></TR><TR><TD><B>Total Mark:</B></TD><TD><H2>" );
					if ( entry.getMark()!=null )
						{
						out.println( entry.getMark().toString() );
						out.print( "<INPUT TYPE=BUTTON VALUE=\"Set\" ONCLICK=" );
						out.print( "\"adjustedform.adjustedmark.value=" );
						out.print( entry.getMark().toString() );
						out.println( "\">" );
						}
					else
						out.println( "No Mark" );
					out.println( "</H2></TD></TR><TR><TD><B>Adjusted Mark</B></TD><TD>" );
					out.print( "<INPUT NAME=adjustedmark VALUE=\"" );
					if ( entry.getAdjustedMark()!=null )
						out.print( entry.getAdjustedMark().toString() );
					out.println( "\"></TD></TR><TR><TD><B>General Feedback</B></TD><TD>" );
					out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=feedback>" );
					if ( entry.getFeedback()!=null )
						out.print( entry.getFeedback() );
					out.println( "</TEXTAREA></SPAN>" );
					out.println( "</TD></TR><TR><TD><B>Private Comments</B></TD><TD>" );
					out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=50 ROWS=5 NAME=comments>" );
					if ( entry.getComments()!=null )
						out.print( entry.getComments() );
					out.println( "</TEXTAREA></SPAN></TD></TR><TR><TD></TD><TD>" );
					out.println( "<INPUT TYPE=SUBMIT VALUE=\"Save Adjusted Mark\">" );
					out.println( "</TD></TR></TABLE></FORM>" );
					}
				else
					{
					out.println( "<P>It isn't possible to edit marks or to save a final adjusted mark.</P>" );
					}
				}
			}			
		}
	
	private void markpaperconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i;
		Vector questions_in_order;
		PrimaryKey[] qids;
		int[] marks;
		String feedback, comments, uid;
		PrimaryKey user_id;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry;

		
		if ( !BuildingContext.getContext().checkPermission( "mark" ) )
			{
			out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
			return;
			}

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		user_id = new PrimaryKey( Integer.parseInt( uid ) );
		entry = ph_session.getPigeonHoleEntry( user_id );
		if ( entry == null )
			{
			out.println( "Unable to find database entry." );
			return;
			}
		
		
		questions_in_order = ph_session.getPigeonHoleQuestionsInOrder();
		qids = new PrimaryKey[questions_in_order.size()];
		marks = new int[questions_in_order.size()];

		feedback=req.getParameter( "feedback" );
		comments=req.getParameter( "comments" );
		if ( feedback==null || comments==null )
			{
			out.println( "<HR>Invalid form input.<HR>" );
			return;
			}
		
		for ( i=0; i<questions_in_order.size(); i++ )
			{
			q=(PigeonHoleQuestion)questions_in_order.elementAt( i );
			
			qids[i] = q.getPigeonHoleQuestionId();
			
			marks[i] = Integer.parseInt( req.getParameter( "QR" + q.getPigeonHoleQuestionId() ) );
			
			if ( marks[i]<0 )
				out.println( "Warning: no mark assigned for category " + (i+1) + "<BR>" );
			}
			
		entry = ph_session.recordMarks( entry.getPigeonHoleEntryId(), qids, marks, feedback, comments );
			
		out.println( "<P>Entries were recorded." );
		
		if ( entry.getMark() != null )
			{
                        //set a provisional adjusted mark
                        entry.setAdjustedMark(entry.getMark());    
           
			out.print( "<BR>The total mark, before moderation is " );
			out.print( entry.getMark().toString() );
			out.print( " out of " );
			PigeonHole ph = ph_session.getPigeonHole();
			out.print( ph.getMaximumMark() );
			out.println( "." );
			}
		else
			out.println( "Marking is incomplete for this work." );
			
		out.print( "</P>" );
		/*
		String uid, feedback, comments, m;
		ResultSet results;
		Connection con;
		PigeonHoleEntry entry;
		PigeonHoleQuestion question;
		PigeonHoleResponse response;
		Hashtable responses;
		double total;
		boolean complete;
		

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		User user = (User)BuildingContext.getContext().getUser();
		
		try
			{
			con=db.getConnection();
			entry=getEntry( req.getResource().getResourceId().intValue(), uid );
			
			if ( entry==null )
				{
				out.println( "<HR>No database entry for that student.</HR>" );
				return;
				}

			responses=getResponses( entry );
			
			Enumeration enumeration=ph.getQuestions();
			total=0;
			complete=true;
			for ( int q=1; enumeration.hasMoreElements(); q++ )
				{
				out.println( "<H4>Assessment Category " + q + "</H4>" );
				question=(PigeonHoleQuestion)enumeration.nextElement();
				response=(PigeonHoleResponse)responses.get( question.question_id );
				if ( response==null )
					{
					response=new PigeonHoleResponse();
					response.result_id=entry.id;
					response.question_id=question.question_id;
					response.feedback=null;
					response.mark=null;
					response.insert( con );
					}
				
				m=req.getParameter( "QR" + question.question_id );
				if ( m==null )
					{
					out.println( "<P>WARNING - no mark given in category.</P> " + q );
					response.mark=null;
					complete=false;
					}
				else
					{
					out.println( "<P>Mark: " + m + "</P>");
					try
						{
						response.mark=new Integer( m );
						total+=
							question.weight.doubleValue() *
							(double)response.mark.intValue()
							/(double)question.available.intValue();
						}
					catch ( NumberFormatException numex )
						{
						response.mark=null;
						out.println( "<P>Technical problem saving mark.</P>" );
						}
					}
				response.update( con );
				}

			feedback=req.getParameter( "feedback" );
			comments=req.getParameter( "comments" );
			if ( feedback==null || comments==null )
				{
				out.println( "<HR>Invalid form input.<HR>" );
				return;
				}
			entry.marker=req.string_user_id;
			java.util.Date d=new java.util.Date();
			entry.when_marked=new java.sql.Timestamp( d.getTime() );
			entry.feedback=feedback;
			entry.comments=comments;

			if ( complete )
				entry.mark=new Integer( Math.round( (float)(total*10.0) ) );
			else
				entry.mark=null;

			if ( entry.status_upload.intValue() == PigeonHoleEntry.STATUS_UPLOAD_LOCKED )
				entry.status_upload = new Integer( PigeonHoleEntry.STATUS_UPLOAD_PROCESSED );
			entry.update( con );
			
			
        	AssessmentEvent event = new AssessmentEvent( 
        					AssessmentEvent.EVENT_RECORD_ASSESSMENT,
        					req.getResource().getResourceId(), 
        					user.getUserId(), 
							null,
        					new Integer( entry.id.intValue() ), 
        					entry.mark==null?null:new BigDecimal( entry.mark.toString() )
        					);
        	event.save();
			}
		catch ( Exception ex )
			{
			out.println( "</TABLE>" );
			out.println( ex.toString() );
			}
		*/
		}

	private void adjustedmarkconfirm(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException, BuildingServerException
		{
		int i, amark;
		Vector questions_in_order;
		PrimaryKey[] qids;
		String feedback, comments, uid;
		PrimaryKey user_id;
		PigeonHoleQuestion q;
		PigeonHoleEntry entry;

		
		if ( !BuildingContext.getContext().checkPermission( "mark" ) )
			{
			out.println( "<PRE>You don't have permission to mark papers here.</PRE>\n" );
			return;
			}

		if ( req.getTemplateParameterCount()<1 )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		uid=(String)req.getTemplateParameter( 0 );
		if ( uid==null )
			{
			out.println( "Invalid page address - no user specified" );
			return;
			}

		user_id = new PrimaryKey( Integer.parseInt( uid ) );
		entry = ph_session.getPigeonHoleEntry( user_id );
		if ( entry == null )
			{
			out.println( "Unable to find database entry." );
			return;
			}
		
		
		feedback=req.getParameter( "feedback" );
		comments=req.getParameter( "comments" );

		if ( feedback==null || comments==null )
			{
			out.println( "<HR>Invalid form input.<HR>" );
			return;
			}
		
		//general marks.
		amark=-1;
		if ( req.getParameter( "adjustedmark" )!=null && req.getParameter( "adjustedmark" ).length()>0 )
			{
			try
				{
				amark = Integer.parseInt( req.getParameter( "adjustedmark" ) );
				}
			catch ( NumberFormatException nfex )
				{
				out.println( "The value entered in the adjusted mark box was not recognized as a number." );
				return;
				}
			}
			
		
		entry = ph_session.recordAdjustedMark( entry.getPigeonHoleEntryId(), amark, feedback, comments );
			
		out.println( "<P>Your entries were recorded.</P>" );
		
			
		}
/*
	private void remark(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		String uid, feedback, comments, m;
		ResultSet results;
		Connection con;
		PigeonHoleEntry entry;
		PigeonHoleQuestion question;
		PigeonHoleResponse response;
		Hashtable responses=new Hashtable();
		Vector questions=new Vector();
		Vector entries=new Vector();
		

		try
			{
			con=db.getConnection();

			results=db.select( 
				"SELECT * FROM pigeon_hole_questions WHERE (location = " + 
				req.location.id +
				")" );
			while ( results.next() )
				{
				question = new PigeonHoleQuestion();
				question.load( results );
				questions.addElement( question );
				}

			results=db.select( 
				"SELECT id, location, user_id, up_changed, down_changed, " +
				"deleted, marker, when_marked, mark, penalty, adjusted_mark, " +
				"feedback, comments, DATALENGTH(upload) up_length, " +
				"DATALENGTH(download) down_length " +
				"FROM pigeon_hole_entries WHERE (location = " +
				req.location.id +
				")" );
			while ( results.next() )
				{
				entry=new PigeonHoleEntry();
				entry.load( results );
				entries.addElement( entry );
				}


			for ( int s=0; s<entries.size(); s++ )
				{
				entry=(PigeonHoleEntry)entries.elementAt( s );

				responses=new Hashtable();
				results=db.select( 
					"SELECT * FROM pigeon_hole_responses WHERE (result_id = " + 
					entry.id +
					")" );
				while ( results.next() )
					{
					response = new PigeonHoleResponse();
					response.load( results );
					responses.put( response.question_id, response );
					}

				entry.mark=new Integer( 0 );
				for ( int q=0; q<questions.size(); q++ )
					{
					question=(PigeonHoleQuestion)questions.elementAt( q );
					response=(PigeonHoleResponse)responses.get( question.question_id );
					if ( response==null || response.mark==null )
						{
						out.println( "<HR>Missing response for: " + entry.user_id + "<BR>" );
						entry.mark=null;
						break;
						}
					else
						{
						entry.mark=new Integer( 
								entry.mark.intValue() + 
								response.mark.intValue() );
						}
				 	}
				entry.update( con );
				}
			}
		catch ( Exception ex )
			{
			out.println( "</TABLE>" );
			out.println( ex.toString() );
			}
		}
*/	

/*
	private void eventlog(  Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws IOException
		{
		int j;
		String sql;
		PreparedStatement statement;
		Connection con;
		ResultSet results;
		java.util.Date jdate;
		Event event;

		if ( !BuildingContext.getContext().checkPermission( "manage" ) )
			{
			out.println( "<HR>You need manage access to view the log.<HR>" );
			return;
			}

		try
			{
			sql="SELECT * " +
				"FROM events_detail " +
				"WHERE (location = ? AND detail_level <= 1000 ) " +
				"ORDER BY happened ASC";
			con=db.getConnection();
			statement=con.prepareStatement( sql );
			statement.setInt( 1, req.location.id.intValue() );
			results=statement.executeQuery();
			
			for ( j=0; results.next();  )
				{
				event=new Event( );
				event.load( results );
				jdate=new java.util.Date( event.happened.getTime() );
				
				if ( j==0 )
					out.println( "</A></H3><TABLE CLASS=bs-table-opaque>" );

				out.println( "<TR><TD><I>" );
				outputDate( req, out, jdate, 0 );
				out.println( "</I></TD><TD>" );
				printEvent( out, event );
				out.println( "</TD></TR>" );
				j++;
				}
			if ( j>0 )
				out.println( "</TABLE>" );
			}
		catch ( Exception ex )
			{
			out.println( "<HR>There was a technical problem trying to get a list of events.<HR>" + ex.getMessage() );
			return;
			}
		
		}
*/
	
	private void uplink( Request req, PrintWriter out, PigeonHoleSession ph_session, String url )
		throws ServletException, IOException, BuildingServerException
		{
		PigeonHoleEntry entry;
		UploadedFileSummary uf;
		PrimaryKey uid=null;
		
	    if ( req.getTemplateParameterCount()>0 )
	    	{
	    	uid = new PrimaryKey( Integer.parseInt( (String)req.getTemplateParameter( 0 ) ) );
			entry = ph_session.getPigeonHoleEntry( uid );
			}
		else
			{
			entry = ph_session.getPigeonHoleEntry();
			if ( entry!=null )
			    uid = entry.getUserId();
			}
		
		if ( entry == null || uid == null )
			return;
			
		uf = ph_session.getFileSummary( entry.getUploadedFileId() );
		if ( uf == null )
			return;
		
		out.print( "<A HREF=\"" + req.absoluteURL() );
        out.print( uid.toString() );
        out.print( "/" );
        out.print( uf.getUrl() );
        out.print( "\">" );
        out.print( uf.getName() );
        out.print( "</A>" );
        
		
		}
	
	private void pigeonfield( Request req, PrintWriter out, PigeonHoleSession ph_session, String name )
		throws ServletException, IOException, BuildingServerException
		{
		if ( !BuildingContext.getContext().checkPermission( "manage" ) )
			{
			out.println( "<PRE>You don't have permission to manage this pigeon hole.</PRE>\n" );
			return;
			}


		PigeonHole ph = ph_session.getPigeonHole();

		if ( name.equalsIgnoreCase( "maximum_mark" ) )
			{
			out.print( "<INPUT NAME=maximum_mark VALUE=\"" );
			out.print( ph.getMaximumMark() );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_open" ) )
			{
			out.print( "<INPUT NAME=date_open VALUE=\"" );
			if ( ph.getDateOpen()!=null )
				DateFormatter.outputDate( out, ph.getDateOpen(), 1 );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_deadline" ) )
			{
			out.print( "<INPUT NAME=date_deadline VALUE=\"" );
			if ( ph.getDateDeadline()!=null )
				DateFormatter.outputDate( out, ph.getDateDeadline(), 1 );
			out.println( "\">" );
			return;
			}
		if ( name.equalsIgnoreCase( "date_penalty" ) )
			{
			out.print( "<INPUT NAME=date_penalty VALUE=\"" );
			if ( ph.getDatePenalty()!=null )
				DateFormatter.outputDate( out, ph.getDatePenalty(), 1 );
			out.println( "\">" );
			return;
			}

		
		return;
		}


	private void pigeonmodify( Request req, PrintWriter out, PigeonHoleSession ph_session )
		throws ServletException, IOException, BuildingServerException
		{
		String strdate;
		java.util.Date date;
		Timestamp open, deadline, penalty;
		int max;
		
		open=null;
		deadline=null;
		penalty=null;

		strdate=req.getParameter( "date_open" );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					{
					out.println( "<HR><P>The opening date couldn't be understood and has been cleared.<HR>" );
					open=null;
					}
				else
					open=new java.sql.Timestamp( date.getTime() );
				}
			}

		strdate=req.getParameter( "date_deadline" );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					{
					out.println( "<HR><P>The deadline date couldn't be understood and has been cleared.<HR>" );
					deadline=null;
					}
				else
					deadline=new java.sql.Timestamp( date.getTime() );
				}
			}

		strdate=req.getParameter( "date_penalty" );
		if ( strdate!=null )
			{
			strdate=strdate.trim();
			if ( strdate.length()>0 )
				{
				date=DateParser.parse( strdate );
				if ( date==null )
					{
					out.println( "<HR><P>The penalty date couldn't be understood and has been cleared.<HR>" );
					penalty=null;
					}
				else
					penalty=new java.sql.Timestamp( date.getTime() );
				}
			}

		strdate=req.getParameter( "maximum_mark" );
		try
			{
			max = Integer.parseInt( strdate );
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "The entry in the maximum mark field could not be understood as a valid number - changes have not been saved." );
			return;
			}
			
		ph_session.setDates( open, deadline, penalty );
		
		out.println( "<p>Dates saved.</P>" );
		
		if ( max<1 )
			{
			max = 1;
			out.println( "The maximum mark field has been adjusted up to 1." );
			}

		if ( max>100 )
			{
			max = 100;
			out.println( "The maximum mark field has been adjusted down to 100." );
			}
		ph_session.setMaximumMark( max )	;
		
		out.println( "<p>Maximum mark saved.</P>" );
		}


	}


