/* ======================================================================
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.logbook.servlet;


import java.io.*;
import java.math.BigInteger;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.rmi.RemoteException;
import java.text.DateFormat;
import javax.servlet.*;
import javax.servlet.http.*;

import org.bodington.logbook.server.*;
import org.bodington.database.PrimaryKey;
import org.bodington.server.BuildingContext;
import org.bodington.server.BuildingServerUserException;
import org.bodington.server.BuildingSession;
import org.bodington.server.BuildingSessionManagerImpl;
import org.bodington.server.BuildingServerException;
import org.bodington.server.events.UserFileEvent;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.UploadedFile;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.servlet.Request;
import org.bodington.servlet.facilities.Facility;
import org.bodington.util.CsvReader;

import org.apache.log4j.Logger;

/**
 * This implements the web based user interface onto a LogBookSession.  In other words
 * it translates HTML form input and HTML template instructions into calls to the session
 * and translates responses from the session into HTML to return to the user in web
 * pages.
 *
 * To help with maintainance of the code any HTML that is output should try to conform to
 * XHTML standards.  Mainly;
 *
 * 1) Lower case letters for element and attribute names
 * 2) Quotes around all attribute values
 * 3) For HTML tags that lack a close element tag, e.g. <br> use a space and slash i.e. <br />
 * 
 * @author Jon Maber
 * @author Peter Crowther
 */
public class LogBookFacility extends Facility
	{
    
    private static Logger log = Logger.getLogger(LogBookFacility.class);
	private static final String FORMAT_EDIT     = "edit";
	private static final String FORMAT_PERSONAL = "personal";
	private static final String FORMAT_CROSSPOST = "crosspost";
        
        private static final String PARAGRAPH_MODE_LINK = "link";
        private static final String PARAGRAPH_MODE_DELETE = "appenddelete";
        private static final String PARAGRAPH_MODE_OPTION = "option";

	
	private static DateFormat date_format = 
		DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM );
	
	
	/**
	 * Called by the Bodington system when the user creates a new log book.
	 * The Facility can decide which subclass of Resource to instantiate and
	 * kind do some initialisation.  This method must not attempt to save the
	 * resource in this method.
	 * 
	 * @return A newly instantiated but unsaved Resource or sub-class of Resource.
	 */
	public Resource newResource()
		{
		return new LogBook();
		}
	
	public boolean canCopy( Resource resource )
    {
        return true;
    }
	
	/**
	 * Take form data from a create log book form (other than the standard fields)
	 * and sets corresponding properties in the object.  For this to work the
	 * template that contains the form and this method must use the same field
	 * names. This method mustn't attempt to save the resource.
	 * 
	 * @param breq The Bodington Request object from which form parameters can be retrieved.
	 * @param new_resource The new resource that needs initialisation.
	 * @return Returns true if initialisation succeeded or false if it failed.
	 * @exception java.lang.Exception
	 */
	public boolean initResource( Request breq, Resource new_resource )
		throws Exception
		{
		LogBook log_book;

		String user_closable = breq.getParameter( "user_closable" );
		
		if ( !(new_resource instanceof LogBook) )
			throw new Exception( "Technical problem: An incorrect type of resource was created." );
		
		log_book = (LogBook)new_resource;
		log_book.setUserClosable( user_closable != null && user_closable.length() > 0 );

		return true;
		}

	
	public boolean initResource( Resource original_resource, Resource new_resource )
		throws BuildingServerException
		{
		 if ( !(new_resource instanceof LogBook) )
		   throw new BuildingServerException( "Error: The wrong type of resource was created." );

		 LogBook original = (LogBook)original_resource;
		 LogBook new_logbook = (LogBook)new_resource;

		 new_logbook.setUserClosable( original.isUserClosable() );
		 new_logbook.setLogFlags( original.getLogFlags() );

		 return true;
	}


    /**
     * Copy the content of one resource to another.
     * <p>
     * <h4>Content copied</h4>
     * <ul>
     * <li>authored sections.
     * <li>authored questions.
     * </ul>
     * <h4>Content <em>not</em> copied</h4>
     * <ul>
     * <li>user entries.
     * <li>user pages.
     * </ul>
     * </p>
     * @param original_resource the resource whose contents are to be copied.
     * @param new_resource the resource to copy content to.
     * @param breq the building request object.
     * @throws BuildingServerException if there is any problem copying the
     *         resource content.
     */
	public void copyContent( Resource original_resource, Resource new_resource, Request breq )
	        throws BuildingServerException
	      	{
//	  authored : sections, questions
//	  userdata : entries, pages? /** @todo  */

		 BuildingSession session = BuildingSessionManagerImpl.getSession( original_resource );
		 if ( !(session instanceof LogBookSession) )
		   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 (RemoteException ex)
		 {
		   throw new BuildingServerException( ex.getMessage(), "Error copying resource content." );
		 }
	}


    /**
     * 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
	      	{
	  	 LogBookSession session;
	  	 LogBookSection[] sections;
		 LogBookQuestion[] questions;
		 PrimaryKey section_id;
		 LogBookSection new_section;
		 LogBookQuestion new_question;

		 session = (LogBookSession)BuildingSessionManagerImpl.getSession( original_resource );
		 sections = session.getSectionsInOrder();

		 for (int i=0; i < sections.length; i++)
		 {
		   new_section = new LogBookSection();
		   new_section.setLogBookId( new_resource.getPrimaryKey() );
		   new_section.setTitle( sections[i].getTitle() );
		   new_section.setIntroduction( sections[i].getIntroduction() );
		   new_section.setOrdinal( sections[i].getOrdinal() );
		   new_section.save();
		   section_id = sections[i].getLogBookSectionId();
		   questions = session.getQuestionsInOrder( section_id );
		   for (int j=0; j < questions.length; j++)
		   {
		     new_question = new LogBookQuestion();
		     new_question.setLogBookSectionId( new_section.getLogBookSectionId() );
		     new_question.setQuestion( questions[j].getQuestion() );
		     new_question.setTitle( questions[j].getTitle() );
		     new_question.setHelp( questions[j].getHelp() );
		     new_question.setOrdinal( questions[j].getOrdinal() );
		     new_question.save();
		   }
		 }
	}

		
	/**
	 * Parses form data from a create new logbook form to validate user input.
	 * 
	 * @param breq The Request object from which form parameters can be read.
	 * @param out Writer to which user input validation messages can be sent adivsing
	 * the user how to correct their selections.
	 * @return Returns true if all is OK or false if the input is invalid.
	 */
	public boolean createCheck( Request breq, PrintWriter out )
		{
		// There is only one boolean to check - it's empty or has a value to
		// indicate true/false so it can't have invalid values.  So always
		// return true.
		return true;
		}
		
		
		
		
	public boolean generateFile( Request req, HttpServletResponse res )
		throws ServletException, IOException
		{
		// generate a file, or select a previously generated file
		
		if ( req.getPageName().equals( "questions.csv" ) )
			{
			return generateCsvQuestionFile( req, res );
			}
			
		return super.generateFile( req, res );
		}

		
	public boolean generateCsvQuestionFile( Request req, HttpServletResponse res )
		throws ServletException, IOException
		{
		try
		    {
			BuildingSession session;
			LogBookSession lb_session;

		    session = BuildingSessionManagerImpl.getSession( req.getResource() );
		    if ( !(session instanceof LogBookSession) )
		    	throw new ServletException( "Unexpected problem generating file." );
		    lb_session = (LogBookSession)session;

			lb_session.generateCsvQuestionFile();

			res.setContentType( "text/plain" );
			return true;
		    }
		catch ( BuildingServerException bsex )
		{
		    log.error(bsex.getMessage(), bsex );
		}
		
		return false;
		}
		
	public void insert( Request req, PrintWriter out, String command, String insertname )
		throws ServletException, IOException
		{

		
		try
			{
			BuildingSession session;
			LogBookSession lb_session;

		    session = BuildingSessionManagerImpl.getSession( req.getResource() );
		    if ( !(session instanceof LogBookSession) )
		    	{
		    	out.println( "<hr />Technical problem: unable to access appropriate tool session.<hr />" );
		    	return;
		    	}
		    lb_session = (LogBookSession)session;
			
			// now we can examine the command and decide what to do and what HTML
			// to output.  If the command is processed here we need to return but
			// if not pass it on to the insert method in the parent class of this class, i.e. 
			// Facility.insert()

			if ( command.equalsIgnoreCase( "logbookfield" ) )
				{
				logBookField( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "logbookmodify" ) )
				{
				logBookModify( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "questionmenu" ) )
				{
				logBookMenu( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "newsection" ) )
				{
				newSection( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "newquestion" ) )
				{
				newQuestion( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "logbooksectionfield" ) )
				{
				logBookSectionField( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "logbookquestionfield" ) )
				{
				logBookQuestionField( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "logbooksectionproperty" ) )
				{
				logBookSectionProperty( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "logbookquestionproperty" ) )
				{
				logBookQuestionProperty( req, out, lb_session, insertname );
				return;
				}

			if ( command.equalsIgnoreCase( "deletesection" ) )
				{
				deleteSection( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "deletequestion" ) )
				{
				deleteQuestion( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "editsection" ) )
				{
				editSection( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "editquestion" ) )
				{
				editQuestion( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "importquestions" ) )
				{
				parseImportQuestions( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "confirmimportquestions" ) )
				{
				importQuestions( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "logbookentry" ) )
				{
				logBookEntry( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "logentrymenu" ) )
				{
				logEntryMenu( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "collatedlog" ) )
				{
				collatedLog( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "logentrymenuoperation" ) )
				{
				logEntryMenuOp( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "crosspost" ) )
				{
				parseCrossPostEntries( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "confirmcrosspost" ) )
				{
				newCrossPostEntries( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "newentry" ) )
				{
				newEntry( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "editentry" ) )
				{
				editEntry( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "isdraft" ) )
				{
				PrimaryKey entry_id = getEntryId( req );
				LogBookEntry entry = null;
				if ( entry_id!=null )
					entry = lb_session.getEntry( entry_id );
				
				if ( entry == null )
					{
					req.setSwitchedOff(  true );
					return;
					}
				req.setSwitchedOff(  !entry.isDraft() );
				return;
				}

			if ( command.equalsIgnoreCase( "isprivate" ) )
				{
				PrimaryKey entry_id = getEntryId( req );
				LogBookEntry entry = null;
				if ( entry_id!=null )
					entry = lb_session.getEntry( entry_id );
				
				if ( entry == null )
					{
					req.setSwitchedOff(  true );
					return;
					}
                                req.setSwitchedOff( !entry.isPrivate() );
				return;
				}

                        // True if the page's viewer is not the page's owner.  PJC 2004-07-29.
			if ( command.equalsIgnoreCase( "isvisitor" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
                                User viewer = (User)BuildingContext.getContext().getUser();
				LogBookPage page = lb_session.getPageForUser( user_id );
                		if ( page == null )
					return;

				req.setSwitchedOff( page.getUserId().equals( viewer.getPrimaryKey() ) );
				return;
				}

                        // True if the user could create a private entry, i.e. if they own the page.  PJC 2004-04-08.
			if ( command.equalsIgnoreCase( "allowsprivate" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
                                User viewer = (User)BuildingContext.getContext().getUser();
				LogBookPage page = lb_session.getPageForUser( user_id );
                		if ( page == null )
					{
                                        // If the current user would be the owner of the created log book, they can create private entries. PJC 2004-07-19.
					req.setSwitchedOff( !viewer.getPrimaryKey().equals( user_id ) );
					return;
					}

				req.setSwitchedOff( !page.getUserId().equals( viewer.getPrimaryKey() ) );
				return;
				}

                        // True if the user could create a private entry, i.e. if they own the page, but this entry is new or not private.  PJC 2004-04-08.
			if ( command.equalsIgnoreCase( "allowsbutisnotprivate" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
                                User viewer = (User)BuildingContext.getContext().getUser();
				LogBookPage page = lb_session.getPageForUser( user_id );
                		if ( page == null )
					{
                                        // If the current user would be the owner of the created log book, they can create private entries. PJC 2004-07-19.
					req.setSwitchedOff( !viewer.getPrimaryKey().equals( user_id ) );
					return;
					}
				PrimaryKey entry_id = getEntryId( req );
				LogBookEntry entry = null;
				if ( entry_id!=null )
					entry = lb_session.getEntry( entry_id );
				
				if ( entry == null )
					{
        				req.setSwitchedOff( !page.getUserId().equals( viewer.getPrimaryKey() ) );
					return;
					}
                        
				req.setSwitchedOff( entry.isPrivate() || !page.getUserId().equals( viewer.getPrimaryKey() ) );
				return;
				}

                        // 'public' actually means 'neither private nor draft'
			if ( command.equalsIgnoreCase( "ispublic" ) )
				{
				PrimaryKey entry_id = getEntryId( req );
				LogBookEntry entry = null;
				if ( entry_id!=null )
					entry = lb_session.getEntry( entry_id );
				
				if ( entry == null )
					{
					req.setSwitchedOff(  true );
					return;
					}
				req.setSwitchedOff( entry.isPrivate() || entry.isDraft() );
				return;
				}

                        // True if the user can upload files.  At present, this is true iff they own the page.  PJC 2004-04-14.
			if ( command.equalsIgnoreCase( "allowsattachments" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
                                User viewer = (User)BuildingContext.getContext().getUser();
				LogBookPage page = lb_session.getPageForUser( user_id );
                		if ( page == null )
					{
                                        // If the current user would be the owner of the created log book, they can upload. PJC 2004-07-20.
					req.setSwitchedOff( !viewer.getPrimaryKey().equals( user_id ) );
					return;
					}

				req.setSwitchedOff( !page.getUserId().equals( viewer.getPrimaryKey() ) );
				return;
				}

			if ( command.equalsIgnoreCase( "islogclosed" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
				LogBookPage page = lb_session.getPageForUser( user_id );
				if ( page == null )
					req.setSwitchedOff(  true );
				else
					req.setSwitchedOff(  !page.isClosed() );
				return;
				}

			if ( command.equalsIgnoreCase( "canpost" ) )
				{
				// here it just checks post access and if the page is closed
				// but when the user
				// acutally tries to post messages more checks are made
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
				LogBookPage page = lb_session.getPageForUser( user_id );
				if ( page == null )
					req.setSwitchedOff(  !req.getResource().checkPermission( Permission.POST ) );
				else
					req.setSwitchedOff(  !(req.getResource().checkPermission( Permission.POST ) && !page.isClosed()) );
				return;
				}

			if ( command.equalsIgnoreCase( "canopenorclose" ) )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
				LogBookPage page = lb_session.getPageForUser( user_id );
				if ( page==null )
					req.setSwitchedOff(  true );
				else
					req.setSwitchedOff(  !lb_session.canOpenOrClose( page.getLogBookPageId() ) );
				return;
				}

			if ( command.equalsIgnoreCase( "confirmopenlog" )  || 
			     command.equalsIgnoreCase( "confirmcloselog" )     )
				{
				PrimaryKey user_id = getUserId( req );
				if ( user_id == null )
					{
					out.println( "<p><strong>User wasn't specified</strong></p>" );
					return;
					}
				LogBookPage page = lb_session.getPageForUser( user_id );
				if ( command.equalsIgnoreCase( "confirmopenlog" ) )
					{
					lb_session.setPageClosed( page.getLogBookPageId(), false );
					out.println( "<p>Log Opened.</p>" );
					}
				else
					{
					lb_session.setPageClosed( page.getLogBookPageId(), true );
					out.println( "<p>Log Closed.</p>" );
					}
				return;
				}

			if ( command.equalsIgnoreCase( "logpagemenu" ) )
				{
				logPageMenu( req, out, lb_session );
				return;
				}

			if ( command.equalsIgnoreCase( "logbookpageproperty" ) )
				{
				logBookPageProperty( req, out, lb_session, insertname );
				return;
				}

                        if (  command.equalsIgnoreCase( "fileparagraph" ) )
                                {
                                if ( out!=null )
                                        {
                                        String mode = req.getInsertAttribute( "mode", "link" );
                                        boolean hide_deleted = req.getInsertAttribute( "hidedeleted", "yes" ).equalsIgnoreCase( "yes" );
                                        String linked_url = null;
                                        if ( mode.equals( "option" ) )
                                                {
                                                PrimaryKey entry_id = getEntryId( req );
                                                LogBookEntry entry = null;
                                                if ( null != entry_id )
                                                        entry = lb_session.getEntry( entry_id );
                                                if ( null != entry )
                                                        linked_url = entry.getLinkedUrl();
                                                }
                                        fileparagraph( req, out, null, hide_deleted, mode, linked_url );
                                        }
                                return;
                                }
                        if (  command.equalsIgnoreCase( "uploadtolog" ) )
                                {
                                if ( null != out )
                                        uploadToLog( req, out );
                                return;
                                }
			
                        if (  command.equalsIgnoreCase( "maybeamendpeers" ) )
                                {
                                if ( null != out )
                                        {
                                        boolean amend = (null != req.getParameter( "mode" ) );
                                        if ( amend )
                                            {
                                            boolean shouldAdd = req.getParameter( "mode" ).equalsIgnoreCase( "add" );
                                            String user_id = req.getParameter( "userid" );
                                            if ( null != user_id && user_id.length() > 0 )
                                                {
                                                amendpeers( req, out, lb_session, shouldAdd, user_id );
                                                }
                                            }
                                        }
                                return;
                                }

                        if (  command.equalsIgnoreCase( "peers" ) )
                                {
                                if ( null != out )
                                        {
                                        boolean possible = req.getInsertAttribute( "mode", "existing" ).equalsIgnoreCase( "possible" );
                                        peers( req, out, lb_session, possible );
                                        }
                                return;
                                }

			}		
		catch ( BuildingServerException bsex )
			{
			out.println( bsex.toString() );
			return;
			}
		
		super.insert( req, out, command, insertname );
		}
	

	/**
	 * Private method called from insert() inserts an HTML form field
	 * corresponding to a property of the LogBook with the current value
	 * set as the default value.  Used to build a form to modify the logbook
	 * that has the correct current values in it.  If the name parameter is 
	 * user_closable this produces a check box tag which is checked or unchecked 
	 * depending  on the current setting of the user_closable field in the 
	 * LogBook that was accessed.
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the field to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookField(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{

		if ( name.equalsIgnoreCase( "user_closable" ) )
			{
			out.print( "<input type=\"checkbox\" name=\"user_closable\" " );
			if ( lb_session.isUserClosable() )
				out.print( "checked=\"true\"" );
			out.println( " />" );
			return;
			}

		// current version of log book doesn't have any other fields
		return;
		}
	

	/**
	 * Private method called from insert() inserts an HTML form field
	 * corresponding to a property of the LogBook with the current value
	 * set as the default value.  Used to build a form to modify the logbook
	 * that has the correct current values in it.  If the name parameter is 
	 * user_closable this produces a check box tag which is checked or unchecked 
	 * depending  on the current setting of the user_closable field in the 
	 * LogBook that was accessed.
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the field to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookSectionField(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey section_id = getSectionOrQuestionId( req );
		if ( section_id == null )
			{
			out.println( "<p><strong>Section wasn't specified</strong></p>" );
			return;
			}

		Hashtable table = lb_session.getSectionAndQuestionTable();
		if ( table == null )
			{
			out.println( "<p><strong>No sections in logbook.</strong></p>" );
			return;
			}
			
		LogBookSection section = (LogBookSection)table.get( section_id );
		if ( section == null )
			{
			out.println( "<p><strong>Section not in logbook.</strong></p>" );
			return;
			}


		if ( name.equalsIgnoreCase( "ordinal" ) )
			{
			out.println( "<input name=\"ordinal\" VALUE=\"" +section.getOrdinal() + "\">" );
			return;
			}

		// span is used to help make it possible to use a style sheet to
		// select the font and define the size of the textarea in older
		// browsers.
		if ( name.equalsIgnoreCase( "title" ) )
			{
			out.print( "<span class=bs-textarea>" );
			out.print( "<textarea name=\"title\" COLS=\"45\" ROWS=\"4\">" );
			out.print( section.getTitle() );
			out.println( "</textarea></span>" );
			return;
			}

		if ( name.equalsIgnoreCase( "introduction" ) )
			{
			out.print( "<span class=bs-textarea>" );
			out.print( "<textarea name=\"introduction\" COLS=\"45\" ROWS=\"14\">" );
			out.print( section.getIntroduction() );
			out.println( "</textarea></span>" );
			return;
			}

		// current version of log book doesn't have any other fields
		return;
		}
	

	/**
	 * Private method called from insert() inserts an HTML form field
	 * corresponding to a property of the LogBook with the current value
	 * set as the default value.  Used to build a form to modify the logbook
	 * that has the correct current values in it.  If the name parameter is 
	 * user_closable this produces a check box tag which is checked or unchecked 
	 * depending  on the current setting of the user_closable field in the 
	 * LogBook that was accessed.
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the field to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookQuestionField(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey question_id = getSectionOrQuestionId( req );
		if ( question_id == null )
			{
			out.println( "<p><strong>Question wasn't specified</strong></p>" );
			return;
			}


		Hashtable table = lb_session.getSectionAndQuestionTable();
		if ( table == null )
			{
			out.println( "<p><strong>No questions in logbook.</strong></p>" );
			return;
			}
			
		LogBookQuestion question = (LogBookQuestion)table.get( question_id );
		if ( question == null )
			{
			out.println( "<p><strong>Question not in logbook.</strong></p>" );
			return;
			}


		if ( name.equalsIgnoreCase( "ordinal" ) )
			{
			out.println( "<input name=\"ordinal\" VALUE=\"" +question.getOrdinal() + "\">" );
			return;
			}

		// span is used to help make it possible to use a style sheet to
		// select the font and define the size of the textarea in older
		// browsers.
		if ( name.equalsIgnoreCase( "title" ) )
			{
			out.print( "<span class=bs-textarea>" );
			out.print( "<textarea name=\"title\" COLS=\"45\" ROWS=\"4\">" );
			out.print( question.getTitle() );
			out.println( "</textarea></span>" );
			return;
			}

		if ( name.equalsIgnoreCase( "question" ) )
			{
			out.print( "<span class=bs-textarea>" );
			out.print( "<textarea name=\"question\" COLS=\"45\" ROWS=\"14\">" );
			out.print( question.getQuestion() );
			out.println( "</textarea></span>" );
			return;
			}

		if ( name.equalsIgnoreCase( "help" ) )
			{
			out.print( "<span class=bs-textarea>" );
			out.print( "<textarea name=\"help\" COLS=\"45\" ROWS=\"14\">" );
			out.print( question.getHelp() );
			out.println( "</textarea></span>" );
			return;
			}

		// current version of log book doesn't have any other fields
		return;
		}
	

	/**
	 * Private method called from insert() outputs a given property of the
	 * section as plain text.
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the property to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookSectionProperty(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey section_id = getSectionOrQuestionId( req );
		if ( section_id == null )
			{
			out.println( "<p><strong>Section wasn't specified</strong></p>" );
			return;
			}

		Hashtable table = lb_session.getSectionAndQuestionTable();
		if ( table == null )
			{
			out.println( "<strong>No sections in logbook.</strong>" );
			return;
			}
			
		LogBookSection section = (LogBookSection)table.get( section_id );
		if ( section == null )
			{
			out.println( "<strong>Section not in logbook.</strong>" );
			return;
			}


		if ( name.equalsIgnoreCase( "ordinal" ) )
			{
			out.print( section.getOrdinal() );
			return;
			}
		if ( name.equalsIgnoreCase( "title" ) )
			{
			out.print( section.getTitle() );
			return;
			}
		if ( name.equalsIgnoreCase( "introduction" ) )
			{
			out.print( section.getIntroduction() );
			return;
			}
		
		return;
		}
	
	/**
	 * Private method called from insert() inserts plain text
	 * corresponding to a property of the LogBookQuestion
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the field to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookQuestionProperty(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey question_id = getSectionOrQuestionId( req );
		if ( question_id == null )
			{
			out.println( "<p><strong>Question wasn't specified</strong></p>" );
			return;
			}

		Hashtable table = lb_session.getSectionAndQuestionTable();
		if ( table == null )
			{
			out.println( "<strong>No questions in logbook.</strong>" );
			return;
			}
			
		LogBookQuestion question = (LogBookQuestion)table.get( question_id );
		if ( question == null )
			{
			out.println( "<strong>Question not in logbook.</strong>" );
			return;
			}


		if ( name.equalsIgnoreCase( "ordinal" ) )
			{
			out.print( question.getOrdinal() );
			return;
			}

		// span is used to help make it possible to use a style sheet to
		// select the font and define the size of the textarea in older
		// browsers.
		if ( name.equalsIgnoreCase( "title" ) )
			{
			out.print( question.getTitle() );
			return;
			}
		if ( name.equalsIgnoreCase( "question" ) )
			{
			out.print( question.getQuestion() );
			return;
			}
		if ( name.equalsIgnoreCase( "help" ) )
			{
			out.print( question.getHelp() );
			return;
			}

		// current version of log book doesn't have any other fields
		return;
		}
	
	/**
	 * Private method called from insert() inserts plain text
	 * corresponding to a property of the LogBookPage referenced by user_id
	 * 
	 * @param req The Request object the defines the page request.
	 * @param out An output writer to send the HTML to.
	 * @param lb_session The user's session with the LogBook.
	 * @param name The name of the field to output.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookPageProperty(  Request req, PrintWriter out, LogBookSession lb_session, String name )
		throws IOException, BuildingServerException
		{
		// The section id is in the  web address requested
		PrimaryKey user_id = getUserId( req );
		if ( user_id == null )
			{
			out.println( "<p><strong>User wasn't specified</strong></p>" );
			return;
			}

		// this isn't a real property of the page but of the user whether
		// they have a page yet or not
		if ( name.equalsIgnoreCase( "name" ) )
			{
			out.print( lb_session.getUserFullName( user_id ) );
			return;
			}

		LogBookPage page = lb_session.getPageForUser( user_id );

		if ( page==null )
			return;
			
		if ( name.equalsIgnoreCase( "when_first" ) )
			{
			if ( page.getWhenFirst() == null )
				out.println( "~" );
			else
				out.print( date_format.format( page.getWhenFirst() ) );
			return;
			}

		if ( name.equalsIgnoreCase( "when_last" ) )
			{
			if ( page.getWhenLast() == null )
				out.println( "~" );
			else
                                {
				out.print( date_format.format( page.getWhenLast() ) );
                                //PrimaryKey user_last_id = page.getUserLastId();
        			//if ( null != user_last_id )
                			//out.print( " by " + lb_session.getUserFullName( user_last_id ) );
                                }
			return;
			}

		if ( name.equalsIgnoreCase( "when_user_first" ) )
			{
			if ( page.getWhenUserFirst() == null )
				out.println( "~" );
			else
				out.print( date_format.format( page.getWhenUserFirst() ) );
			return;
			}

		if ( name.equalsIgnoreCase( "when_user_last" ) )
			{
			if ( page.getWhenUserLast() == null )
				out.println( "~" );
			else
				out.print( date_format.format( page.getWhenUserLast() ) );
			return;
			}

		if ( name.equalsIgnoreCase( "section_first" ) )
			{
			if ( page.getSectionFirst() == null )
				out.println( "~" );
			else
				out.print( page.getSectionFirst() );
			return;
			}

		if ( name.equalsIgnoreCase( "section_last" ) )
			{
			if ( page.getSectionLast() == null )
				out.println( "~" );
			else
				out.print( page.getSectionLast() );
			return;
			}

		if ( name.equalsIgnoreCase( "section_user_first" ) )
			{
			if ( page.getSectionUserFirst() == null )
				out.println( "~" );
			else
				out.print( page.getSectionUserFirst() );
			return;
			}

		if ( name.equalsIgnoreCase( "section_user_last" ) )
			{
			if ( page.getSectionUserLast() == null )
				out.println( "~" );
			else
				out.print( page.getSectionUserLast() );
			return;
			}

		if ( name.equalsIgnoreCase( "question_first" ) )
			{
			if ( page.getQuestionFirst() == null )
				out.println( "~" );
			else
				out.print( page.getQuestionFirst() );
			return;
			}

		if ( name.equalsIgnoreCase( "question_last" ) )
			{
			if ( page.getQuestionLast() == null )
				out.println( "~" );
			else
				out.print( page.getQuestionLast() );
			return;
			}

		if ( name.equalsIgnoreCase( "question_user_first" ) )
			{
			if ( page.getQuestionUserFirst() == null )
				out.println( "~" );
			else
				out.print( page.getQuestionUserFirst() );
			return;
			}

		if ( name.equalsIgnoreCase( "question_user_last" ) )
			{
			if ( page.getQuestionUserLast() == null )
				out.println( "~" );
			else
				out.print( page.getQuestionUserLast() );
			return;
			}

		return;
		}
	/**
	 * Takes form input and modifies the properties of the LogBook accordingly
	 * by making calls to the LogBookSession.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookModify(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{

		String user_closable = req.getParameter( "user_closable" );
		lb_session.setUserClosable( user_closable!=null && user_closable.length()>0 );

		out.println( "<p align=\"center\">The requested changes were stored.</p>" );
		}

	
	/**
	 * Outputs the titles of the sections and the questions of the log book in 
	 * specified format.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @param format Selector for the output format to use.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookMenu(  Request req, PrintWriter out, LogBookSession lb_session, String format )
		throws IOException, BuildingServerException
		{
		int i, j;
		String s_page, q_page, target;
		PrimaryKey user_id=null;
		boolean new_link, collate_link;
		
		if ( FORMAT_EDIT.equalsIgnoreCase( format ) )
			{
			s_page = "bs_template_editsection.html";
			q_page = "bs_template_editquestion.html";
			target = "editcontent";
			new_link = true;
			collate_link = false;
			}
		else if ( FORMAT_CROSSPOST.equalsIgnoreCase( format ) )
			{
			s_page = "bs_template_section.html";
			q_page = "bs_template_crosspostquestion.html";
			target = "content";
			new_link = false;
			collate_link = false;
			}
		else if ( FORMAT_PERSONAL.equalsIgnoreCase( format ) )
			{
			s_page = "bs_template_section.html";
			q_page = "bs_template_question.html";
			target = "content";
			user_id = getUserId( req );
			if ( user_id == null )
				out.println( "Didn't specify which user's log to view." );
			new_link = false;
			collate_link = true;
			}
		else
			{
			out.println( "Unknown menu format requested." );
			return;
			}
		
		LogBookSection[] sections = lb_session.getSectionsInOrder();
		LogBookQuestion[] questions;
		

		if ( collate_link )
			{
			// link to collation form
			out.print( "<div class=\"bs-logbook-menu-command\"><a target=\"" );
			out.print( target );
			out.print( "\" href=\"" );
			out.print( req.absoluteURL() );
			if ( user_id != null )
				{
				out.print( user_id.toString() );
				out.print( "/" );
				}
			out.print( "bs_template_collatelog.html\">" );
			out.print( "Collate Entries</a></div>" );
			
			if ( user_id!=null )
				{
				LogBookPage page = lb_session.getPageForUser( user_id );
				if ( page!=null && lb_session.canOpenOrClose( page.getLogBookPageId() ) )
					{
					out.print( "<div class=\"bs-logbook-menu-command\"><a target=\"" );
					out.print( target );
					out.print( "\" href=\"" );
					out.print( req.absoluteURL() );
					if ( user_id != null )
						{
						out.print( user_id.toString() );
						out.print( "/" );
						}
					out.print( "bs_template_closelog.html\">" );
					if ( page.isClosed() )
						out.print( "Open this Log." );
					else
						out.print( "Close this Log." );
					out.print( "</a></div>" );
					}
				}
			}


		// putting the menu in a 'div' element means we can change the
		// style of the heading elements independantly of other parts of
		// the Bodington system by adding lines to the style sheet.
		out.println( "<div class=\"bs-logbook-menu\">" );
		for ( i=0; i<sections.length; i++ )
			{
			// this puts a heading in the menu for a section and makes
			// it a link to the section editing page targetted on the
			// right hand frame
			out.print( "<div class=\"bs-logbook-menu-section\"><a target=\"" );
			out.print( target );
			out.print( "\" href=\"" );
			out.print( req.absoluteURL() );
			if ( user_id != null )
				{
				out.print( user_id.toString() );
				out.print( "/" );
				}
			out.print( sections[i].getLogBookSectionId().toString() );
			out.print( "/" );
			out.print( s_page );
			out.print( "\">" );
			out.print( sections[i].getTitle() );
			out.println( "</a></div>" );
			
			// Questions similar but are a lower level heading 
			questions = lb_session.getQuestionsInOrder( sections[i].getLogBookSectionId() );
			for ( j=0; j<questions.length; j++ )
				{
				out.print( "<div class=\"bs-logbook-menu-question\"><a target=\"" );
				out.print( target );
				out.print( "\" href=\"" );
				out.print( req.absoluteURL() );
				if ( user_id != null )
					{
					out.print( user_id.toString() );
					out.print( "/" );
					}
				out.print( questions[j].getLogBookQuestionId().toString() );
				out.print( "/" );
				out.print( q_page );
				out.print( "\">" );
				out.print( questions[j].getTitle() );
				out.println( "</a></div>" );
				}

			if ( new_link )
				{
				// link to immediately create a new question under specific section
				out.print( "<div class=\"bs-logbook-menu-new-q\"><a target=\"editcontent\" href=\"" );
				out.print( sections[i].getLogBookSectionId().toString() );
				out.print( "/bs_template_newquestionconfirm.html\">" );
				out.println( "Add a question here</div>" );
				}
			}

		if ( new_link )
			{
			// link to immediately create a new section
			out.print( "<div class=\"bs-logbook-menu-new-s\">" );
			out.print( "<a target=\"editcontent\" href=\"bs_template_newsectionconfirm.html\">" );
			out.println( "Add a section here</a></div>" );
			}

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


	/**
	 * Immediately creates a new section at the end of the log book
	 * by making calls to the LogBookSession.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void newSection(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{


		try
			{
			lb_session.createSection();
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "newSection", 
			    "A technical problem occured tring to create a new section.", bsex );
			return;
			}


		out.println( "<p align=\"center\">A new section was added.</p>" );
		}

	/**
	 * Immediately creates a new question at the end of the specified section
	 * by making calls to the LogBookSession.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void newQuestion( Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey section_id = getSectionOrQuestionId( req );
		if ( section_id == null )
			{
			out.println( "<p><strong>Section wasn't specified</strong></p>" );
			return;
			}


		try
			{
			lb_session.createQuestion( section_id );
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "newQuestion", 
			    "A technical problem occured tring to create a new question.", bsex );
			return;
			}


		out.println( "<p align=\"center\">A new question was added.</p>" );
		}
	

	/**
	 * Deletes a specified section.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void deleteSection( Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{

		// The section id is in the  web address requested
		PrimaryKey section_id = getSectionOrQuestionId( req );
		if ( section_id == null )
			{
			out.println( "<p><strong>Section wasn't specified</strong></p>" );
			return;
			}
		
		
		String param=req.getParameter( "confirm" );
		if ( param==null ) param="";
		String notparam=req.getParameter( "notconfirm" );
		if ( notparam==null ) notparam="";
		if ( param.length()<1 || notparam.length()>0 )
			{
			out.println( "<hr />Wrong checkboxes selected - section not deleted.<hr />" );
			return;
			}


		try
			{
			lb_session.deleteSection( section_id );
			}
		catch (BuildingServerUserException bsue)
			{
		    out.write(bsue.getMessage());
		    return;
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "deleteSection", 
			    "A technical problem occured tring to delete a section.", bsex );
			return;
			}


		out.println( "<p align=\"center\">The section was deleted.</p>" );
		}
	
	/**
	 * Deletes a specified question.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void deleteQuestion( Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{

		// The question id is in the  web address requested
		PrimaryKey question_id = getSectionOrQuestionId( req );
		if ( question_id == null )
			{
			out.println( "<p><strong>Question wasn't specified</strong></p>" );
			return;
			}
		
		
		String param=req.getParameter( "confirm" );
		if ( param==null ) param="";
		String notparam=req.getParameter( "notconfirm" );
		if ( notparam==null ) notparam="";
		if ( param.length()<1 || notparam.length()>0 )
			{
			out.println( "<hr />Wrong checkboxes selected - section not deleted.<hr />" );
			return;
			}


		try
			{
			lb_session.deleteQuestion( question_id );
			}
		catch (BuildingServerUserException bsue)
			{
		    out.write(bsue.getMessage());
		    return;
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "deleteQuestion", 
			    "A technical problem occured tring to delete a question.", bsex );
			return;
			}


		out.println( "<p align=\"center\">The question was deleted.</p>" );
		}

	
	/**
	 * Edits a specified section by taking input from an HTML form, validating it
	 * and then calling a method in the session object.  Confirmation or an error
	 * message is given.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void editSection( Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		// The section id is in the  web address requested as if it were a directory
		// of the log book
		PrimaryKey section_id = getSectionOrQuestionId( req );
		if ( section_id == null )
			{
			out.println( "<p><strong>Section wasn't specified</strong></p>" );
			return;
			}
		
		
		String s_ordinal=req.getParameter( "ordinal" );
		int ordinal;
		try
			{
			ordinal = Integer.parseInt( s_ordinal );
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "<p><strong>An invalid integer number was given in the ordinal field.</strong></p>" );
			return;
			}
		String title=req.getParameter( "title" );
		String introduction=req.getParameter( "introduction" );
		

		try
			{
			lb_session.changeSection( section_id, ordinal, title, introduction );
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "editSection", 
			    "A technical problem occured tring to save an edited section.", bsex );
			return;
			}


		out.println( "<p align=\"center\">The section was updated.</p>" );
		}
	
	/**
	 * Edits a specified question by taking input from an HTML form, validating it
	 * and then calling a method in the session object.  Confirmation or an error
	 * message is given.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */	
	 private void editQuestion( Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{

		// The question id is in the  web address requested as if it were a directory
		// of the log book
		PrimaryKey question_id = getSectionOrQuestionId( req );
		if ( question_id == null )
			{
			out.println( "<p><strong>Question wasn't specified</strong></p>" );
			return;
			}
		
		
		String s_ordinal=req.getParameter( "ordinal" );
		int ordinal;
		try
			{
			ordinal = Integer.parseInt( s_ordinal );
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "<p><strong>An invalid integer number was given in the ordinal field.</strong></p>" );
			return;
			}
		String title=req.getParameter( "title" );
		String question=req.getParameter( "question" );
		String help=req.getParameter( "help" );
		

		try
			{
			lb_session.changeQuestion( question_id, ordinal, title, question, help );
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "LogBookFacility", "editQuestion", 
			    "A technical problem occured tring to save an edited question.", bsex );
			return;
			}


		out.println( "<p align=\"center\">The question was updated.</p>" );
		}

	/**
	 * Outputs the poster name and datestamps of a set of entries
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logEntryMenu(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException, ServletException
		{
		int i, j;
		
		PrimaryKey user_id = getUserId( req );
		PrimaryKey question_id = getSectionOrQuestionId( req );
		
		if ( user_id == null )
			{
			out.println( "<p>Unable to determine whose log entries to display.</p>" );
			return;
			}
		
		if ( question_id == null )
			{
			out.println( "<p>Unable to determine which question to search for log entries.</p>" );
			return;
			}
		
		boolean post_permitted = req.getResource().checkPermission( Permission.POST );
		
		LogBookPage page = lb_session.getPageForUser( user_id );
		
		LogBookEntry[] entries = lb_session.getEntriesInOrder( question_id, user_id );
		
		
		out.println( "<div class=\"bs-logbook-entrylist\">" );
		out.println( "<form name=\"entryselectform\" method=\"POST\" action=\"bs_template_question.html\">" );
		out.println( "<input type=\"hidden\" name=\"operation\" value=\"entryselection\">" );
		
		for ( i=0; i<entries.length; i++ )
			{
                        // Private entries are only visible to the user who created them and to sysadmins.  PJC 2004-04-08
                        User viewer = (User)BuildingContext.getContext().getUser();
                        if ( !entries[i].isPrivate() || ( entries[i].getUserId().equals( viewer.getPrimaryKey() ) ) || BuildingContext.getContext().checkPermission( Permission.SYSADMIN ) )
                            {
                            out.println( "<div class=\"bs-logbook-entry\">" );

                            out.println( "<div class=\"bs-logbook-entry-title\">" );
                            out.println( "<span class=\"bs-logbook-entry-author\">" );
                            out.print( lb_session.getUserFullName( entries[i].getUserId() ) );
                            out.print( "</span>" );
                            out.println( "<span class=\"bs-logbook-entry-date\">" );
                            out.print( date_format.format( entries[i].getWhenModified() ) );
                            out.println( "</span>" );
                            if ( entries[i].isDraft() )
                                    {
                                    out.print( "<span class=\"bs-logbook-entry-draft\">This entry is only a draft.</span>" );
                                    }
                            if ( entries[i].isPrivate() )
                                    {
                                    out.print( "<span class=\"bs-logbook-entry-draft\">This entry is private.</span>" );
                                    }
                            out.println( "</div>" );

                            // anyone with 'post' access to the log book page can select entries
                            // as long as the page isn't closed.
                            if ( page != null && !page.isClosed() && post_permitted )
                                    {
                                    out.print( "<div class=\"bs-logbook-entry-checkbox\">" );
                                    out.print( "<input type=\"checkbox\" name=\"selectentry_" );
                                    out.print( entries[i].getLogBookEntryId() );
                                    out.print( "\" onclick=\"forms.entryselectform.submit()\" " );
                                    if ( entries[i].isSelected() )
                                            out.print( " CHECKED" );
                                    out.print( ">Selected</div>" );
                                    }
                            else
                                    {
                                    if ( entries[i].isSelected() )
                                            {
                                            out.print( "<div class=\"bs-logbook-entry-checkbox\">" );
                                            out.print( "Selected for collation.</div>" );
                                            }
                                    }

                            if ( entries[i].canEdit() && post_permitted && 
                                                            page != null && !page.isClosed() )
                                    {			
                                    out.print( "<div class=\"bs-logbook-entry-editlink\">" );
                                    out.print( "<a href=\"" );
                                    out.print( entries[i].getLogBookEntryId() );
                                    out.print( "/bs_template_editentry.html\">Edit</a></div>" );
                                    }

                            out.print( "<div class=bs-logbook-entry-text>" );
                            out.print( formatEntryText( entries[i], true, false, 500 ) );
                            out.println( "</div>" );

                            if ( !( null == entries[i].getLinkedUrl() ) )
                                    {
                                    String fileName = entries[i].getLinkedUrl();
                                    int lastSlash = fileName.lastIndexOf("/");
                                    fileName = fileName.substring(lastSlash + 1);
                                    boolean target_exists = filesContain( req, out, null, entries[i].getLinkedUrl() );
                                    out.println( "<p><strong>Attached file: " );
                                    if ( target_exists ) out.println( "<a target=\"_blank\" href=\"" + entries[i].getLinkedUrl() + "\">" );
                                    out.println( fileName );
                                    if ( target_exists )
                                        out.println( "</a>" );
                                    else
                                        out.println( " (deleted)" );
                                    out.println( "</strong></p>" );
                                    }
                            out.println( "</div>" );
                            }
                        }

		out.println( "</form></div>" );

		lb_session.markEntriesSeen( question_id, user_id );

		}


	/**
	 * Outputs entries for all the questions in the log for printing or saving.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void collatedLog(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		int i, j, k;
		String param;
		StringBuffer section_html=new StringBuffer(), question_html=new StringBuffer();
		boolean opt_selected, opt_draft, opt_empties, opt_section, 
				opt_question, opt_author, opt_date, opt_convert;
		boolean section_done, question_done;
		
		param = req.getParameter( "selected" );
		opt_selected = param!=null && param.trim().length()>0;
		param = req.getParameter( "draft" );
		opt_draft = param!=null && param.trim().length()>0;
		param = req.getParameter( "convert" );
		opt_convert = param!=null && param.trim().length()>0;
		param = req.getParameter( "empties" );
		opt_empties = param!=null && param.trim().length()>0;
		param = req.getParameter( "section" );
		opt_section = param!=null && param.trim().length()>0;
		param = req.getParameter( "question" );
		opt_question = param!=null && param.trim().length()>0;
		param = req.getParameter( "author" );
		opt_author = param!=null && param.trim().length()>0;
		param = req.getParameter( "date" );
		opt_date = param!=null && param.trim().length()>0;
			 
		
		PrimaryKey user_id = getUserId( req );
		if ( user_id == null )
			{
			out.println( "<p>Didn't specify which user's log to view.</p>" );
			return;
			}
			
		LogBookSection[] sections = lb_session.getSectionsInOrder();
		LogBookQuestion[] questions;
		
		
		
		out.println( "<div class=\"bs-logbook-collated\">" );
		out.print( "<div class=\"bs-logbook-collated-name\">Collated Log: " );
		out.print( lb_session.getUserFullName( user_id ) );
		out.println( "</div>" );
		for ( i=0; i<sections.length; i++ )
			{
			// this puts a heading in the menu for a section and makes
			// it a link to the section editing page targetted on the
			// right hand frame
			
			section_done=false;
			section_html.setLength( 0 );
			section_html.append( "<div class=\"bs-logbook-collated-section-title\">" );
			section_html.append( sections[i].getTitle() );
			section_html.append( "</div>\n" );
			
			if ( opt_section )
				{
				section_html.append( "<div class=\"bs-logbook-collated-section-intro\">" );
				section_html.append( sections[i].getIntroduction() );
				section_html.append( "</div>" );
				}

			// Questions similar but are a lower level heading 
			questions = lb_session.getQuestionsInOrder( sections[i].getLogBookSectionId() );
			for ( j=0; j<questions.length; j++ )
				{
				question_done=false;
				
				question_html.append( "<div class=\"bs-logbook-collated-question-title\">" );
				question_html.append( questions[j].getTitle() );
				question_html.append( "</div>\n" );

				if ( opt_question )
					{
					question_html.append( "<div class=\"bs-logbook-collated-question-text\">" );
					question_html.append( questions[j].getQuestion() );
					question_html.append( "</div>\n" );
					}

				question_html.append( "<div class=\"bs-logbook-collated-entrylist\">\n" );
				
				LogBookEntry[] entries = lb_session.getEntriesInOrder( questions[j].getLogBookQuestionId(), user_id );
				
				
				
				for ( k=0; k<entries.length; k++ )
					{
                                        // Private entries are never collated.  PJC 2004-04-08.
					if ( entries[k].isPrivate() )
						continue;
					if ( !opt_draft && entries[k].isDraft() )
						continue;
					if ( opt_selected && !entries[k].isSelected() )
						continue;
					
					// roll out the stuff so far
					if ( !section_done )
						{
						out.print( section_html );
						section_done=true;
						}
						
					if ( !question_done )
						{
						out.print( question_html );
						question_html.setLength( 0 );
						question_done=true;
						}

					out.println( "<div class=\"bs-logbook-collated-entry\">" );
					
					if ( opt_author || opt_date )
						{
						out.println( "<div class=\"bs-logbook-collated-entry-title\">" );
						if ( opt_author )
							{
							out.println( "<span class=\"bs-logbook-collated-entry-author\">" );
							out.print( lb_session.getUserFullName( entries[k].getUserId() ) );
							out.print( "</span>" );
							}
						if ( opt_date )
							{
							out.println( "<span class=\"bs-logbook-collated-entry-date\">" );
							out.print( date_format.format( entries[k].getWhenModified() ) );
							out.println( "</span>" );
							}
						out.println( "</div>" );
						}
					
					out.print( "<div class=bs-logbook-collated-entry-text>" );
					out.print( formatEntryText( entries[k], opt_convert, true, Integer.MAX_VALUE ) );
					out.println( "</div>" );
					
					out.println( "</div>" );
					}
				
				// if we are to include empty questions and there were
				// no entries roll out the html so far.
				if ( opt_empties )
					{
					if ( !section_done )
						{
						out.print( section_html );
						section_done=true;
						}
					if ( !question_done )
						{
						out.print( question_html );
						question_html.setLength( 0 );
						question_done=true;
						}
					
					}
				else
					{
					if ( !question_done )
						question_html.setLength( 0 );
					}
					
				// if question header was printed we need to finish it off
				if ( question_done )
					out.println( "</div>" );
				}
			}

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


	/**
	 * Process form input on the log entry page for a question.  E.g. select
	 * entries.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logEntryMenuOp(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		int i, j;
		
		PrimaryKey user_id = getUserId( req );
		PrimaryKey question_id = getSectionOrQuestionId( req )		;
		
		if ( user_id == null )
			{
			out.println( "<p>Unable to determine who's log entries to display.</p>" );
			return;
			}
		
		if ( question_id == null )
			{
			out.println( "<p>Unable to determine which question to search for log entries.</p>" );
			return;
			}
			
		String op = req.getParameter( "operation" );
		if ( op==null || op.trim().length() == 0 )
			return;
			
		if ( op.equalsIgnoreCase( "entryselection" ) )
			{
			String param;
			LogBookEntry[] entries = lb_session.getEntriesInOrder( question_id, user_id );
			
			for ( i=0; i<entries.length; i++ )
				{
				param = req.getParameter( "selectentry_" + entries[i].getLogBookEntryId() );
				if ( param != null && param.trim().length()>0 )
					{
					if ( !entries[i].isSelected() )
						lb_session.selectEntry( entries[i].getLogBookEntryId(), true );
					}
				else
					{
					if ( entries[i].isSelected() )
						lb_session.selectEntry( entries[i].getLogBookEntryId(), false );
					}
				}
			out.println( "<p>Selection changes saved.</p>" );
			}
		}
		
	/**
	 * Outputs the text of an entry
	 * 
	 * @param req The page request.
	 * @param out The Writer to send the text to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logBookEntry(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		int i, j;
		
		PrimaryKey entry_id = getEntryId( req );
		LogBookEntry entry = null;
		if ( entry_id!=null )
			entry = lb_session.getEntry( entry_id );
		
		if ( entry == null )
			{
			out.println( "<p>Unable to determine which entry to display.</p>" );
			return;
			}
			
		String format = req.getInsertAttribute( "format", "display" );
		if ( "edit".equalsIgnoreCase( format ) )
			out.println( entry.getEntry() );
		else
			out.println( formatEntryText( entry, false, true, Integer.MAX_VALUE ) );
		}

	
	/*
	private String entryOpening( LogBookEntry entry )
		throws BuildingServerException
		{
		String text = entry.getEntry();
		if ( text.length() < 500 )
			return text;
			
		int i;
		for ( i=499; i>200; i++ )
			if (  Character.isWhitespace( text.charAt( i ) ) )
				break;
				
		StringBuffer shortened = new StringBuffer();
		
		shortened.append( text.substring( 0, i ) );
		shortened.append( "... <em>There is <a href=\"" );
		shortened.append( entry.getLogBookEntryId() );
		shortened.append( "/bs_template_logbookentry.html\">more text</a></em>" );
		
		return shortened.toString();
		}
	*/
	
	/**
	 * Reads uploaded file and parses out imported questions.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void parseImportQuestions(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		// find the file that was uploaded to temporary space...
		String file_name = req.getParameter( "file" );
		File file = new File( file_name );
		
		if ( file.length() > (10L*1024L*1024L) )
			{
			out.println( "<p>The uploaded file was too big to process.</p>" );
			return;
			}

		// input data is parsed and an HTML form is produced for
		// checking and confirmation. THe form is put in a temp
		// file initialy.
		File temp_file = BuildingContext.createTempFile( "tmplogbookform", ".tmp" );
		BufferedWriter form_html=null;
		
		try
			{
			form_html = 
				new BufferedWriter( 
					new OutputStreamWriter( 
						new FileOutputStream(temp_file), "utf-8" ) );
			
			form_html.write( "<form method=\"post\" action=\"bs_template_confirmimportquestions.html\">" );
			form_html.newLine();
			form_html.flush();
			}
		catch ( IOException ioex1 )
			{
			logException( out, "LogBookFacility", "parseImportQuestions", 
			    "A technical problem occured tring to import questions.", ioex1 );
			return;
			}
	
		
		
		// Parse the file and check for errors.
		int i=0, j;
		String field, type, title, intro, help;
		boolean completed=false;
		try
			{
			CsvReader reader = new CsvReader(
										new InputStreamReader( 
												new FileInputStream( file ), "utf-8" ) );
												
			for ( i=1; reader.nextLine(); i++ )
				{
				type=null;
				title=null;
				intro=null;
				help=null;
				for ( j=0; (field=reader.readField()) != null; j++ )
					{
					if ( j==0 )
						{
						type = field;
						if ( !type.equals( "section" ) && !type.equals( "question" ) )
							{
							out.println( "<p>Expected 'section' or 'question' in first field at line " + i + ".</p>" );
							return;
							}
						if ( i==1 && !type.equals( "section" ) )
							{
							out.println( "<p>The first record must be a section record.</p>" );
							return;
							}
						}
						
					if ( j==1 )
						{
						if ( field.trim().length()==0 )
							{
							out.println( "<p>No title at line " + i + ".</p>" );
							return;
							}
						title=field;
						}
					if ( j==2 )
						{
						if ( field.trim().length()==0 )
							{
							out.println( "<p>No introductory text at line " + i + ".</p>" );
							return;
							}
						intro=field;
						}
					if ( j==3 )
						{
						help=field;
						}
						
					}
				if ( j==0 )
					continue;
					
				if ( type.equals( "section" ) && (j<3 || j>4) )
					{
					out.println( "<p>Wrong number of fields in section record at line " + i + ".</p>" );
					return;
					}
				
				if ( type.equals( "question" ) && j!=4 )
					{
					out.println( "<p>Wrong number of fields in question record at line " + i + ".</p>" );
					return;
					}
				
				if ( type.equals( "section" ) )
					{
					form_html.write( "<h3>New Section" );
					form_html.write( "<input type=\"hidden\" name=\"type_" );
					form_html.write( Integer.toString( i ) );
					form_html.write( "\" value=\"section\"></h3>" );
					form_html.newLine();
					}
				else
					{
					form_html.write( "<h4>New Question" );
					form_html.write( "<input type=\"hidden\" name=\"type_" );
					form_html.write( Integer.toString( i ) );
					form_html.write( "\" value=\"question\"></h3>" );
					form_html.newLine();
					}

				form_html.write( "<p><strong>Title</strong></p>" );
				form_html.newLine();
				form_html.write( "<p><textarea cols=\"40\" rows=\"2\" name=\"title_" );
				form_html.write( Integer.toString( i ) );
				form_html.write( "\">" );
				form_html.write( title );
				form_html.write( "</textarea></p>" );
				form_html.newLine();
				
				form_html.write( "<p><strong>Text</strong></p>" );
				form_html.newLine();
				form_html.write( "<p><textarea cols=\"40\" rows=\"8\" name=\"intro_" );
				form_html.write( Integer.toString( i ) );
				form_html.write( "\">" );
				form_html.write( intro );
				form_html.write( "</textarea></p>" );
				form_html.newLine();
				
				if ( type.equals( "question" ) )
					{
					form_html.write( "<p><strong>Help</strong></p>" );
					form_html.newLine();
					form_html.write( "<p><textarea cols=\"40\" rows=\"8\" name=\"help_" );
					form_html.write( Integer.toString( i ) );
					form_html.write( "\">" );
					form_html.write( help );
					form_html.write( "</textarea></p>" );
					form_html.newLine();
					}
				

					
				}
				
			form_html.write( "<input type=\"submit\" value=\"Import Now\">" );
			form_html.newLine();
			form_html.write( "</form>" );
			form_html.newLine();
			form_html.close();
			
			completed=true;
			}
		catch ( IOException ioex )
			{
			out.println( "<p>Input error at line " + i + ". ( " + ioex.getMessage() + ")</p>" );
			return;
			}
		finally
			{
			if ( temp_file!=null )
				{
				try
					{
					form_html.close();
					}
				catch ( Exception ex )
					{
					}
				if ( !completed )
					temp_file.delete();
				}
			}
		
		
		
		BufferedReader form_input=null;
		try
			{
			form_input= 
				new BufferedReader( 
						new InputStreamReader( 
								new FileInputStream( temp_file ), "utf-8" ) );
				
			int c;
			out.println( "<p>Review the following and confirm if you which to go ahead and import.</p>" );
			while ( (c=form_input.read()) >= 0 )
				out.print( (char)c );
			}		
		finally
			{
			form_input.close();
			temp_file.delete();
			}
		
		}
		
	/**
	 * Reads form input and creates new sections and questions.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void importQuestions(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		int i;
		String type, title, intro, help;
		String text;
		LogBookPage page;

		
		// one pass for validation
		for ( i=1; true; i++ )
			{
			type = req.getParameter( "type_" + i );
			if ( type == null || type.length() == 0 )
				break;
			
			if ( !type.equals( "section" ) && !type.equals( "question" ) )
				{
				out.println( "<p>Invalid form input.</p>" );
				return;
				}
			
			title = req.getParameter( "title_" + i );
			intro = req.getParameter( "intro_" + i );
			if ( title == null || title.length() == 0 || intro == null || intro.length() == 0 )
				{
				out.println( "<p>Every section and question must have a title and introductory text.</p>" );
				return;
				}
			if ( type.equals( "question" ) )
				{
				help = req.getParameter( "help_" + i );
				if ( help==null || help.length() == 0 )
					{
					out.println( "<p>Every question must have help text.</p>" );
					return;
					}
				}
			}
		
		if ( i==1 )
			{
			out.println( "<p>No sections or questions to add.</p>" );
			return;
			}
		
		// second pass for processing
		LogBookSection section=null;
		LogBookQuestion question;
		for ( i=1; true; i++ )
			{
			type = req.getParameter( "type_" + i );
			if ( type == null || type.length() == 0 )
				break;
			
			title = req.getParameter( "title_" + i );
			intro = req.getParameter( "intro_" + i );
			if ( type.equals( "question" ) )
				help = req.getParameter( "help_" + i );
			else
				help=null;
				
			if ( type.equals( "section" ) )
				{
				section=lb_session.createSection();
				lb_session.changeSection( section.getLogBookSectionId(), section.getOrdinal(), title, intro );
				}
			else
				{
				question=lb_session.createQuestion( section.getLogBookSectionId() );
				lb_session.changeQuestion( question.getLogBookQuestionId(), question.getOrdinal(), title, intro, help );
				}
			}

		out.print( "<p>Import completed.</p>" );
		}

	/**
	 * Reads form input and makes entries on multiple user's logs.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void parseCrossPostEntries(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		String type = req.getInsertAttribute( "type", "view" );
		
		if ( "file".equalsIgnoreCase( type ) )
			{
			parseCrossPostEntriesFromFile(  req, out, lb_session );
			return;
			}
		
		String entry_text = req.getParameter( "entry" );
		
		boolean type_page = "page".equalsIgnoreCase( type );
		
		Vector user_ids;
		if ( type_page )
			user_ids = lb_session.getPageUserIds();
		else
			user_ids = lb_session.getViewUserIds();
			
		out.println( "<form method=\"post\" action=\"bs_template_confirmcrosspost.html\">" );

		String target_user;
		PrimaryKey user_id;
		LogBookPage page;
		for ( int i=0; i< user_ids.size(); i++ )
			{
			user_id = (PrimaryKey)user_ids.elementAt( i );
			target_user = 
				target_user = lb_session.getUserFullName( user_id );

			out.println( "<h4>" );
			out.println( target_user );
			out.println( "</h4>" );

			page = lb_session.getPageForUser( user_id );
			if ( page!=null && page.isClosed() )
				{
				out.print( "<p><strong>WARNING: the log belonging to " );
				out.print( target_user );
				out.println( " is closed and no entry can been posted there.</strong></p>" );
				}
			else
				{
				out.print( "<p><input type=\"checkbox\" checked name=\"include_user_" );
				out.print( user_id.toString() );
				out.print( "\" value=\"yes\">Include this entry.</p>" );
				out.print( "<p><textarea cols=\"40\" rows=\"8\" name=\"entry_user_" );
				out.print( user_id.toString() );
				out.print( "\">" );
				out.print( entry_text );
				out.println( "</textarea></p>" );
				}
			}
		out.println( "<input type=\"submit\" value=\"Post All Now\">" );
		out.println( "</form>" );
		}
		
	private void parseCrossPostEntriesFromFile(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		// find the file that was uploaded to temporary space...
		String file_name = req.getParameter( "file" );
		File file = new File( file_name );
		
		if ( file.length() > (10L*1024L*1024L) )
			{
			out.println( "<p>The uploaded file was too big to process.</p>" );
			return;
			}

		// input data is parsed and an HTML form is produced for
		// checking and confirmation. THe form is put in a temp
		// file initialy.
		File temp_file = BuildingContext.createTempFile( "tmplogbookform", ".tmp" );
		BufferedWriter form_html=null;
		
		try
			{
			form_html = 
				new BufferedWriter( 
					new OutputStreamWriter( 
						new FileOutputStream(temp_file), "utf-8" ) );
			
			form_html.write( "<form method=\"post\" action=\"bs_template_confirmcrosspost.html\">" );
			form_html.newLine();
			form_html.flush();
			}
		catch ( IOException ioex1 )
			{
			logException( out, "LogBookFacility", "parseCrossPostEntriesFromFile", 
			    "A technical problem occured tring to cross post.", ioex1 );
			return;
			}
		
		
		
		// Parse the file and check for errors.
		int i=0, j, id=0;
		String field, entry_text=null;
		String target_user;
		boolean completed=false;
		LogBookPage page;
		try
			{
			CsvReader reader = new CsvReader(
										new InputStreamReader( 
												new FileInputStream( file ), "utf-8" ) );
												
			for ( i=0; reader.nextLine(); i++ )
				{
				for ( j=0; (field=reader.readField()) != null; j++ )
					{
					if ( j==0 )
						{
						id = Integer.parseInt( field );
						}
						
					if ( j==1 )
						{
						if ( field.trim().length()==0 )
							{
							out.println( "<p>No entry text at line " + i + ".</p>" );
							return;
							}
						entry_text=field;
						}
					}
				if ( j==0 )
					continue;
				if ( j!=2 )
					{
					out.println( "<p>Wrong number of fields at line " + i + ".</p>" );
					return;
					}
				
				target_user = lb_session.getUserFullName( new PrimaryKey( id ) );
				if ( target_user == null )
					{
					out.println( "<p>Unknown user ID at line " + i + ".</p>" );
					return;
					}
					
				form_html.write( "<h4>" );
				form_html.write( target_user );
				form_html.write( "</h4>" );
				form_html.newLine();

				page = lb_session.getPageForUser( new PrimaryKey( id ) );
				if ( page!=null && page.isClosed() )
					{
					out.print( "<p><strong>WARNING: the log belonging to " );
					out.print( target_user );
					out.println( " is closed and no entry can been posted there.</strong></p>" );
					}
				else
					{
					form_html.write( "<p><input type=\"checkbox\" checked name=\"include_user_" );
					form_html.write( Integer.toString( id ) );
					form_html.write( "\" value=\"yes\">Include this entry.</p>" );
					form_html.newLine();
					form_html.write( "<p><textarea cols=\"40\" rows=\"8\" name=\"entry_user_" );
					form_html.write( Integer.toString( id ) );
					form_html.write( "\">" );
					form_html.write( entry_text );
					form_html.write( "</textarea></p>" );
					}
				form_html.newLine();
				}
				
			form_html.write( "<input type=\"submit\" value=\"Post All Now\">" );
			form_html.newLine();
			form_html.write( "</form>" );
			form_html.newLine();
			form_html.close();
			
			completed=true;
			}
		catch ( IOException ioex )
			{
			out.println( "<p>Input error at line " + i + ". ( " + ioex.getMessage() + ")</p>" );
			return;
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "<p>Invalid numeric data at line " + i + " </p>" );
			return;
			}
		finally
			{
			if ( temp_file!=null )
				{
				try
					{
					form_html.close();
					}
				catch ( Exception ex )
					{
					}
				if ( !completed )
					temp_file.delete();
				}
			}
		
		
		
		BufferedReader form_input=null;
		try
			{
			form_input= 
				new BufferedReader( 
						new InputStreamReader( 
								new FileInputStream( temp_file ), "utf-8" ) );
				
			int c;
			out.println( "<p>Review the following set of new entries and confirm posting.</p>" );
			while ( (c=form_input.read()) >= 0 )
				out.print( (char)c );
			}		
		finally
			{
			form_input.close();
			temp_file.delete();
			}
		
		}
		
	/**
	 * Reads form input and makes a new entry.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void newCrossPostEntries(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		PrimaryKey question_id = getSectionOrQuestionId( req )		;
		
		if ( question_id == null )
			{
			out.println( "<p>Unable to determine which question to search for log entries.</p>" );
			return;
			}

		
		Enumeration enumeration = req.getParameterNames();
		Vector user_ids = new Vector();
		String pname, str_id, name_user, check_pname, check;
		PrimaryKey user_id;
		String text;
		LogBookPage page;
		while ( enumeration.hasMoreElements() )
			{
			pname = (String)enumeration.nextElement();
			if ( !pname.startsWith( "entry_user_" ) )
				continue;
			str_id = pname.substring( "entry_user_".length() );
			try
				{
				user_id = new PrimaryKey( Integer.parseInt( str_id ) ); 
				}
			catch ( NumberFormatException nfex )
				{
				out.println( "<p>Invalid form input.</p>" );
				return;
				}
			name_user = lb_session.getUserFullName( user_id );
			if ( name_user == null )
				{
				out.println( "<p>An unknown user was given in the form data.</p>" );
				return;
				}

			// skip entries where the checkbox wasn't selected
			check_pname = "include_user_" + user_id.toString();
			check = req.getParameter( check_pname );
			if ( check==null || check.trim().length()==0 )
				continue;

			text = req.getParameter( pname  );
			if ( text!=null )
				text = text.trim();
			
			if ( text == null || text.length() == 0 )
				{
				out.println( "There was no text in one of the form fields." );
				return;
				}
				
			user_ids.addElement( user_id );
			}
		
		for ( int i=0; i<user_ids.size(); i++ )
			{
			user_id = (PrimaryKey)user_ids.elementAt( i );

			page = lb_session.getPageForUser( user_id );
			if ( page!=null && page.isClosed() )
				{
				out.print( "<p><strong>WARNING:</strong> the log belonging to " );
				out.print( lb_session.getUserFullName( user_id ) );
				out.println( " is closed and no entry has been posted there.</p>" );
				}
			else
				{
				text = req.getParameter( "entry_user_" + user_id  );
			
				lb_session.postEntry( question_id, user_id, text, false, false, null );
			
				out.print( "<p>Entry made in the log of " );
				out.print( lb_session.getUserFullName( user_id ) );
				out.print( ".</p>" );
				}
			}

		out.print( "<p>All posting now completed.</p>" );
		}

	
	/**
	 * Reads form input and makes a new entry.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void newEntry(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		boolean d;
                boolean p;
		
		PrimaryKey user_id = getUserId( req );
		PrimaryKey question_id = getSectionOrQuestionId( req )		;
		
		if ( user_id == null )
			{
			out.println( "<p>Unable to determine whose log entries to display.</p>" );
			return;
			}
		
		if ( question_id == null )
			{
			out.println( "<p>Unable to determine which question to search for log entries.</p>" );
			return;
			}
		
		String text = req.getParameter( "entry" );
		String entrytype = req.getParameter( "entrytype" );
                if ( entrytype==null || entrytype.length()==0 )
                    entrytype = "public";
                String linked_url = req.getParameter( "linkedurl" );
                if ( "".equals( linked_url ) )
                        linked_url = null;
		
		if ( text!=null )
			text = text.trim();
			
		if ( text == null || text.length() == 0 )
			{
			out.println( "There was no text in the form so no entry has been made." );
			return;
			}
		
		d = entrytype.equals( "draft" );
		p = entrytype.equals( "private" );
		
		lb_session.postEntry( question_id, user_id, text, d, p, linked_url );
		
		out.println( "<p><strong>Entry made.</strong></p>" );
		}

	
	/**
	 * Edits content and/or flags of an entry.
	 * 
	 * @param req The page request.
	 * @param out The Writer to send the text to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void editEntry(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		boolean d;
                boolean p;
		
		PrimaryKey entry_id = getEntryId( req );
		LogBookEntry entry = null;
		if ( entry_id!=null )
			entry = lb_session.getEntry( entry_id );
		
		if ( entry == null )
			{
			out.println( "<p>Unable to determine which entry to display.</p>" );
			return;
			}
		
		String text = req.getParameter( "entry" );
		String entrytype = req.getParameter( "entrytype" );
                String linked_url = req.getParameter( "linkedurl" );
                if ( "".equals( linked_url ) )
                        linked_url = null;
		
		if ( text!=null )
			text = text.trim();
			
		if ( text == null || text.length() == 0 )
			{
			out.println( "There was no text in the form so no edits have been made." );
			return;
			}
		
		d = entrytype.equals( "draft" );
		p = entrytype.equals( "private" );

                lb_session.editEntry( entry.getLogBookEntryId(), text, d, p, linked_url );

		out.println( "<p><strong>Entry saved.</strong></p>" );
		}


	/**
	 * Outputs a name with a link to its log book page.
	 * 
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @param event_table A table of logbook events filtered down to the most recent READ event where current user was the reader for each log page
         * @param page The page for which output is to be generated
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logPageMenuEntry( PrintWriter out, LogBookSession lb_session, Hashtable event_table, LogBookPage page )
		throws IOException, BuildingServerException
		{
		int i, j;
		
		LogBookEvent event;
		
                event = (LogBookEvent)event_table.get( page.getUserId() );

                out.print( "<p><strong>" );
                out.print( lb_session.getUserFullName( page.getUserId() ) );
                out.println( "</strong></p>" );


                if (  event!= null && event.getEventTime()!=null )
                        {
                        if ( page.getWhenLast() != null  )
                                {
                                if ( page.getWhenLast().after( event.getEventTime() ) )
                                        {
                                        out.print( "<p>" );
                                        out.print( "New entries since your last visit." );
                                        //out.print( date_format.format( pages[i].getWhenLast() ) );
                                        out.println( "</p>" );
                                        }
                                }
                        }
                else
                        {
                        out.print( "<p>" );
                        out.print( "Never visited by you." );
                        //out.print( date_format.format( pages[i].getWhenLast() ) );
                        out.println( "</p>" );
                        }


                out.println( "<p>" );
                out.print( "<a target=\"content\" href=\"" );
                out.print( page.getUserId().toString() );
                out.print( "/bs_template_logpage.html\">Summary" );
                out.print( "</a><br />" );
                out.print( "<a href=\"" );
                out.print( page.getUserId().toString() );
                out.print( "/bs_template_logpagelogmenu.html\">" );
                out.print( "Go to log</a>" );
                out.println( "</p>" );
		}

	/**
	 * Outputs a list of names with link to each one's log book page.
	 * 
	 * @param req The page request including form input paramters.
	 * @param out The Writer to send a confirmation message to.
	 * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException
	 * @exception org.bodington.server.BuildingServerException
	 */
	private void logPageMenu(  Request req, PrintWriter out, LogBookSession lb_session )
		throws IOException, BuildingServerException
		{
		int i, j;
		
		// a table of logbook events filtered down to the most recent
		// READ event where current user was the reader for each 
		// log page
		Hashtable event_table = lb_session.getLastReadEventTable();
		LogBookEvent event;
		LogBookPage[] pages = lb_session.getPagesInOrder();

                // First emit an entry for this user's page, assuming they have one
		User viewer = (User)BuildingContext.getContext().getUser();
                LogBookPage viewer_page = lb_session.getPageForUser( viewer.getUserId() );
                if ( ! ( null == viewer_page ) )
                        logPageMenuEntry( out, lb_session, event_table, viewer_page );
                // Now emit entries for everybody else's pages
		for ( i=0; i<pages.length; i++ )
                        logPageMenuEntry( out, lb_session, event_table, pages[i] );
		}
	

	private PrimaryKey getUserId( Request req )
		{
		// The user id is in the  web address requested as if it were a directory
		// of the log book.  If not there, then the user is accessing their own
		// record so return the user id set in the request.
		if ( req.getTemplateParameterCount() < 1 )
			{
			return req.getUserId();
			}
		
		PrimaryKey id;
		try
			{
			id =  new PrimaryKey( Integer.parseInt( (String)req.getTemplateParameter( 0 ) ) );
			}
		catch ( NumberFormatException nfex )
			{
			return null;
			}
		return id;
		}
	private PrimaryKey getSectionOrQuestionId( Request req )
		{
		// The section or question id is in the  web address requested as if it 
		// were a director of the log book but if a user id is supplied it appears
		// like a subdirectory of the user id.
		
		if ( req.getTemplateParameterCount() < 1 )
			{
			return null;
			}
			
		int pos;
		if ( req.getTemplateParameterCount() == 1 )
			pos = 0;
		else
			pos = 1;
		
		PrimaryKey id;
		try
			{
			id =  new PrimaryKey( Integer.parseInt( (String)req.getTemplateParameter( pos ) ) );
			}
		catch ( NumberFormatException nfex )
			{
			return null;
			}
		return id;
		}
	private PrimaryKey getEntryId( Request req )
		{
		// If entry id appears it will be in the web address like a sub directory of
		// the question id
		
		if ( req.getTemplateParameterCount() < 3 )
			{
			return null;
			}
			
		PrimaryKey id;
		try
			{
			id =  new PrimaryKey( Integer.parseInt( (String)req.getTemplateParameter( 2 ) ) );
			}
		catch ( NumberFormatException nfex )
			{
			return null;
			}
		return id;
		}

	
	static final boolean use_tt_tags=false;
	
	private String formatEntryText( LogBookEntry entry, boolean force_plain, boolean suppress_link, int max_length )
		throws BuildingServerException
		{
		int i, j;
		char c;
		boolean shortened=false;
		boolean stripped=false;
		boolean is_html;
		
		String preprocessed = entry.getEntry();
		
		// start by detecting HTML markup
		is_html=( preprocessed.indexOf( "<html>" ) >=0 || 
				  preprocessed.indexOf( "<HTML>" ) >=0    );
		
		// the processed output
		StringBuffer buffer = new StringBuffer( preprocessed.length() + preprocessed.length()/10 );
		
		if ( is_html )
			{
			if ( !force_plain )
				// ignore max length if we are returning HTML
				return preprocessed.toString();
				
			// strip out the tags for later processing as plain text
			stripped = true;
			boolean in_tag=false, in_quote=false;
			for ( i=0; i<preprocessed.length(); i++ )
				{
				c = preprocessed.charAt( i );
				if ( in_quote )
					{
					if ( c=='\"' )
						in_quote=false;
					}
				else
					{
					if ( in_tag )
						{
						if ( c=='>' )
							in_tag=false;
						if ( c=='\"' )
							in_quote=true;
						}
					else
						{
						if ( c=='<' )
							{
							in_tag=true;
							// best to add some whitespace in place of
							// tag to avoid words getting concatenated
							buffer.append( " " );
							}
						else
							buffer.append( c );
						}
					}
				}
			preprocessed = buffer.toString();
			buffer = new StringBuffer( preprocessed.length() + preprocessed.length()/10 );
			}

		// trim down to size if required
		if ( preprocessed.length()>max_length )
			{
			for ( i=max_length; i>(max_length/2); i++ )
				if (  Character.isWhitespace( preprocessed.charAt( i ) ) )
					break;
			preprocessed = preprocessed.substring( 0, i );
			shortened=true;
			}
		
		// mark up with tags to reproduce intended line breaking etc.
		buffer.append( "<p>" );
		if ( use_tt_tags )
			buffer.append( "<tt>" );
		for ( i=0; i< preprocessed.length(); i++ )
			{
			if ( preprocessed.regionMatches( i, "http:",   0, 5 ) ||
					preprocessed.regionMatches( i, "ftp:",    0, 4 ) ||
					preprocessed.regionMatches( i, "mailto:", 0, 7 ) )
				{
				buffer.append( "<a target=\"_new\" href=\"" );
				for ( j=i; j<preprocessed.length(); j++ )
					{
					c=preprocessed.charAt( j );
					if ( Character.isWhitespace( c ) )
						break;
					buffer.append( preprocessed.charAt( j ) );
					}
				buffer.append( "\">" );
				for ( j=i; j<preprocessed.length(); j++ )
					{
					c=preprocessed.charAt( j );
					if ( Character.isWhitespace( c ) )
						break;
					buffer.append( preprocessed.charAt( j ) );
					}
				buffer.append( "</a>" );
				i=j;
				}
			if ( i>=preprocessed.length() )
				break;

			c = preprocessed.charAt( i );
			switch ( c )
				{
				case '"':
					buffer.append( "&quot;" );
					break;
				case '&':
					buffer.append( "&amp;" );
					break;
				case '<':
					buffer.append( "&lt;" );
					break;
				case '>':
					buffer.append( "&gt;" );
					break;
				case '\n':
					if ( use_tt_tags )
						buffer.append( "</tt></p>\n<p><tt>" );
					else
						buffer.append( "</p>\n<p>" );
					break;
				default:
					buffer.append( c );
				}
			}
		if ( use_tt_tags )
			buffer.append( "</tt>" );
		buffer.append( "</p>" );

		if ( !suppress_link )
			{
			if ( stripped )
				{
				buffer.append( "<p><em>You can view the <a href=\"" );
				buffer.append( entry.getLogBookEntryId() );
				buffer.append( "/bs_template_logbookentry.html\">full formatted text</a> of this entry.</em></p>" );
				}
			else if ( shortened )
				{
				buffer.append( "<p><em>There is <a href=\"" );
				buffer.append( entry.getLogBookEntryId() );
				buffer.append( "/bs_template_logbookentry.html\">more text.</a></em></p>" );
				}
			}
		
		return buffer.toString();	
		}

        /** Check whether an uploaded file is present in an entry; if so, try to upload the file into the log entry.
         * @return a description of the state of the upload. An empty String denotes success.
         */
        private String uploadToLog( Request req, PrintWriter out )
		throws IOException
		{
		StringBuffer dest_filename=new StringBuffer();
		String file, file_name, mime_type;
		BuildingSession session;
		PrimaryKey id;
		UploadedFileSummary summary;

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

		if ( file_name == null || file_name.length() == 0 )
			{
                        /* No file specified - warn the user. */
			return "Please specify a file to upload";
			}

		
		dest_filename.append( fileParameter( req ) );
		if ( dest_filename.length()>0 )
			dest_filename.append( "/" );
                dest_filename.append(req.getUserId().toString() + "/");

		try
			{
   			session = BuildingSessionManagerImpl.getSession( req.getResource() );
   			if ( session==null )
   				throw new BuildingServerException( "Unable to access the destination resource." );

			if ( file!=null && file_name!=null )
				{
				if ( file_name.indexOf( '/' )>=0 || file_name.indexOf( '\\' )>=0 )
					{
					return "File names may not contain any slash or backslash characters.";
					}
				dest_filename.append( file_name );
				
				mime_type=req.getServletContext().getMimeType( file_name.toLowerCase() );
				if ( mime_type==null )
					   mime_type="application/octet-stream";

				summary = session.getUploadedFileSession().transferFile( file, dest_filename.toString(), mime_type );
				}
			}
		catch ( Exception ex )
			{
			logException( out, "LogBookFacility", "uploadToLog", 
			        "A technical problem occured.",
				ex );
                        return "A technical problem occured.";
			}
                return "";
		}

        /**
	 * Outputs a HTML paragraph of files in the given path.
	 *
	 * @param req The building HTTP request.
	 * @param out The output stream.
	 * @exception java.io.IOException Thrown if there is a problem outputting the HTML.
	 */
	protected void fileparagraph( Request req, PrintWriter out, String basepath, boolean hide_deleted, String mode, String linked_url)
		throws IOException, ServletException
		{
		Enumeration enumeration;
		int i, j, n;
		StringBuffer address= new StringBuffer();
		BuildingSession session;
		UploadedFileSummary[] summaries;
		UploadedFileSummary parent;
                boolean first = true;
                boolean atLeastOneFile = false;

		Vector path = new Vector();

		if ( basepath==null )
			basepath = "/";
		if ( !basepath.endsWith( "/" ) )
			basepath = basepath + "/";
		if ( !basepath.startsWith( "/" ) )
			basepath = "/" + basepath;
        
        basepath = basepath + req.getUserId().toString() + "/";

		try
			{
  			session = BuildingSessionManagerImpl.getSession(req.getResource());
			summaries = session.getUploadedFileSession().getFileAndDescendentSummaries( basepath, hide_deleted );
			}
		catch ( Exception ex )
			{
			out.println( "<P>Technical Problem finding file list<BR>" + ex );
			return;
			}

                if ( summaries.length == 0 )
                        {
                        summaries = new UploadedFileSummary[1];
                        UploadedFile uploadedFile = new UploadedFile();
                        uploadedFile.setFlags( UploadedFileSummary.FLAG_FOLDER );
                        summaries[0] = uploadedFile.getSummary();
                        }

                path.addElement( summaries[0] );

                if ( mode.equals( PARAGRAPH_MODE_OPTION ) )
                        {
                        out.print( "<option value=\"\"" );
                        if (null == linked_url) out.print( " selected=\"selected\"" );
                        out.println( ">--No file to attach--</option>");
                        }

                for ( i=1; i<summaries.length; i++ )
			{
			// remove unwanted parts from path
			// work down path until reach a parent of current node
			for ( j=path.size()-1; j>=0; j-- )
				{
				parent = (UploadedFileSummary)path.elementAt(j);
				if ( summaries[i].getLeftIndex() > parent.getLeftIndex() &&
					 summaries[i].getRightIndex() < parent.getRightIndex()  )
					break;
				}
			// j indexes parent or is -1
			path.setSize( j+1 );
			path.addElement( summaries[i] );
			
			if ( !summaries[i].isFolder() )
				{
                                atLeastOneFile = true;
				address.setLength( 0 );
				address.append( req.absoluteURL() );
				address.setLength( address.length()-1 );  //to trim off last slash
				address.append( basepath );
				for ( j=1; j<path.size(); j++ )
					{
					parent = (UploadedFileSummary)path.elementAt(j);
					if ( j>1 )
						address.append( "/" );
					address.append( parent.getUrl() );
					}
                                if ( mode.equals( PARAGRAPH_MODE_OPTION ) )
                                        {
                                        out.print( "<option value=\"" + address.toString() + "\"" );
                                        if ( null != linked_url && linked_url.equals( address.toString() ) ) out.print( " selected=\"selected\"" );
                                        out.println( ">" + summaries[i].getName() + "</option>");
                                        }
                                else
                                        {
                                        if (first)
                                                first = false;
                                        else
                                                out.print( "<br>" );
        				out.print( "<a target=\"_blank\" href=\"" );
                                        out.print( address.toString() );
                                        if ( mode.equals( PARAGRAPH_MODE_DELETE ) )
                                            out.print( "/bs_template_filedelete.html" );
                                        out.print( "\">" );
                                        out.print( "<nobr>" );

                                        if ( summaries[i].isDeleted() )
                                                out.print( "<font color=\"gray\">" );

                                        out.print( summaries[i].getName() );

                                        if ( summaries[i].isDeleted() )
                                                out.print( "</font>" );

                                        out.print( "</nobr>" );

                                        out.print( "</a>" );
                                        }
				}
			}
                if (!atLeastOneFile)
                        out.print("There are currently no files attached to your log.");
		}

        /**
	 * Outputs a HTML paragraph of files in the given path.
	 *
	 * @param req The building HTTP request.
	 * @param out The output stream.
	 * @exception java.io.IOException Thrown if there is a problem outputting the HTML.
	 */
	protected boolean filesContain( Request req, PrintWriter out, String basepath, String candidate)
		throws IOException, ServletException
		{
		Enumeration enumeration;
		int i, j, n;
		StringBuffer address= new StringBuffer();
		BuildingSession session;
		UploadedFileSummary[] summaries;
		UploadedFileSummary parent;

		Vector path = new Vector();

		if ( basepath==null )
			basepath = "/";
		if ( !basepath.endsWith( "/" ) )
			basepath = basepath + "/";
		if ( !basepath.startsWith( "/" ) )
			basepath = "/" + basepath;
                basepath = basepath + req.getUserId().toString() + "/";
                log.debug("basepath is " + basepath);
                log.debug("candidate is " + candidate);

                try
                        {
                        session = BuildingSessionManagerImpl.getSession(req.getResource());
                        summaries = session.getUploadedFileSession().getFileAndDescendentSummaries( basepath, true );
                        }
		catch ( Exception ex )
			{
			logException( out, "LogBookFacility", "filesContain", 
			        "A technical problem occured getting the file list.",
				ex );
			return false;
			}

                if ( summaries.length == 0 )
                        {
                        summaries = new UploadedFileSummary[1];
                        UploadedFile uploadedFile = new UploadedFile();
                        uploadedFile.setFlags( UploadedFileSummary.FLAG_FOLDER );
                        summaries[0] = uploadedFile.getSummary();
                        }

                path.addElement( summaries[0] );

                log.debug("Entries: " + summaries.length);
                for ( i=1; i<summaries.length; i++ )
			{
			// remove unwanted parts from path
			// work down path until reach a parent of current node
			for ( j=path.size()-1; j>=0; j-- )
				{
				parent = (UploadedFileSummary)path.elementAt(j);
				if ( summaries[i].getLeftIndex() > parent.getLeftIndex() &&
					 summaries[i].getRightIndex() < parent.getRightIndex()  )
					break;
				}
			// j indexes parent or is -1
			path.setSize( j+1 );
			path.addElement( summaries[i] );
			
			if ( !summaries[i].isFolder() )
				{
				address.setLength( 0 );
				address.append( req.absoluteURL() );
				address.setLength( address.length()-1 );  //to trim off last slash
				address.append( basepath );
				for ( j=1; j<path.size(); j++ )
					{
					parent = (UploadedFileSummary)path.elementAt(j);
					if ( j>1 )
						address.append( "/" );
					address.append( parent.getUrl() );
					}
                                if ( address.toString().equals( candidate ) )
                                    log.debug( "address is " + address.toString() );
                                    return true;
				}
			}
                log.warn("No matches, returning false" );
                return false;
		}

        /**
	 * Outputs a set of options (to go inside a HTML select) listing users and their IDs.
         * The listed users are, or could be, visitors to the user's log book page.
	 *
	 * @param req The building HTTP request.
	 * @param out The output stream.
         * @param lb_session The user's session with the logbook.
         * @param possible if true, list users who are not visitors but could be. If false, list current visitors.
	 * @exception java.io.IOException Thrown if there is a problem outputting the HTML.
	 */
	protected void peers( Request req, PrintWriter out, LogBookSession lb_session, boolean possible)
		throws IOException, ServletException
		{
                try
                        {
                        int i;
                        PrimaryKey user_id = req.getUserId();
                        LogBookPage page = lb_session.getPageForUser( user_id );
                        if ( null == page )
                                {
                                out.print( "<option value=\"\">In order to share</option>");
                                out.print( "<option value=\"\">your log with other</option>");
                                out.print( "<option value=\"\">users, please add</option>");
                                out.print( "<option value=\"\">an entry first.</option>");
                                return;
                                }
                        Vector users = lb_session.getPeers( page, possible );

                        if ( 0 == users.size () )
                                out.print( "<option value=\"\">No users found</option>");
                        else
                                for ( i = 0; i < users.size(); i++ )
                                        {
                                        User u = (User) users.get( i );
                                        out.print( "<option value=\"" + u.getUserId().toString() + "\">" + u.getName() + "</option>");
                                        }
                        }
		catch ( Exception ex )
			{
			logException( out, "LogBookFacility", "peers", 
			        "A technical problem occured.",
				ex );
                        return;
			}
                }

        /**
	 * Outputs a set of options (to go inside a HTML select) listing users and their IDs.
         * The listed users are, or could be, visitors to the user's log book page.
	 *
	 * @param req The building HTTP request.
	 * @param out The output stream.
         * @param lb_session The user's session with the logbook.
	 * @exception java.io.IOException Thrown if there is a problem outputting the HTML.
	 */
	protected void amendpeers( Request req, PrintWriter out, LogBookSession lb_session, boolean add, String user_id_string)
		throws IOException, ServletException
		{
                try
                        {
                        PrimaryKey user_id = new PrimaryKey( Integer.parseInt( user_id_string ) );
                        LogBookPage page = lb_session.getPageForUser( req.getUserId() );
                        if ( null == page )
                                return;

                        if ( add )
                                {
                                lb_session.addVisitor( page, user_id );
                                out.println( "<p>Visitor added.</p>");
                                }
                        else
                                {
                                lb_session.removeVisitor( page, user_id );
                                out.println( "<p>Visitor removed.</p>");
                                }
                        }
		catch ( Exception ex )
			{
			logException( out, "LogBookFacility", "amendpeers", 
			        "A technical problem occured.",
				ex );
                        return;
			}
                }
	}
