/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

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

package org.bodington.servlet.facilities;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import org.bodington.servlet.*;
import org.bodington.servlet.HttpSession;

import java.awt.Color;

import java.io.*;
import java.util.*;
import java.util.Date;
import java.util.zip.*;
import java.sql.*;

import javax.servlet.*;
import javax.servlet.http.*;

import java.net.URL;
import java.net.HttpURLConnection;

import java.security.cert.*;

import org.bodington.assessment.Questionnaire;
import org.bodington.database.*;
import org.bodington.server.*;
import org.bodington.server.events.*;
import org.bodington.server.realm.*;
import org.bodington.server.resources.*;
import org.bodington.servlet.facilities.StyleSheets.StyleSheetSessionData;
import org.bodington.sqldatabase.SqlDatabase;
import org.bodington.servlet.template.Template;
import org.bodington.xml.XMLMetadataUtils;
import org.bodington.xml.XMLRepository;
import org.bodington.xml.XMLQuery;
import org.bodington.xml.XMLObjectRecord;
import org.bodington.util.BodingtonURL;
import org.bodington.util.CSVWriter;
import org.bodington.util.ColourPreferenceMapper;
import org.bodington.util.DateFormatter;
import org.bodington.util.ResourceBundleHelper;
import org.bodington.util.TextUtils;
import org.bodington.util.TinyUrl;
import org.bodington.util.html.HtmlFilterFactory;
import org.bodington.util.html.nodevisitors.ReportingVisitor;
import org.htmlparser.Parser;



/**
 * Class responsible for interfacing between the HTML world and the Bodington one.
 * This class also has the concept of the building/floors/rooms which should be 
 * split out from the taglib like functionality.
 * @author Jon Maber
 */
public class Facility implements ResourceCreator
	{
	
    private static Logger log = Logger.getLogger(Facility.class);

    X509CertificateBuilder x509_cert_builder=null;
	
    static boolean copy_in_progress=false;
    static Object copy_synch = new Object();

    /**
     * Resource bundle for constants.
     */
    protected ResourceBundle bundle; 
    
    /**
     * The ID for this facility. This is used to link a facility to a resource
     * and is set by {@link FacilityList} during startup.
     */
	public Integer id;
    
	/**
	 * Store the short name for this facility.
	 * This is set by {@link FacilityList} during startup.
	 */
	public String facilityname;
    
    private List contains;
	
	protected StyleSheets styleSheet = new StyleSheets();

	public Facility()
		{
		facilityname="building";
		}


    /**
     * Display the Exception to the user and write it to the logs.
     * If the Exception is a BuildingServerUserException then it doesn't get logged.
     * @param out The PrintWriter to output the HTML to.
     * @param sourceClassName The class that this error occurred in.
     * @param sourceMethodName The method this error ocurred in (unused).
     * @param message The message to display (unused).
     * @param th The Exception to display to the user/log.
     */
    public void logException( PrintWriter out, String sourceClassName, String sourceMethodName, String message, Throwable th )
	{
	BuildingServerException bsex=null;
	if ( th instanceof BuildingServerException )
	    bsex = (BuildingServerException)th;
	
    // BuildingServerUserExceptions aren't serious and so shouldn't be logged.
    if (! (th instanceof BuildingServerUserException) )
        log.log(sourceClassName, Level.ERROR, th.getMessage(), th );

	if ( out!=null )
	    {
	    out.println( "<p align=\"center\">" );
	    if ( bsex!=null )
		out.println( bsex.friendlyMessage() );
	    else
		out.println( th.getMessage() );
	    out.println( "</p>" );
	    out.println( "<br />" );
	    }
	}	
		
		
    public String getTitle()
        {
        return null;
        }

    public String getTitle( boolean capital, boolean article )
        {
        return getTitle( capital, article, false );
        }
        
    public String getTitle( boolean capital, boolean article, boolean plural )
        {
        String t = getTitle();
        if ( t==null ) return null;
        
        StringBuffer buffer = new StringBuffer( t.length() + 4 );
        char first = Character.toLowerCase( t.charAt( 0 ) );
        
        if ( article )
            {
            buffer.append( capital?"A":"a" );
            if ( first == 'a' || first == 'e' || first == 'i' || first == 'o' || first == 'u' )
                buffer.append( "n" );
            buffer.append( " " );
            }
        
        
        if ( capital && !article )
            first = Character.toUpperCase( first );
            
        buffer.append( first );
        buffer.append( t.substring( 1 ) );
        
        if ( plural )
            buffer.append( "s" );
        
        return buffer.toString();
        }

	/* (non-Javadoc)
     * @see org.bodington.servlet.facilities.ResourceCreator#newResource()
     */
	public Resource newResource()
		{
		return new Resource();
		}
		
	/*
     * (non-Javadoc)
     * @see org.bodington.servlet.facilities.ResourceCreator#initResource(org.bodington.servlet.Request,
     *      org.bodington.server.resources.Resource)
     */
    public List initResource(HttpServletRequest request, Resource newResource)
    {
        String url = getParamValue(request, "url");
        String title = getParamValue(request, "title");
        String description = getParamValue(request, "description");
        String introduction = getParamValue(request, "introduction");
        String parentAcl = request.getParameter("parentacl");

        newResource.setName(url);
        newResource.setUseParentAcl(parentAcl != null && parentAcl.length() > 0);
        try 
        {
            newResource.setTitle((title != null)?EscapedHtmlWriter.filter(title):null);
            newResource.setDescription(description);
            newResource.setIntroduction(introduction);
        }
        catch (BuildingServerException bse)
        {
            // TODO: This is an evil hack because setters shouldn't do DB work.
            throw new RuntimeException("Failed to setup resource.", bse);
        }
        return Collections.EMPTY_LIST;
    }


    /**
     * Facility subclasses can refuse copying of resources.
     * @return True if resources that use this facility can be copied.
     */
    public boolean canCopy( Resource resource )
    {
        // if here then resource is either served by Facility or by a subclass
        // that doesn't override this method.
        FacilityList facilityList = FacilityList.getFacilities();
        Facility facility = facilityList.get( new Integer( resource.getHttpFacilityNo() ) );
        // if resource is served by Facility itself then allow copy
        if ( facility.equals(Facility.class.getClass()) )            
            return true;
        // if resource is served by subclass of Facility but that subclass doesn't
        // overide this method then refuse copy operation.  Subclass must override
        // this method to allow copying.
        return false;
    }
    
    /**
     * Looks through resource children.....
     * @return True if resources that use this facility can be copied.
     */
    public boolean canCopyWithChildren( Resource resource )
    throws BuildingServerException
    {
    if ( !canCopy( resource ) )
        return false;

    if ( !resource.checkPermission( Permission.MANAGE ) )
        return false;
    
    FacilityList facilityList = FacilityList.getFacilities();
    Facility facility = null;
    Resource child;

    Enumeration enumeration = resource.findChildren();
    while ( enumeration.hasMoreElements() )
    {
        child = (Resource)enumeration.nextElement();
        facility = facilityList.get( new Integer( child.getHttpFacilityNo() ) );
        if ( !facility.canCopyWithChildren( child ) )
        return false;
    }

    return true;
    }    
    
    /* (non-Javadoc)
     * @see org.bodington.servlet.facilities.ResourceCreator#initResource(org.bodington.server.resources.Resource, org.bodington.server.resources.Resource)
     */
	public boolean initResource( Resource original, Resource new_resource)
		throws BuildingServerException
	        {
	        //default init routine does nothing but
	        //other facilities can check/copy specific properties.
		return true;
		}


    /**
     * Copy the content of one resource to another.
     * @param original 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 new_resource, Request breq  )
		throws BuildingServerException
		{
		//default routine does nothing but
		//other facilities can copy specific content.
		}

	public void userevent(  Request req, PrintWriter out )
		throws IOException
		{
		String user_message, notify;
		
        user_message=req.getParameter( "user_message" );
        if ( user_message!=null  )
        	user_message = user_message.trim();
        	
        if ( user_message==null || user_message.length()==0 )
        	{
        	out.println( "You have to enter a message to generate an event." );
        	return;
        	}

		if ( user_message.length()>250 )
			{
			user_message = user_message.substring( 0, 250 );
			out.println( "<P>Your message will be truncated because it is too long.</P>" );
			}

        notify=req.getParameter( "notify" );
		
		
		User user = (User)BuildingContext.getContext().getUser();
		UserEvent event = new UserEvent( UserEvent.EVENT_USER_EVENT,
										 req.getResource().getResourceId(),
										 user.getUserId(),
										 user_message,
										 (notify!=null && notify.length()>0) );
        event.save();
			
		out.println( "<P>The event was generated.</P>" );
		}

    /**
     * Display the events for the current location in a paged format.
     * This doesn't check the permission of every event.
     * @param req The request which contains the resource to get the events
     * for and the page to display.
     * @param out The Writer to which the HTML is sent
     * @see Event#checkPermission().
     */
	public void eventlog(  Request req, PrintWriter out )
		throws IOException
		{
		Event event;
		PrimaryKey event_id;
		StringBuffer where, order;
        final int PAGE_SIZE = 30;
        boolean prev, next;

		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE) )
			{
			out.println( "<HR>You need manage access to view the log.<HR>" );
			return;
			}
        
        int page = 0;
        try
        {
            page = Integer.parseInt(req.getParameter("page"));
        }
        catch (Exception e){}
		
		try
			{
			where = new StringBuffer();
			where.append( "resource_id = " );
			where.append( req.getResource().getResourceId().toString() );
			where.append( " AND importance >= " );
			where.append( Event.IMPORTANCE_MANAGEMENT_MIN );
            
            order = new StringBuffer();
            order.append("event_time desc ");
            order.append("LIMIT ");
            order.append(PAGE_SIZE + 1);
            order.append(" OFFSET ");
            order.append(page*PAGE_SIZE);
            
			
			Enumeration enumeration = Event.findEventPrimaryKeys( where.toString(), order.toString());
			if (enumeration.hasMoreElements())
			{
			    out.println( "<TABLE CLASS=bs-table-opaque>" );
			    for(int j = 0;  enumeration.hasMoreElements() && j < PAGE_SIZE;  )
			    {
			        event_id = (PrimaryKey)enumeration.nextElement();
			        event=Event.findEvent( event_id );
			        if ( event== null )
			            continue;
                    out.println("<TR><TD>");
                    event.printMessage( out, true );
                    out.println( "<br/><span style='font-size: smaller'>" );
                    out.print( DateFormatter.formatDate( event.getEventTime(), DateFormatter.DEFAULT ) );
                    out.println( "</span>" );
			        out.flush();
			        out.println( "</TD></TR>" );
			        j++;
			    }
			    out.println( "</TABLE>" );
			    
			    prev = (page != 0);
			    next = enumeration.hasMoreElements();
			    
			    if (prev)
			        out.print("<a href='?page="+ (page-1)+ "'>");
			    out.print("prev");
			    if (prev)
			        out.print("</a>");
			    out.print( " | ");
			    if (next)
			        out.print("<a href='?page="+ (page+1)+ "'>");
			    out.print("next");
			    if (next)
			        out.print("</a>");
			    
			    
			}
			else
				out.println( "<P>There are no events in the event log here.</P>" );
			}
		catch ( Exception ex )
			{
            log.warn("Failed to get event list.", ex);
			out.println( "</TD></TR></TABLE><HR>There was a technical problem trying to get a list of events.<HR>" );
			out.println( ex.toString() );
			return;
			}
		
		}
    
    private static Map dayOptions = Utils.createMap(new Object[][]{
        {new Integer(1), "day"},
        {new Integer(7), "week"},
        {new Integer(31), "month"}
    });
    
    /**
     * Prints out the people who have recently used this recourse and how
     * long ago they did.
     * @param req The request in which we look for the value
     * <code>days</days> to limit the number of days in the past we look. 
     * The accepted range for this value is 1 to 31 inclusive. 
     * @param out The PrintWriter to write the HTML to.
     */
    public void accesslog( Request req, PrintWriter out)
    {
        final long MILLI_SECONDS_PER_DAY = 86400000L;
        if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE) )
        {
            out.println( "<HR>You need manage access to view the access log.<HR>" );
            return;
        }
        
        int days = 7;
        try
        {
            days = Integer.parseInt(req.getParameter("days"));
        }
        catch (Exception e){}
        out.println("<form method='get'>");
        out.print("Display the last ");
        Utils.writeSelect(out, "days", dayOptions, new Integer(days));
        out.println("<input type='submit' value='Show'/>");
        out.println("</form>");
        // Bound days between 1 and 31
        days = (days < 1)?1:((days>31)?31:days);
        long from = System.currentTimeMillis() - (days * MILLI_SECONDS_PER_DAY);
        final Map lastAccessed = new HashMap();
        try
        {
            Enumeration events = NavigationEvent.findEvents(req.getResource().getPrimaryKey(), from );
            while (events.hasMoreElements())
            {
                Event event = (NavigationEvent)events.nextElement();
                PrimaryKey userId = event.getActiveUserId();
                Timestamp last = (Timestamp)lastAccessed.get(userId);
                if (last == null)
                {
                    lastAccessed.put(userId, event.getEventTime());
                }
                else
                {
                    if (last.before(event.getEventTime()))
                    {
                        lastAccessed.put(userId, event.getEventTime());
                    }
                }
            }
            Object keys[] = (lastAccessed.keySet().toArray());
            Arrays.sort(keys, new Comparator() {
                public int compare(Object o1, Object o2)
                {
                    return ((Comparable)lastAccessed.get(o2)).compareTo(lastAccessed.get(o1));
                }
            });
            out.println("<table class='bs-table-opaque'>");
            for(int index = 0; index < keys.length; index++)
            {
                out.print("<tr><td>");
                if (keys[index] == null)
                    out.print("Unknown User");
                else
                    out.print(User.findUser((PrimaryKey)keys[index]).getName());
                out.println("<br/>");
                out.print("<span style='font-size: smaller'>");
                out.println(DateFormatter.formatDuration(System.currentTimeMillis() -((Timestamp)lastAccessed.get(keys[index])).getTime()));
                out.print(" ago");
                out.print("</span>");
                out.print("</td></tr>");
            }
            out.println("</table>");
            out.println("<br/>");
            out.println("Download <a href='bs_virtual_accesslog.csv?days="+days+"'>accesslog.csv</a>");
        }
        catch (BuildingServerException bse)
        {
            log.warn("Problem loading objects from database.", bse);
        }  
    }
    
    /**
     * Send a CSV file of the access log.
     */
    public void sendAccesslog ( Request req, HttpServletResponse resp) throws IOException
    {
        
        final long MILLI_SECONDS_PER_DAY = 86400000L;
        if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE) )
        {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You need manage access");
            return;
        }
        resp.setContentType("text/comma-separated-values");
        resp.setHeader("Content-Disposition","attachment; filename=\"accesslog.csv\"");
        CSVWriter out = new CSVWriter(resp.getWriter());
        int days = 7;
        try
        {
            days = Integer.parseInt(req.getParameter("days"));
        }
        catch (Exception e){}
        // Bound days between 1 and 31
        days = (days < 1)?1:((days>31)?31:days);
        long from = System.currentTimeMillis() - (days * MILLI_SECONDS_PER_DAY);
        try
        {
            Enumeration events = NavigationEvent.findEvents(req.getResource().getPrimaryKey(), from );
            while (events.hasMoreElements())
            {
                NavigationEvent event = (NavigationEvent)events.nextElement();
                User user = event.getActiveUser();
                out.write(DateFormatter.formatDate(event.getEventTime(),
                    DateFormatter.DEFAULT));
                out.write((user == null)?"Unknown User":user.getName());
                out.writeln();
            }
        }
        catch (BuildingServerException e)
        {
            log.warn("Problem loading events.", e);
        }
    }

	public boolean clearCacheEntry( Object d, boolean force )
		{
		//no cached data so no clean up to do
		//sub classes will have to over ride this do do fancy stuff
		return true;
		}

	public void init( Integer i, ResourceCreator def, List contains )
		{
		id=i;
		//deffacility=def;
        try
        {
            bundle = ResourceBundle.getBundle("facility-"+ facilityname);
        }
        catch (MissingResourceException mre)
        {
            log.error("Couldn't find resource bundle for:"+ facilityname);
        }
        this.contains = new ArrayList(contains.size());
        this.contains.addAll(contains);
		}

	public boolean generateFile( Request req, HttpServletResponse res )
		throws ServletException, IOException
		{
		// generate a file, or select a previously generated file
		
		try
			{
			log.debug( "Generated file requested: " + req.getPageName() );
			if ( req.getPageName().equals( "filelisting.xml" ) )
				{
				return generateFileListing( req, res );
				}
			else if ( req.getPageName().equals( "menu.xml" ) )
				{
				return generateResourceMenu( req, res );
				}
			else if ( req.getPageName().equals( "metadata.xml" ) )
				{
				log.debug( "Generated file metadata.xml requested." );
				File d = req.getResource().getGeneratedFileFolder();
				File man = new File( d, "metadata.xml" );
				
				if ( !man.exists() )
					{
					PrintWriter writer = new PrintWriter( new FileWriter( man ) );
					writer.println( "Just testing!" );
					writer.close();
					}
				
				res.setContentType( "text/plain" );
				
				return true;
				}
			}
		catch ( BuildingServerException bsex )
			{
			log.error(bsex.getMessage(), bsex );
			throw new ServletException( bsex.getMessage() );
			}

			
		return false;
		}

		
	public boolean generateFileListing( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		BuildingSession session;

		log.debug( "Generated file filelisting.xml requested." );

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

		session.generateFileListing();

		log.debug( "Generated file filelisting.xml generated." );

		res.setContentType( "text/plain" );
		return true;
		}
		
	public boolean generateResourceMenu( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		BuildingSession session;

		log.debug( "Generated file menu.xml requested." );

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

		session.generateResourceMenu();

		log.debug( "Generated file menu.xml generated." );

		res.setContentType( "text/plain" );
		return true;
		}
		
		
		
	public void sendVirtualFile( Request req, HttpServletResponse res )
		throws ServletException, IOException
		{
		try
		    {
		    if ( req.getPageName().equals( "author.css" ) )
			{
			styleSheet.sendAuthorStyleSheet( req, res );
			}
		    // user style sheets may have varying names to force the browser to reload when
		    // options are changed.
		    else if ( req.getPageName().startsWith( "user" ) && req.getPageName().endsWith( ".css" ) )
			{
			styleSheet.sendUserStyleSheet( req, res );
			}
		    else if ( req.getPageName().startsWith( "auto" ) && req.getPageName().endsWith( ".css" ) )
			{
			styleSheet.sendAutoStyleSheet( req, res );
			}
            else if ( req.getPageName().equals( "logout.html" ) )
            {
             logout(req, res);
            }
		    else if ( req.getPageName().equals( "uploadconfirm.txt" ) )
			{
			sendUploadConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "menuconfirm.txt" ) )
			{
			sendMenuConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "createfolderconfirm.txt" ) )
			{
			sendCreateFolderConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "renameconfirm.txt" ) )
			{
			sendRenameConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "deleteconfirm.txt" ) )
			{
			sendDeleteConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "undeleteconfirm.txt" ) )
			{
			sendUndeleteConfirmation( req, res );
			}
		    else if ( req.getPageName().equals( "userclientcert.cer" ) )
			{
			sendUserClientCert( req, res );
			}
            else if ( req.getPageName().equals("accesslog.csv"))
            {
            sendAccesslog(req, res);
            }
		  //uhi:awc zips up webDocument content packages in resource tree
            else if ( req.getPageName().equals( "imsCP_WebDocuments.zip" ) )
            {
            ResourceUtils.sendWebDocumentContentPackages(req, res);
            }
            else
			{
			res.setContentType( "text/plain" );
			PrintWriter writer = new PrintWriter( res.getWriter() );

			writer.println( "ERROR" );
			writer.print( "Unknown virtual file requested: " );
			writer.println( req.getPageName() );
			writer.close();
			}
			}
		catch ( BuildingServerException bsex )
			{
            if (!(bsex instanceof BuildingServerUserException))
                log.error( bsex.getMessage(), bsex );
			try
			    {
			    res.setContentType( "text/plain" );
			    PrintWriter writer = new PrintWriter( res.getWriter() );

			    writer.println( "ERROR" );
			    writer.println( bsex.friendlyMessage() );
			    writer.close();
			    }
			catch ( Throwable th )
			    {
			    throw new ServletException( th.getMessage() );
			    }
			}
		}

        
    public void writeClientX509( Request request, PrintWriter writer )
    throws IOException
    {
        Object obj = request.getAttribute( "javax.servlet.request.X509Certificate" );
        if ( obj == null )
        {
            writer.println( "No client X509 certificate in page request." );
            return;
        }
        if ( !(obj instanceof java.security.cert.X509Certificate[]) )
        {
            writer.println( "Request attribute had wrong data type." );
            return;
        }
        
        java.security.cert.X509Certificate[] certs = (java.security.cert.X509Certificate[])obj;
        for ( int i=0; i<certs.length; i++ )
        {
            writer.println( "Certificate chain item " + i );
            writer.println( certs[i].toString() );
        }
    }
        
    public void sendUserClientCert( Request req, HttpServletResponse res )
    throws IOException, BuildingServerException
    {
        NavigationSession nav_session = req.getServerNavigationSession();
        if ( !nav_session.isAuthenticated() )
        {
            res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "User is not authenticated" );
            return;
        }
        if ( nav_session.isAnonymous() )
        {
            res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Can't issue certificate to anonymous user." );
            return;
        }

        User user = nav_session.getAuthenticatedUser();
        
        String pubkey = req.getParameter( "public_key" );
        if ( pubkey==null || pubkey.length() <= 0 )
        {
            res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "No public key was supplied by the web browser." );
            return;
        }
        
        if ( x509_cert_builder == null )
        {
            String kstr = req.getServletContext().getRealPath( "WEB-INF/ca.keystore" );
            x509_cert_builder = new X509CertificateBuilder( kstr );
            x509_cert_builder.init();
        }
        
        X509Certificate client_cert;
        byte[] encoded_cert=null;

        try
        {
            UserX509 user_x509 = new UserX509();
            user_x509.setUserId( user.getUserId() );
            // save to generate primary key
            user_x509.save();
            
            client_cert = x509_cert_builder.createClientCertificate( 
                user.getName(),
                1000*60*60, 
                user_x509.getUserX509Id().intValue(),
                pubkey );

            user_x509.setX509Certificate( client_cert );
            user_x509.setSerialNumber( user_x509.getUserX509Id().intValue() );
            user_x509.setDistinguishedName( client_cert.getSubjectDN().getName() );
            user_x509.save();
            
            encoded_cert = client_cert.getEncoded();
        }
        catch ( Exception e )
        {
            log.error( e.getMessage(), e );
            res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Problem creating certificate." );
            return;
        }

        if ( encoded_cert == null || encoded_cert.length==0 )
        {
            res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Generated certificate was empty." );
            return;
        }
        
        res.setContentType( "application/x-x509-user-cert" );
        ServletOutputStream out = res.getOutputStream();


        System.err.println( "cert length: " + encoded_cert.length );

        out.write( encoded_cert );
        out.close();
    }

        
        public void sendProcessedGif( org.bodington.servlet.Request request, org.bodington.servlet.Response response )
    throws java.io.IOException, javax.servlet.ServletException
    {
        // this methods simply forwards to specialised servlet with appropriate query string.
        String name = request.getPageName();
        // can tell with this kind of request what type of page the GIF was
        // embedded in so make an assumption based on GIF name. 
        String html_body_class = name.startsWith( "navigation_" )?"bodington_navigation_page":"";
        String url = getTemplateGifUrl( request, html_body_class, "bs_template_" + name );
        
        // instruct client to fetch the graphic on a different URL and allow client
        // to cache this instruction for 10 seconds which should be long enough to
        // reduce hits when a page contains multiple references to the same graphic
        response.setStatus( HttpServletResponse.SC_TEMPORARY_REDIRECT, "Your browser should have forwarded you to the correct URL." );
        response.setDateHeader( "Expires", System.currentTimeMillis() + (1000*60*5) );
        response.setHeader( "Location", url );
        response.flushBuffer();
    }
	
	
	
	
	public void resourcePropertyField( Request req, PrintWriter out, String name )
	throws IOException, ServletException
        {
            try
            {
                if ( name == null )
                    return;
                BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
                String val = session.getSpecifiedProperty( name );
                out.print( "<input name=\"" );
                out.print( name );
                out.print( "\" value=\"" );
                if ( val != null )
                    out.print( val );
                out.print( "\">" );
            }
            catch ( BuildingServerException bsex )
            {
                log.error( bsex.getMessage(), bsex );
                throw new ServletException( bsex.getMessage() );
            }
        }
        
        public void resourcePropertyConfirm( Request req, PrintWriter out, String name )
	throws IOException, ServletException
        {
           
            try
            {
                if ( name == null )
                    return;
                String val = req.getParameter( name );
                if ( val == null )
                    return;
                if (!req.getResource().checkPermission(Permission.SYSADMIN))
                {
                    out.print("<p>You need sysadmin permission to change this.</p>");
                    return;
                }
                
                BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );

                if ( val == null || val.length()==0 )
                    session.unspecifyProperty( name );
                else
                    session.specifyProperty( name, val );
                out.print( "<p>Saved property.</p>" );
            }
            catch ( BuildingServerException bsex )
            {
                log.error( bsex.getMessage(), bsex );
                throw new ServletException( bsex.getMessage() );
            }
        }
        
        public void sendMenuConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		String file = req.getParameterFileLocation( "file" );
		
		PrintWriter writer = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );
		
		if ( file == null || file.length()==0 )
		    {
    		writer.println( "ERROR" );
    		writer.println( "No file was sent to the server." );
            return;    		
		    }
   		    BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
   		    if ( session==null )
   			    throw new BuildingServerException( "Unable to access the destination resource." );

    	    session.transferResourceMenu( file );
   		
   		writer.println( "OK" );
		}
		
	public void sendCreateFolderConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		String newname = req.getParameter( "folder_name" );
		
		PrintWriter writer = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );
		
		if ( newname == null || newname.length()==0 )
		    {
    		writer.println( "ERROR" );
    		writer.println( "No folder name was sent to the server." );
            return;    		
		    }

		if ( newname.length()>255 )
			{
    		writer.println( "ERROR" );
			writer.println( "Name is too long." );
			return;
			}
			
		if ( newname.indexOf( '/' ) >=0 || newname.indexOf( '\\' ) >=0 )
			{
    		writer.println( "ERROR" );
			writer.println( "Name cannot contain forward or backward slashes." );
			return;
			}
		
			
		BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
		if ( session==null )
			throw new BuildingServerException( "Unable to access the destination resource." );

		String base = fileParameter( req );
		if ( base.length()>0 && !base.endsWith( "/" ) )
		    base = base + "/";
		session.getUploadedFileSession().createFolder( base + newname );
   		
   		writer.println( "OK" );
		}
		
	public void sendRenameConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		String newname = req.getParameter( "file_name" );
		
		PrintWriter writer = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );
		
		if ( newname == null || newname.length()==0 )
		    {
    		writer.println( "ERROR" );
    		writer.println( "No file name was sent to the server." );
            return;    		
		    }

		if ( newname.length()>255 )
			{
    		writer.println( "ERROR" );
			writer.println( "Name is too long." );
			return;
			}
			
		if ( newname.indexOf( '/' ) >=0 || newname.indexOf( '\\' ) >=0 )
			{
    		writer.println( "ERROR" );
			writer.println( "Name cannot contain forward or backward slashes." );
			return;
			}
		
			
		BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
		if ( session==null )
			throw new BuildingServerException( "Unable to access the destination resource." );

		session.getUploadedFileSession().renameFile( fileParameter( req ), newname );
   		
   		writer.println( "OK" );
		}
		
		
	public void sendDeleteConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		PrintWriter writer = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );
		
		BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
		if ( session==null )
			throw new BuildingServerException( "Unable to access the destination resource." );

		session.getUploadedFileSession().deleteFile( fileParameter( req ) );
   		
   		writer.println( "OK" );
		}
		
	public void sendUndeleteConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		String recurse = req.getParameter( "recurse" );
		
		PrintWriter writer = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );
		
		BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
		if ( session==null )
			throw new BuildingServerException( "Unable to access the destination resource." );

		session.getUploadedFileSession().undeleteFile( fileParameter( req ), recurse != null && recurse.length() > 0 );
   		
   		writer.println( "OK" );
		}
		
	public void sendUploadConfirmation( Request req, HttpServletResponse res )
		throws IOException, BuildingServerException
		{
		PrintWriter out = new PrintWriter( res.getWriter() );
	    res.setContentType( "text/plain" );

		StringBuffer dest_filename=new StringBuffer();
		String file, file_name;
		BuildingSession session;
		UploadedFileSummary summary;

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

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

		
		dest_filename.append( fileParameter( req ) );
		if ( dest_filename.length()>0 )
			dest_filename.append( "/" );
		
		
		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 )
				{
				out.println( "ERROR" );
				out.println( "File names may not contain any slash characters." );
				return;
				}
			dest_filename.append( file_name );

			summary = session.getUploadedFileSession().transferFile( file, dest_filename.toString(), null );

			out.println( "OK" );

		UserFileEvent event = new UserFileEvent(UserFileEvent.EVENT_UPLOAD,
                req.getResource(), summary);
			event.save();
			}
		}
		
    /**
     * Insert some output within the context of the current request. This method
     * will typically perform some logic and then write some content to the
     * writer object. Typically, this method is invoked from legacy templates
     * when a <code>building</code> element is encountered. The value of the
     * <code>command</code> attribute corresponds to the <code>command</code>
     * parameter of this method. Likewise for the (optional) <code>name</code>
     * attribute. The <code>command</code> parameter typically refers to an
     * internal method of the same name. The <code>name</code> parameter is
     * intended as an additional (optional) parameter that can be passed to the
     * command and as such its semantics are dependant on the command in
     * question.
     * @param req the request object.
     * @param out The writer object, this <strong>will</strong> be <code>null</code>
     * if the command is inside an if statement that isn't being executed.
     * @param command the name of the command.
     * @param name an additional parameter to pass to the command.
     * @see org.bodington.servlet.template.LegacyTemplate
     */
	public void insert( Request req, PrintWriter out, String command, String name )
		throws ServletException, IOException
		{

		log.debug( getClass().getName() );
		//if ( !getClass().getName().equals( "org.bodington.servlet.facilities.Facility" ) )
		//	return "<!Error - facility insert (forgot to include insert method in facility?)>\n";

		if ( command.equalsIgnoreCase( "CREATE" ) )
			{
			if ( out!=null )
				createconfirm( req, out, name, true );
			return;
			}

		if ( command.equalsIgnoreCase( "modify" ) )
			{
			if ( out!=null )
				confirmmodify( req, out );
			return;
			}
		if ( command.equalsIgnoreCase( "MOVE" ) )
			{
			if ( out!=null )
				moveconfirm( req, out );
			return;
			}
		if ( command.equalsIgnoreCase( "copy" ) )
			{
			if ( out!=null )
				copyconfirm( req, out );
			return;
			}

		if ( command.equalsIgnoreCase( "delete" ) )
			{
			if ( out!=null )
				deleteconfirm( req, out );
			return;
			}
        
        if ( command.equalsIgnoreCase( "deletewarn" ) )
            {
            if ( out!=null )
                deletewarn(req, out);
            return;
            }

		if ( command.equalsIgnoreCase( "VARIABLE" ) )
			{
			if ( out!=null )
				variable( req, out, name, false );
			return;
			}

		if ( command.equalsIgnoreCase( "VARIABLEF" ) )
			{
			if ( out!=null )
				variable( req, out, name, true );
			return;
			}

		if ( command.equalsIgnoreCase( "NAVIGATION" ) )
			{
			if ( out!=null )
			    {
			    navigation( req, out, name );
			    }
			return;
			}

		if ( command.equalsIgnoreCase( "resourcemenu" ) )
			{
			if ( out!=null )
			    {
			    resourceMenu( req, out );
				 }
			return;
			}

		if ( command.equalsIgnoreCase( "stylesheet" ) )
			{
			if ( out!=null )
				styleSheet.stylesheet( req, out );
			return;
			}
		
		if ( command.equalsIgnoreCase( "javascript" ) )
			{
			if ( out != null )
				javascript ( req, out );
			return;
			}

		if ( command.equalsIgnoreCase( "usercreatelist" ) )
			{
			if ( out != null )
				writeCreateMenu(out, req, false);
			return;
			}
        if ( command.equalsIgnoreCase( "admincreatelist" ) )
        {
        if ( out != null )
            writeCreateMenu(out, req, true);
        return;
        }
        
		if ( command.equalsIgnoreCase( "userevent" ) )
			{
			if ( out!=null )
				userevent( req, out );
			return;
			}

		if ( command.equalsIgnoreCase( "eventlog" ) )
			{
			if ( out!=null )
				eventlog( req, out );
			return;
			}

        if ( command.equalsIgnoreCase("reorder") )
            {
			if ( out!=null )
				reorder( req, out );
		    return;
            }

        if ( command.equalsIgnoreCase("reordercontrol") )
            {
			if ( out!=null )
				reordercontrol( req, out );
		    return;
            }


		if (  command.equalsIgnoreCase( "ifcanreorder" ) )
			{
			ifcanreorder( req );
			return;
			}

		if (  command.equalsIgnoreCase( "ifloggedin" ) )
			{
			ifloggedin( req );
			return;
			}

		if (  command.equalsIgnoreCase( "IFACCESS" ) )
			{
			ifaccess( req, name );
			return;
			}

		if (  command.equalsIgnoreCase( "ELSE" ) )
			{
			req.setSwitchedOff(  !req.isSwitchedOff() );
			return;
			}

		if (  command.equalsIgnoreCase( "ENDIF" ) )
			{
			req.setSwitchedOff( false );
			return;
			}

		if (  command.equalsIgnoreCase( "parentaclform" ) )
			{
			if ( out!=null )
				parentaclform( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "acl" ) )
			{
			if ( out!=null )
				acl( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "aclentry" ) )
			{
			if ( out!=null )
				aclentry( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "addaclentry" ) )
			{
			if ( out!=null )
				addaclentry( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "accessoperation" ) )
			{
			if ( out!=null )
				accessoperation( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "groupmenu" ) )
			{
			if ( out!=null )
				groupmenu( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "groupoperation" ) )
			{
			if ( out!=null )
				groupoperation( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "rawfileindex" ) )
			{
			if ( out!=null )
				{
				rawfileindex( req, out );
				}
			return;
			}
			
		if (  command.equalsIgnoreCase( "fileindex" ) )
			{
			if ( out!=null )
				{
				boolean show_root = req.getInsertAttribute( "root", "no" ).equalsIgnoreCase( "yes" );
				boolean hide_deleted = req.getInsertAttribute( "hidedeleted", "yes" ).equalsIgnoreCase( "yes" );
				int link_type = 0;
				if ( req.getInsertAttribute( "link", "download" ).equalsIgnoreCase( "info" ) )
					link_type=1;
                String target = req.getInsertAttribute( "target", null );
				fileindex( req, out, null, show_root, hide_deleted, link_type, target );
				}
			return;
			}
			
		if (  command.equalsIgnoreCase( "fileinfo" ) )
			{
			if ( out!=null )
				fileinfo( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "filerename" ) )
			{
			if ( out!=null )
				filerename( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "fileisdeleted" ) )
			{
			req.setSwitchedOff(  !fileisdeleted( req ) );
			return;
			}

		if (  command.equalsIgnoreCase( "foldercreate" ) )
			{
			if ( out!=null )
				foldercreate( req, out );
			return;
			}

		if (  command.equalsIgnoreCase( "upload" ) )
			{
			if ( out!=null )
			    {
			        if ("expand".equalsIgnoreCase( name ))
			            uploadzip( req, out );
			        else
			            upload( req, out, ("raw").equalsIgnoreCase(name) );
			    }
			return;
			}
			
		if (  command.equalsIgnoreCase( "filedelete" ) )
			{
			if ( out!=null )
				filedelete( req, out, true );
			return;
			}

		if (  command.equalsIgnoreCase( "fileundelete" ) )
			{
			if ( out!=null )
				filedelete( req, out, false );
			return;
			}

		if (  command.equalsIgnoreCase( "metadata" ) )
			{
			if ( out!=null )
				metadata( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "metadataupload" ) )
			{
			if ( out!=null )
				metadataupload( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "metadatasearch" ) )
			{
			if ( out!=null )
				metadatasearch( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "importresource" ) )
			{
			if ( out!=null )
				importresource( req, out, name );
			return;
			}
			
		if (  command.equalsIgnoreCase( "generatemanifest" ) )
			{
			if ( out!=null )
				generatemanifest( req, out );
			return;
			}
        
        if (  command.equalsIgnoreCase( "refresh" ) )
        {
            if ( out!=null )
            {
                outputPostSessionRefresh(req, out);
            }
            return;
            }
        	
		if (  command.equalsIgnoreCase( "zonecontrol" ) )
			{
			if ( out!=null )
				zonecontrol( req, out );
			return;
			}
			
		if (  command.equalsIgnoreCase( "zoneconfirm" ) )
			{
			if ( out!=null )
				zoneconfirm( req, out );
			return;
			}
        if ( command.equalsIgnoreCase( "approot" ) )
            {
            if ( out!=null )
                approot(req, out);
            return;
            }
        // uhi:awc: added insert command to return username
  		if ( command.equalsIgnoreCase( "username" ) )
			{
            if ( out!=null )
            {
                String username = getUsername(req);
                if ( username == null )
                    username = "anonymous";
                out.print( username );
            }
			return;
            }
  		if (command.equalsIgnoreCase("uploadlimit"))
  		{
  		    if ( out != null )
  		        uploadLimit( req, out );
  		    return;
  		}
        if (command.equalsIgnoreCase("tinyurl"))
        {
            if ( out != null )
                tinyUrl( req, out );
            return;
        }
        if (command.equalsIgnoreCase("allowedtags"))
        {
            if ( out != null )
                allowedTags( req, out );
            return;
        }
        // uhi:awc: added insert command to return url with username appended to query - url supplied by insertname
  		if ( command.equalsIgnoreCase( "usernameloginurl" ) )
			{
            if ( out!=null )
            {
                String loginUrl = getUsernameLoginUrl(req, name);
                out.print( loginUrl );
            }
			return;
            }
			
        // <TimedRelease>
        if ( command.equalsIgnoreCase( "releasedate" ) )
        {
            if ( out != null ) 
                releaseDate( req, out, name );
            return;
        }
                        /*
                         * WebLearn inserted code; A Corfield 02/12/2003.
                         */
                        if (  command.equalsIgnoreCase( "fileparse" ) )
                        {
//                        not yet implemented
                          if ( out!=null )
                            out.println("<CENTER><B>Sorry, parsing bodington links in html files is not yet available!</B></CENTER>");
//                            check mime type - must be html
//                            fileparse( req, out );
                          return;
                        }
		if ( out!=null )
		    out.println( "<!Unknown base facility insert>\n" );
		}


    /**
     * Outputs a meta tag that refreshes just after the session is cleaned up.
     * Because the HttpSession cleaner only runs every 10 minutes (at the 
     * moment) we add on a little extra so that the cleaner has run.
     * @param req The Request.
     * @param out The output write to print the HTML to.
     */
    private void outputPostSessionRefresh(Request req, PrintWriter out)
    {
        HttpSession session = (HttpSession)req.getSession();
        long time = ((System.currentTimeMillis() - session
            .getLastAccessedTime()) / 1000)
            + session.getMaxInactiveInterval() + 60 * 15;
        out.println("<meta http-equiv=\"refresh\" content=\""+time+"\"/>");
    }

		
    /**
     * Write out the root of the webapp. This is useful in building links.
     * Example output would be something like /bodington
     * @param req The servlet request.
     * @param out The outputstream to write it to.
     * @see HttpServletRequest#getContextPath()
     */
	private void approot(Request req, PrintWriter out)
    {
        out.write(req.getContextPath());  
    }


    private void tinyUrl(Request req, PrintWriter out)
    {
    	/* The parameter is passed in the Javascript link but not in the standard link,
         * this is to stop crawlers from creating 1000s of tinyurls automatically. */
        if (req.getParameter("create") != null) 
        {
            TinyUrl tiny = new TinyUrl();
            BodingtonURL url = new BodingtonURL(req);
            String resourceUrl = url.getResourceUrl(req.getResource());
            log.debug("Getting tiny URL for "+ resourceUrl);
            String tinyUrl = tiny.shorten(resourceUrl);
            if (tinyUrl == null)
            {
                log.info("Problem getting tiny URL: "+ tiny.getError());
                out.print("Problem creating URL");
            }
            else
                out.print(tinyUrl);
        }
        else
        {
            out.print("<form method='GET'><input type='submit' name='create' value='Create'></form>");
        }
    }


    /**
	 * Outputs the current upload limit. Actually is the request
	 * limit.
     * @param req The Request.
     * @param out Where we write the current limit.
     */
    public void uploadLimit(Request req, PrintWriter out)
    {
        //out.print(Request.MAX_INPUT+ "MB");
    }

	public boolean isMatchingUserAgent( Request request, String regex )
	throws ServletException
	{
	    String agent = request.getHeader( "user-agent" );

	    if ( regex == null || agent == null )
		return false;
	    
	    try
	    {
		java.util.regex.Pattern p = java.util.regex.Pattern.compile( regex );
		java.util.regex.Matcher m = p.matcher( agent );
		return m.matches();
	    }
	    catch ( Exception e )
	    {
	        log.error( e.getMessage(), e );
	        throw new ServletException( e.getMessage() );
	    }
	    
	}
	
// Just print out the url of the original link - based on loginOriginalLink
// Weblearn: Added by Paul T 11/05/04

        public void loginOriginalLinkUrlOnly ( Request req, PrintWriter out, String label )
        {
          String original_path = loginEntryPath( req );
          out.print( original_path );
        }
// end addition by Paul T

	
	public void loginSendAuthenticationChallenge( Response response )
	{
	    response.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
	    response.setHeader( "WWW-authenticate", "Basic realm=\"Bodington Web Site\"" );
	}
	
	/**
     * Get the login entry path. This will look in the request, and then in the
     * session. If not found, it will just use the request path.
     * @param request the request.
     * @return the login entry path.
     */
	public String  loginEntryPath( Request request )
	{
	    // look for a form field first
	    String original_path = request.getParameter( "org_bodington_servlet_entry_path" );
	    if ( original_path!=null && original_path.length()>0 )
		return original_path;

	    BodingtonURL bodUrl = new BodingtonURL(request);
        return bodUrl.getResourceUrl(request.getResource());
	}
	
	/**
     * Add the login entry path to the page. This will add the request path as a
     * hidden variable within the current page.
     * @param req the request.
     * @param out the object to write the response to.
     * @throws IOException
     */
	public void loginShowPathField(  Request req, PrintWriter out )
	    throws IOException
	{
	    String original_path = loginEntryPath( req );
		
	    out.print( "<input type=\"hidden\" name=\"org_bodington_servlet_entry_path\" value=\"" );
	    if ( original_path.length()>0 )
		out.print( original_path );
	    out.println( "\">" );
	}
	
	/**
     * Write a login form to the response.
     * @param req the request.
     * @param out the object to write the response to.
     * @param protocol the protocol being used, e.g. <code>http</code> or
     *        <code>https</code>.
     * @param action the handler for the form, e.g.
     *        <code>bs_template_login_step1.html</code>.
     */
	public void loginShowFormTag( Request req, PrintWriter out, String protocol, String action )
	{
        loginOpenFormTag( req, out, protocol, action, "post" );
	}
	
    /*
     * WebLearn additions. [28/02/05] Alexis O'Connor. --->>> 
     * 
     * Numerous methods
     * added to get WebLearn / WebAuth login to work. These methods are generic
     * servlet related functions as opposed to the existing methods, many of
     * which encapsulate business logic.
     */
    
    /**
     * Is the parameter true. The value of a parameter is <code>true</code> if
     * it is present and equal to <code>YES</code>, otherwise it is
     * <code>false</code>.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return <code>true</code> if the value of the parameter is
     *         <code>YES</code>, otherwise <code>false</code>.
     */
    public boolean isParameterTrue( Request request, String name )
    {
        String value = request.getParameter( name );
        return (value == null) ? false : "YES".equals( value );
    }
    
    /**
     * Indicates whether the parameter is present in the request. This method
     * just checks to see whether a given parameter is present in the request,
     * regardless of it's actual value. This method is case-sensitive.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return <code>true</code> if the value of the specified parameter is
     *         not <code>null</code>, otherwise <code>false</code>.
     */
    public boolean isParameterPresent(Request request, String name)
    {
        return request.getParameter( name ) != null;
    }

    /**
     * Hide the specified parameter. This will write an HTML input control of
     * the type <code>hidden</code>. This will hide the specified parameter,
     * acquiring its value from the current request. This should be called
     * within the body of an HTML form.
     * @param request the request object.
     * @param writer the writer object (output stream).
     * @param name the name of the parameter.
     * @param escape specify whether or not to escape the <code>parameter</code>
     * for HTML entities.
     */
    public void hideParameter( Request request, PrintWriter writer, String name, boolean escape )
    {
        String value = request.getParameter( name );
        hideParameter( writer, name, value, escape );
    }

    /**
     * Hide the specified parameter. This will write an HTML input control of
     * the type <code>hidden</code>. This should be called within the body of
     * an HTML form.
     * @param writer the writer object (output stream).
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     * @param escape specify whether or not to escape the <code>parameter</code>
     * for HTML entities.
     */
    public void hideParameter( PrintWriter writer, String name, String value, boolean escape )
    {
    	value = escape ? EscapedHtmlWriter.filter(value) : value;
        writer.print( "<input type=\"hidden\" name=\"" + name + "\" value=\""
            + value + "\"/>" );
    }
    
    /**
     * Hide all of the parameters. This takes all the parameters that it can
     * find in the request and outputs them as hidden variables. Each parameter
     * name-value pair is output as an HTML input control of the type 
     * <code>hidden</code>. This must be called within the body of an HTML
     * form.
     * @param request the request object.
     * @param writer the writer object (output stream).
     * @param escape specify whether or not to escape the <code>parameter</code>
     * for HTML entities.
     */
    public void hideParameters( Request request, PrintWriter writer, boolean escape )
    {
        Enumeration parameters = request.getParameterNames();
        while ( parameters.hasMoreElements() )
        {
            String name = (String) parameters.nextElement();
            hideParameter( writer, name, request.getParameter( name), escape );
        }
    }

    /**
     * Checks to see if the specified username and password are OK. Intended
     * purely as a debugging method. Essentially if
     * <code>password.equals("iam" + username)</code> then <code>true</code>
     * is returned.
     * @param username the username.
     * @param password the corresponding password.
     * @return <code>true</code> if the username and password match, otherwise
     *         false.
     */
    public boolean isUsernameAndPasswordGoodToGo( String username,
        String password )
    {
        return password.equals( "iam" + username );
    }

    /**
     * Send a redirect request to the client user agent.
     * @param response the response object
     * @param path the target path of the redirect.
     */
    public void redirect( Response response, String path )
    {
        try
        {
            response.sendRedirect( response.encodeRedirectURL( path ) );
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
    }

    /**
     * Get the value of the specified parameter. Where a parameter has no
     * matching value, the blank string (<code>""</code>) is returned.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return the value of the requested parameter.
     */
    public String getParameter( Request request, String name )
    {
        String value = request.getParameter( name );
        return (value != null) ? value : "";
    }

    /**
     * Write a textfield object. This will write an HTML input control of the
     * type <code>text</code>. This should be called within the body of an
     * HTML form.
     * @param writer the writer object (output stream).
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     */
    public void textField( PrintWriter writer, String name, String value )
    {
        writer.print( "<input type=\"text\" name=\"" + name + "\" value=\""
            + value + "\"/>" );
    }

    /**
     * Append a query to the specified path. This will append the specified
     * name-value query pair to the path whether or not query parameters already
     * exist.
     * @param path the current target URL.
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     * @return a path with the specified name-value query pair appended to it.
     */
    public String appendQuery( String path, String name, String value )
    {
        path += (path.lastIndexOf( '?' ) == -1) ? '?' : '&';
        path += name + '=' + value;
        return path;
    }

    /**
     * Set a value within the current session.
     * @param request the request object.
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     */
    void setSessionValue( Request request, String name, Object value )
    {
        javax.servlet.http.HttpSession session = request.getSession();
        request.setAttribute( name, value );
    }

    /**
     * Get a value from within the current session. If no matching value is
     * found, then <code>null</code> is returned.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return the session value matching the name, or <code>null</code> if
     *         none exists.
     */
    Object sessionValue( Request request, String name )
    {
        javax.servlet.http.HttpSession session = request.getSession();
        return session.getAttribute( name );
    }

    /**
     * Set a value within the current session.
     * @param request the request object.
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     */
    public void setSessionValue( Request request, String name, boolean value )
    {
        setSessionValue( request, name, value ? Boolean.TRUE : Boolean.FALSE );
    }

    /**
     * Set a value within the current session.
     * @param request the request object.
     * @param name the name of the parameter.
     * @param value the value of the parameter.
     */
    public void setSessionValue( Request request, String name, String value )
    {
        setSessionValue( request, name, (Object)value );
    }

    /**
     * Get a value from within the current session. If no matching value is
     * found, then the blank string (<code>""</code>) is returned.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return the session value matching the name, or <code>""</code> if none
     *         exists.
     */
    public String getSessionValue( Request request, String name )
    {
        Object value = sessionValue( request, name );
        return (value != null) ? (String)value : "";
    }

    /**
     * Get a value from within the current session. If no matching value is
     * found, then the value <code>false</code> is returned.
     * @param request the request object.
     * @param name the name of the parameter.
     * @return the session value matching the name, or <code>false</code> if
     *         none exists.
     */
    public boolean isSessionValue( Request request, String name )
    {
        Object value = sessionValue( request, name );
        return (value != null) ? ((Boolean)value).booleanValue() : false;
    }

    /**
     * Is the name a user in the system (Bodington). This checks to see whether
     * the specified name is that of a user that Bodington already knows about.
     * @deprecated Does this presentation layer need this?
     * @param username the name of the user.
     * @return <code>true</code> if this user is a name in the system,
     *         otherwise <code>false</code>.
     */
    public boolean isSystemUser( String username )
    {
        try
        {
            return PassPhrase.findPassPhraseByUserName( username ) != null;
        }
        catch ( BuildingServerException e )
        {
            return false;
        }
    }

    /**
     * Is the name a user in the WebAuth system. This checks to see whether the
     * specified name is that of a user that WebAuth knows about. The possible
     * values are:
     * <ul>
     * <li><code>YES</code>: they are an Oxford / WebAuth user.
     * <li><code>NO</code>: they are not an Oxford / WebAuth user.
     * <li><code>FAIL</code>: there was some problem with the Delmonte
     * service.
     * </ul>
     * <p>
     * <i>(WebLearn (addition) [03/03/05] Alexis O'Connor).</i>
     * @param username the name of the user.
     * @return either the value <code>YES</code>, <code>NO</code> or
     *         <code>FAIL</code>.
     */
    public String delmonteSay( String username )
    {
        if (isWebAuthEnabled())
        {
            try
            {
                return WebAuthUser.findWebAuthUserByUserName( username ) != null ? "YES" : "NO";
            }
            catch ( BuildingServerException e )
            {
                return "FAIL";
            }
        }
        else
            return "NO";
    } 		

    /**
     * Indicates whether this application is utilizing WebAuth for
     * authentication.
     * @return <code>true</code>if the application is utilizing WebAuth for
     *         authentication, otherwise <code>false</code>.
     */
    public boolean isWebAuthEnabled()
    {
        return "true".equalsIgnoreCase( BuildingContext.getProperty(
            "weblearn.webauth.authentication", "true" ) );
    }

	/**
     * Write a login form to the response. 
     * @param req the request.
     * @param out the object to write the response to.
     * @param protocol the protocol being used, e.g. <code>http</code> or
     *        <code>https</code>. If the Bodington installation doesn't have https
     *        we fall back to http.
     * @param action the handler for the form, e.g.
     *        <code>bs_template_login_step1.html</code>.
     * @param method the http method, e.g. <code>post</code> or <code>get</code>.
     */
	public void loginOpenFormTag( Request req, PrintWriter out, String protocol, String action, String method )
	{
        // target="_top" ensures that following login page fills window
        // may be important if user ends up in sub frame by accident when they
        // log in.
	    out.print( "<form name=\"login\" method=\"" + method + "\" target=\"_top\" action=\"" );

	    BodingtonURL bodingtonURL = new BodingtonURL(req);

	    try
        {
            if ("https".equalsIgnoreCase(protocol))
            {
                bodingtonURL.setHttps(Integer.parseInt(BuildingContext
                    .getProperty("buildingservlet.port.https")));
            }
            else
            {
                bodingtonURL.setHttp(Integer.parseInt(BuildingContext
                    .getProperty("buildingservlet.port.http")));
            }
        }
        catch (NumberFormatException nfe)
        {
        }
        
        out.print( bodingtonURL.getResourceUrl(null));

	    if ( action!=null )
		out.print( action );
	    out.print( "\">" );
	}
    
    public void loginLink (Request req, PrintWriter out, String name, String page, String protocol, String target, boolean includeEntry )
    {
        BodingtonURL bodingtonURL = new BodingtonURL(req);
        try
        {
            if ("https".equalsIgnoreCase(protocol))
            {
                bodingtonURL.setHttps(Integer.parseInt(BuildingContext
                    .getProperty("buildingservlet.port.https")));
            }
            else
            {
                bodingtonURL.setHttp(Integer.parseInt(BuildingContext
                    .getProperty("buildingservlet.port.http")));
            }
        }
        catch (NumberFormatException nfe)
        {
        }
        
        String url = bodingtonURL.getResourceUrl(req.getResource())+ page
            +"?org_bodington_servlet_entry_path="+ loginEntryPath(req);
        if (includeEntry)
            url += req.getAttribute(Request.REDIRECT_ATTRIBUTE);

        hyperlink(out, url, name, target);
    }
    

    /**
     * Write a closing form tag.
     * @param out the PrintWriter object.
     */
    public void loginCloseFormTag( PrintWriter out )
    {
        out.print("</form>");
    }

    /**
     * Is the user of the current request authenticated via WebAuth. Examines
     * the session associated with the current request to see if the
     * corresponding user was authenticated via WebAuth.
     * <p>
     * <i>(WebLearn (addition) [03/03/05] Alexis O'Connor).</i>
     * @param request the request object.
     * @return true if the user authenticated via WebAuth, otherwise false.
     */
    public boolean isWebAuthAuthenticated( Request request )
    {
        try
        {
            NavigationSession session = request.getServerNavigationSession();
            return session.isAuthenticated()
                && WebAuthAuthenticator.ALIAS.equals(session
                    .getAuthenticatorAlias());
        }
        catch (BuildingServerException e)
        {
            log.error("Should Never Happen", e);
        }
        return false;
    }	

    /**
     * Write a hyperlink. This will write an HTML 'A' element.
     * @param writer the writer object (output stream).
     * @param href the URI of the desination web resource.
     * @param content the text of the link.
     */
    public void hyperlink( PrintWriter writer, String href, String content )
    {
        writer.print( "<a href=\"" + href + "\">" + content + "</a>" );
    }

    /**
     * Write a hyperlink. This will write an HTML 'A' element.
     * @param writer the writer object (output stream).
     * @param href the URI of the desination web resource.
     * @param content the text of the link.
     * @param target the target frame for the destination.
     */
    public void hyperlink( PrintWriter writer, String href, String content,
        String target )
    {
        writer.print( "<a href=\"" + href + "\" target=\"" + target + "\">"
            + content + "</a>" );
    }
    
    /**
     * Indicates whether the current resource is at the <em>default URL</em>.
     * The default URL is the URL that is built by concatenating the
     * <em>name</em> of resources in their hierarchical structure to form an
     * absolute URL. This method is intended as a override point for facilities
     * that handle resources for which there is effectively no logical default
     * URL, or put another way do not render a page in response to being accessed
     * via their default URL. These typically take the form of resources that
     * are rendered within the resource menu of another container resource.
     * <p>
     * <em>NOTE: This method is intended to be called from templates.</em>
     * </p>
     * @return <code>true</code> if the current resource is at the default
     *         URL, otherwise <code>false</code>.
     * @see Resource#getFullName()
     */
    public boolean isResourceAtDefaultURL()
    {
        return true;
    }
    
    /**
     * Write out a hyperlink to the current resource. This method will write
     * out a hyperlink which uses the <code>label</code> parameter as the 
     * label.
     * <p>
     * <em>NOTE: This method is intended to be called from templates.</em>
     * </p>
     * @param request the current request.
     * @param out an object to write to.
     * @param label the label for the hyperlink (the visible string).
     */
    public void linkToResource( Request request, PrintWriter out, String label )
    {
        /*
         * TODO: Under construction!
         */
        /*
         * NOTES: if templates could handle maps, then this method could have
         * a map argument, which would enable passing arbitrary quantities of
         * additional attributes such as target, title, etc as name-value pairs.
         * In the absence of which, attributes such as these have to be
         * "hard-wired" for the sake of pragmatism / sanity!
         */
        try
        {
            out.print("<a target=_top title=\"Current resource\" href=\"");
            out.print( request.getContextPath() );
            out.print( request.getServletPath() );
            out.print( request.getResource().getFullName() );
            out.print( "\">" + label + "</a>" );
        }
        catch ( BuildingServerException bsex )
        {
        }
    }
    
    /**
     * Write out a hyperlink to the parent resource. This method will write
     * out a hyperlink which uses the <code>label</code> parameter as the 
     * label.
     * <p>
     * <em>NOTE: This method is intended to be called from templates.</em>
     * </p>
     * @param request the current request.
     * @param out an object to write to.
     * @param label the label for the hyperlink (the visible string).
     */
    public void linkToParent( Request request, PrintWriter out, String label )
    {
        /*
         * NOTES: if templates could handle maps, then this method could have
         * a map argument, which would enable passing arbitrary quantities of
         * additional attributes such as target, title, etc as name-value pairs.
         * In the absence of which, attributes such as these have to be
         * "hard-wired" for the sake of pragmatism / sanity!
         */
        try
        {
            out.print("<a target=_top title=\"Parent resource\" href=\"");
            out.print( request.getContextPath() );
            out.print( request.getServletPath() );
            out.print( request.getResource().getParent().getFullName() );
            out.print( "\">" + label + "</a>" );
        }
        catch ( BuildingServerException bsex )
        {
        }
    }

    /*
     * <<<--- WebLearn additions. [28/02/05] Alexis O'Connor.
     */
    
    public boolean loginHelpRequested( Request request )
	{
	    String param = request.getParameter( "org_bodington_servlet_help" );
	    return param != null && param.length() > 0;
	}
	
	/**
     * Write a hyperlink to the entry path out to the print writer. The label
     * will take the value of the <code>label</code> parameter.
     * @param req the request.
     * @param out the object to write to.
     * @param label the label for the hyperlink.
     */
	public void loginOriginalLink( Request req, PrintWriter out, String label )
	{
	    String original_path = loginEntryPath( req );
	    out.print( "<a href=\"" );
	    out.print( original_path );
	    out.print( "\">" );
	    out.print( label );
	    out.print( "</a>" );
	}

	/**
     * Uses http url connection to external application for logout
     * uhi:awc
     * @param out The PrintWriter.
     * @param logoutUrl The url of external application for logout.
     */
	public void externalLogout( PrintWriter out, String logoutUrl )
	{
        int response = 0;
	    try
	    {
        URL url = new URL(logoutUrl);
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        response = connection.getResponseCode();
        //uhi:awc log response if not successful
        }
	    catch ( Exception e )
	    {
            log.info("External logout failed (URL: " + logoutUrl
                + " response: " + response + ") " + e.getMessage(), e);
        }
    }
    /**
     * Make the logout link always be https if the user is logged in through
     * WebAuth as the webauth module doesn't clean the cookes out otherwise.
     */
    public void logoutLink ( Request request, PrintWriter out )
    {
        out.print("<a href=\"");
        if (isWebAuthAuthenticated(request) && !request.isSecure())
        {
            BodingtonURL url = new BodingtonURL(request);
            int port = 443;
            try
            {
                port = Integer.parseInt(BuildingContext.getProperty("buildingservlet.port.https"));
            }
            catch (Exception e)
            {}
            url.setHttps(port);
            out.print(url.getResourceUrl(request.getResource()));
        }
        out.print("bs_virtual_logout.html\"");
        out.print(" target=\"_top\" style=\"color: black\" >Logout</a>");
    }
    
    private void logout ( Request req, HttpServletResponse resp ) throws IOException
    {
        boolean webAuth = isWebAuthAuthenticated( req );
        String logoutURL = BuildingContext.getProperty("weblearn.webauth.logout");
        boolean canLogout = !webAuth || ( logoutURL != null && logoutURL.length() > 0);
        if (canLogout)
        {    
            logout ( req, resp.getWriter() );
            if (webAuth)
                resp.sendRedirect(logoutURL);
            else
                resp.sendRedirect(new BodingtonURL(req).getResourceUrl(req.getResource()));
        }
        else 
            resp.sendRedirect("bs_template_logout_wa.html");
    }

	public void logout( Request req, PrintWriter out )
	{
	    try
	    {
		NavigationSession nav_session = req.getServerNavigationSession();
		if ( nav_session == null )
		    return;
		
		nav_session.clearAuthenticationCredentials();
		// This should clear up the HttpSession and prevent preferences
		// crossing sessions.
		// XXX: Check that this really is sensible
	    org.bodington.servlet.HttpSession session = (org.bodington.servlet.HttpSession)req.getSession( false );
	    if (session != null)
	        session.cleanout();
	    
	    }
	    catch ( Exception e )
	    {
	        if ( out != null )
	            out.print( "<p>It was not possible to log you out for technical reasons.</p>" );
	        log.error( e.getMessage(), e );
	    }
	    
	}
	
	
	private void parentaclform( Request breq, PrintWriter out )
		throws IOException
		{
		boolean is_owners, is_local;
		String name;
		
		if ( !breq.isAuthenticated() )
			{
			out.println( "<HR>You haven't checked in to the building so you can't control access.<HR>" );
			return;
			}
		
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
		    {
			out.println( "<HR>You need manage access to control access for others.<HR>" );
			return;
		    }


        out.println( "<table CLASS=bs-table-opaque>" );

        try
            {
		    Resource resource = breq.getResource();

		    out.print( "<TR><TD CLASS=bs-cell-special><FORM NAME=parentaclform method=post ACTION=bs_template_access.html>" );
		    out.print( "<input type=hidden name=operation value=parentacl>" );
			out.print( "<INPUT TYPE=CHECKBOX NAME=parentacl VALUE=yes" );
		    if ( resource.getUseParentAcl() )
		    	out.print( " CHECKED" );
		    out.println( " ONCLICK=\"this.form.submit()\">Access controlled by containing location " );
		    out.println( "<FONT SIZE=-1><NOBR>(Click for immediate change.)</NOBR></FORM></TD></TR>" );
		    }
		catch ( Exception ex )
		    {
		    out.println( "<PRE>" + ex + "</PRE>" );
		    return;
		    }
	    out.println( "</TABLE>" );

		}
		

	private void acl( Request breq, PrintWriter out )
		throws IOException
		{
		
		if ( !breq.isAuthenticated() )
			{
			out.println( "<HR>You haven't checked in to the building so you can't control access.<HR>" );
			return;
			}
		
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
		    {
			out.println( "<HR>You need manage access to control access for others.<HR>" );
			return;
		    }


        out.println( "<table border=1>" );
        out.println( "<tr>" );
        out.println( "<td><table CLASS=bs-table-acl border=0 cellpadding=4 cellspacing=0 bordercolor=#C0C0C0 bordercolordark=#808080 bordercolorlight=#FFFFFF>" );
        out.println( "<tr>" );
        out.println( "<td align=center valign=top><h3><strong>Add/<br>" );
        out.println( "Remove</strong></h3>" );
        out.println( "</td>" );
        out.println( "<td align=center valign=top><h3><strong>Group" );
        out.println( "<br>" );
        out.println( "Name</strong></h3>" );
        out.println( "</td>" );
        out.println( "<td align=center valign=top><h3><strong>Access" );
        out.println( "<br>" );
        out.println( "Rights</strong></h3>" );
        out.println( "</td>" );
        out.println( "<td align=center valign=top><h3><strong>Change<br>" );
        out.println( "Access</strong></h3>" );
        out.println( "</td>" );
        out.println( "</tr>" );

        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    //out.println( "<PRE>" + acl.toString() );
		    Enumeration enumeration = acl.entries();
		    AclEntry aclentry;
		    StringTokenizer tok;
		    while ( enumeration.hasMoreElements() )
		        {
		        aclentry = (AclEntry)enumeration.nextElement();
                Group group =aclentry.getGroup();

                out.println( "<tr>" );
                if ( group.isOwnersGroup())
                    out.println( "<td valign=top>&nbsp;</td>");
                else
                    {
                    out.print( "<td valign=top align=right>" );
                    out.print( "<form method=post ACTION=bs_template_access.html>" );
                    out.print( "<input type=hidden name=operation value=accessremove>" );
                    out.print( "<input type=hidden name=group_id value=" );
                    out.print( group.getGroupId().toString() );
                    out.println( "><INPUT TYPE=SUBMIT VALUE=\" - \"></FORM></FONT></TD>" );
                    }
                out.println( "<td align=right valign=top><table CLASS=bs-cell-special>" );
                out.println( "<tr>" );
                out.print( "<td valign=top><strong>" );
                if ( group.isLocalGroup() )
                    out.print( group.getLocalName() );
                else
                    out.print( group.getName() );
                out.print( "</strong><br>" );
                out.print( group.getDescription() );
                out.println( "<br>" );
                out.print( "<font size=2><form method=post ACTION=bs_template_accessgroup.html target=groupwindow>" );
                out.print( "<input type=hidden name=group_id value=" );
                out.print( group.getGroupId().toString() );
                out.println( "><INPUT TYPE=SUBMIT VALUE=\" Membership \" ONCLICK=\"grpwin()\"></FORM>" );
                //out.println( "<a href=membership.html><font size=2><em><strong>Membership</strong></em></font></a></td>" );
                out.println( "</font></a></td>" );
                out.println( "</tr>" );
                out.println( "</table>" );
                out.println( "</td>" );
                out.println( "<td valign=top><table CLASS=bs-cell-special>" );
                out.println( "<tr>" );
                out.print( "<td valign=top><strong>");
                tok= new StringTokenizer( aclentry.getPermissionCoded(), "+-," );
                while ( tok.hasMoreTokens() )
                	{
                	out.print( tok.nextToken() );
                	out.print( " " );
                	}
                out.println( "</strong></td>" );
                out.println( "</tr>" );
                out.println( "</table>" );
                out.println( "</td>" );
                out.println( "<td valign=top>" );
                out.println( "<font size=2><FORM METHOD=POST ACTION=bs_template_accesschange.html>" );
                out.print( "<INPUT TYPE=HIDDEN NAME=group_id VALUE=" );
                out.print( group.getGroupId().toString() );
                out.print( "><INPUT TYPE=SUBMIT VALUE=\" * \"></FORM></FONT>" );
                //out.print( "<a href=page/" );
                //out.print( aclentry.getGroup().getName() );
                //out.print( "/" );
                //out.print( "accesschange.html" );
                //out.println( "><font size=2><em><strong>Change</strong></em></font></a>" );
                out.println( "</td></tr>" );
		        
		        
		        //out.println( aclentry.getGroup().getName() + " = " + aclentry.getPermissionCoded() );
		        }
		    }
		catch ( Exception ex )
		    {
		    out.println( "<PRE>" + ex + "</PRE>" );
		    return;
		    }

        out.println( "<tr>" );
        out.println( "<td valign=top align=right><font size=2>" );
        out.println( "<form method=post ACTION=\"bs_template_accessadd.html\">" );
        out.println( "<input type=hidden name=base value=*>" );
        out.println( "<INPUT TYPE=SUBMIT VALUE=\" + \"></FORM></FONT></TD>" );
        out.println( "<td align=right valign=top><font size=2>You can list more groups here.</FONT></td>" );
        out.println( "<td valign=top>&nbsp;</td>" );
        out.println( "<td valign=top>&nbsp;</td>" );
        out.println( "</tr>" );
        out.println( "</table>" );
        out.println( "</td>" );
        out.println( "</tr>" );
        out.println( "</table>" );

		}
		

	private void aclentry( Request breq, PrintWriter out )
		throws IOException
		{
		boolean is_owners, is_local;
		String g, name;
		PrimaryKey group_id;
		
        g=breq.getParameter( "group_id" );
        if ( g==null )
			{
			out.println( "Problem: no group_id was specified." );
			return;
			}
		try
		    {
		    group_id = new PrimaryKey( Integer.parseInt( g ) );
		    }
		catch ( NumberFormatException nfex )
		    {
			out.println( "Problem: invalid group_id was specified." );
			return;
		    }
		    
		if ( !breq.isAuthenticated() )
			{
			out.println( "<HR>You haven't checked in to the building so you can't change access rights.<HR>" );
			return;
			}
		
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE) )
		    {
			out.println( "<HR>You need manage access rights to view access rights.<HR>" );
			return;
		    }

        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    //out.println( "<PRE>" + acl.toString() );
		    //Enumeration enumeration = acl.entries();
		    AclEntry aclentry;
            out.println( "<input type=hidden name=operation value=accesschange>" );
            out.print( "<input type=hidden name=group_id value=" );
            out.print( group_id.toString() ) ;
            out.println( " >" );
            aclentry=acl.getAclEntry( group_id, false );
            if ( aclentry==null )
                {
                out.println( "The selected access control entry no longer exists." );
                return;
                }
            Group group  =aclentry.getGroup();
            out.println( "<TR>" );
            out.println( "<td valign=top align=center><strong>" );
            if ( group.isLocalGroup() )
                out.print( group.getLocalName() );
            else
                out.print( group.getName() );
            out.println( "</strong></td>" );
            out.println( "<td valign=top>" );
            out.print( group.getDescription() );
            out.println( "</td>" );
            out.println( "<td>" );
            if ( group.isOwnersGroup() )
                {
                out.print( "<P>You cannot remove <B>see</B>, <B>view</B> or <B>manage</B> access from the owners group.</P>" );
                }
                
            Enumeration enumeration = Permission.permissions();
            Permission p;
            boolean first=true;
            while ( enumeration.hasMoreElements() )
                {
                p = (Permission)enumeration.nextElement();
                if ( group.isOwnersGroup() && ( p.equals( Permission.SEE ) || 
                                    p.equals( Permission.VIEW ) || 
                                    p.equals( Permission.MANAGE ) ) )
                    continue;
                if ( p.equals( Permission.ADMINISTER ) || p.equals( Permission.SYSADMIN ) )
                    {
                    if ( !resource.checkPermission( Permission.SYSADMIN ) )
                        continue;
                    }
                if ( !first ) out.println( "<BR>" );
                first=false;
                out.print( "<input type=checkbox " );
                if ( aclentry.checkPermission( p ) )
                    out.print( "checked " );
                out.print( "name=" );
                out.print( p.toString() );
                out.print( " value=on>" );
                out.print( "<em><strong>" );
                out.print( p.toString() );
                out.println( "</strong></em>" );
                }
                
            out.println( "</td></tr>" );
                
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( "<PRE>" + ex + "</PRE>" );
		    return;
		    }

		}

	
	private void addaclentry( Request breq, PrintWriter out )
		throws IOException
		{
		Group group;
		int i;
		
		if ( !breq.isAuthenticated() )
			{
			out.println( "<HR>You haven't checked in to the building so you can't change access rights.<HR>" );
			return;
			}
		
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE) )
		    {
			out.println( "<HR>You need manage access rights to view access rights.<HR>" );
			return;
		    }

		String base = breq.getParameter( "base" );
        String parent = null;
        
		if ( base != null )
			{
			if ( !base.endsWith( "*" ) )
				base = null;
			else
                {
				base = base.substring( 0, base.length()-1 );
                int secondLastDot = base.lastIndexOf('.', base.lastIndexOf('.')-1 );
                if (secondLastDot != -1)
                    {
                    parent = base.substring(0, secondLastDot+1) + "*";
                    }
                }
			}

        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    //out.println( "<PRE>" + acl.toString() );
		    //Enumeration enumeration = acl.entries();
		    AclEntry aclentry;
		    
		    Vector groups = new Vector();
		    Vector headings = new Vector();

		    Enumeration enumeration;
		    if ( base==null || base.length()==0 )
		   	{
		   	enumeration = Group.findGroups( "name LIKE 'localgroup." + 
		    								resource.getResourceId() +
		    								".%'", "name" );
		   	while ( enumeration.hasMoreElements() )
		      	groups.addElement( enumeration.nextElement() );
		      }
		      
		    if ( base==null  || base.length()==0 )
		   	enumeration = org.bodington.server.realm.Group.findGroups( "name NOT LIKE 'localgroup.%'", "name" );
		   else
	   		enumeration = org.bodington.server.realm.Group.findGroups( "name LIKE '" + base + "%'", "name" );
	   
		   String name;
		   int dot_offset;
		    while ( enumeration.hasMoreElements() )
		   	{
		   	if ( base==null )
		        groups.addElement( enumeration.nextElement() );
		      else
		      	{
		      	group = (org.bodington.server.realm.Group)enumeration.nextElement();
		      	name = group.getName();
		      	if ( !name.startsWith( base ) )
		      		continue;
		      	dot_offset = name.indexOf( '.', base.length() );
		      	if ( dot_offset < 0 || (base.length()==0 && name.startsWith( "localgroup." ) ) )
		      		groups.addElement( group );
		      	else
		      		{
		      		name = base+name.substring( base.length(), dot_offset )+".*";
		      		if ( !headings.contains( name ) )
		      			headings.addElement( name );
		      		}
		      	}
		      }

		        
         out.print( "<form name=selectorform method=POST ACTION=\"" );
         out.print( breq.absoluteURL() );
         out.println( "bs_template_access.html\">" );
         out.println( "<TR>" );

     		out.println( "<TD valign=top align=center>" );
			if ( base!=null )
				{
           	out.print( "<INPUT TYPE=HIDDEN NAME=category_url VALUE=\"" );
           	out.print( breq.absoluteURL() );
           	out.print( "\"><INPUT TYPE=HIDDEN NAME=category_page VALUE=\"bs_template_" );
           	out.print( breq.getPageName() );
           	out.println( "\">" );
           	out.println( "<SELECT NAME=category size=10 onchange=\"browse()\">" );
           	if ( base.length() > 0 )
           		{
	      		out.println( "<OPTION VALUE=\"*\">*</OPTION>" );
	      		}
            if ( parent != null )
                {
                out.println( "<OPTION  VALUE=\"" +parent+ "\">"+parent+"</OPTION>" );
                }
      		out.print( "<OPTION SELECTED VALUE=\"" );
      		out.print( base );
      		out.println( "*\">" );
      		out.print( base );
      		out.print( "*" );
      		out.println( "</OPTION>" );
		   	for ( i=0; i<headings.size(); i++ )
		      	{
		      	out.print( "<OPTION VALUE=\"" );
            	out.print( headings.elementAt( i ).toString() );
		      	out.print( "\">" );
            	out.print( headings.elementAt( i ).toString() );
            	out.println( "</OPTION>" );
					}              
        		out.println( "</SELECT>" );
           	}
      	else
      		out.println( "<I>All categories</I>" );
       	out.println( "</TD>" );

         out.println( "<td valign=top align=left>" );
         out.println( "<INPUT TYPE=HIDDEN NAME=operation VALUE=accessadd>" );
         out.println( "<SELECT NAME=group_id size=7 onchange=\"setdescription()\">" );
		    
		    for ( i=0; i<groups.size(); i++ )
		        {
		        group = (org.bodington.server.realm.Group)groups.elementAt( i );
		        //don't list group if current user hasn't got see access to it.
		        if ( !group.checkPermission( Permission.SEE ) )
		        	continue;
                aclentry=acl.getAclEntry( group.getGroupId(), false );
                //don't list group if it is in acl already
                if ( aclentry!=null )
                    continue;
		        out.print( "<OPTION VALUE=\"" );
		        out.print( group.getGroupId().toString() );
		        out.print( "\">" );
		        if ( group.isLocalGroup() )
		        	out.print( group.getLocalName());
		        else
		        	out.print( group.getName() );
		        out.println( "</OPTION>" );
		        }
		    out.println( "</SELECT>" );
		    
		    out.print( "<BR>Description:<BR><SPAN CLASS=bs-textarea><TEXTAREA NAME=description rows=4 cols=25 wrap=HARD>" );
		    out.println( "Select a group to see its description.</TEXTAREA></SPAN></TD><TD>" );

            enumeration = Permission.permissions();
            Permission p;
            boolean first=true;
            while ( enumeration.hasMoreElements() )
                {
                p = (Permission)enumeration.nextElement();
                if ( p.equals( Permission.ADMINISTER ) || p.equals( Permission.SYSADMIN ) )
                    {
                    if ( !resource.checkPermission( Permission.SYSADMIN ) )
                        continue;
                    }
                if ( !first ) out.println( "<BR>" );
                first=false;
                out.print( "<input type=checkbox " );
                out.print( "name=" );
                out.print( p.toString() );
                out.print( " value=on>" );
                out.print( "<em><strong>" );
                out.print( p.toString() );
                out.println( "</strong></em>" );
                }

                        
                
            out.println( "</td></tr><TR><TD COLSPAN=2 VALIGN=CENTER ALIGN=CENTER>" );
            out.println( "<INPUT TYPE=SUBMIT VALUE=\"Add Selected Group\"></TD></form>" );
				out.print( "<TD VALIGN=CENTER ALIGN=CENTER><form method=POST ACTION=\"" );
				out.print( breq.absoluteURL() );
				out.print( "bs_template_access.html\"><INPUT TYPE=HIDDEN NAME=operation VALUE=noop><input type=SUBMIT value=\"Cancel\"></FORM>" );
				out.println( "<form name=describeform></TD></TR>" );
                
		    for ( i=0; i<groups.size(); i++ )
		        {
		        group = (org.bodington.server.realm.Group)groups.elementAt( i );
                aclentry=acl.getAclEntry( group.getGroupId(), false );
                if ( aclentry!=null )
                    {
                    continue;
                    }
		        out.print( "<INPUT TYPE=HIDDEN NAME=\"desc" );
		        out.print( group.getGroupId().toString() );
		        out.print( "\" Value=\"" );
                out.print( group.getDescription() );
		        out.println( "\">" );
		        }
            out.println( "</FORM>" );
            
		    for ( i=0; i<headings.size(); i++ )
		        {
		        out.print( "<!-- " );
		        out.print( headings.elementAt( i ).toString() );
		        out.println( " -->" );
         	  }
            
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( "<PRE>" + ex + "</PRE>" );
		    return;
		    }

		}

	/**
     *Entry point for ACL chenges. 
     * @param breq The request through which the change is being made
     * @param out The Writer to print messages to. 
     * @throws IOException Could probably go, but used method throw it. 
	 */
	private void accessoperation( Request breq, PrintWriter out )
        throws IOException
		{
        String op=breq.getParameter( "operation" );
        if ( op==null ) return;
        op=op.trim();
        if ( op.length()==0 ) return;
        
        if (!breq.getResource().checkPermission(Permission.MANAGE))
            {
            out.println( "<table CLASS=bs-table-opaque>" );
            out.println( "<tr><td align=center valign=top CLASS=bs-cell-special>" );
            out.println( "<FONT COLOR=RED>You don't have manage access.</FONT>");
            out.println( "</TD></TR></TABLE>" );
            return;
            }
        
        if ( op.equalsIgnoreCase( "parentacl" ) )
        	{
            out.println( "<table CLASS=bs-table-opaque>" );
            out.println( "<tr><td align=center valign=top CLASS=bs-cell-special>" );
            out.println( "<FONT COLOR=RED>Change to use of containing location: " );
            
            String str = breq.getParameter( "parentacl" );
            try
            	{
            	breq.getResource().setUseParentAcl( str!=null && str.equalsIgnoreCase( "yes" ) );
            	breq.getResource().save();
            	out.println( "CONFIRMED" );
            	}
            catch ( BuildingServerException bsex )
            	{
            	out.println( "Technical problem trying to save option change: " + bsex.toString() );
            	}
            
            out.println( ".</FONT></TD></TR></TABLE>" );
            return;
        	}

        if ( op.equalsIgnoreCase( "accessadd" ) ||
             op.equalsIgnoreCase( "accesschange" ) ||
             op.equalsIgnoreCase( "accessremove" ) )
            {
            String g;
            PrimaryKey group_id;
            
            out.println( "<table CLASS=bs-table-opaque>" );
            out.println( "<tr><td align=center valign=top CLASS=bs-cell-special>" );
            out.println( "<FONT COLOR=RED>" );
            g=breq.getParameter( "group_id" );
            if ( g==null )
			    {
			    out.println( " Problem: no group_id was specified.</FONT></TD></TR></TABLE>>" );
			    return;
			    }
		    try
		        {
		        group_id = new PrimaryKey( Integer.parseInt( g ) );
		        }
		    catch ( NumberFormatException nfex )
		        {
			    out.println( "Problem: invalid group_id was specified.</FONT></TD></TR></TABLE>" );
			    return;
		        }
            if ( op.equalsIgnoreCase( "accessadd" ) )
	    	    aclentryadd( breq, out, group_id );
            if ( op.equalsIgnoreCase( "accesschange" ) )
	    	    aclentrychange( breq, out, group_id );
            if ( op.equalsIgnoreCase( "accessremove" ) )
	    	    aclentryremove( breq, out, group_id );
            return;
            }
		}
		
	private void aclentryadd( Request breq, PrintWriter out, PrimaryKey group_id )
		throws IOException
		{ 
		String box;

        out.println( "Access Change: " );
		
        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    org.bodington.server.realm.Group group = org.bodington.server.realm.Group.findGroup( group_id );
		    AclEntry aclentry;
            aclentry=new AclEntry();
            aclentry.setPrincipal( group );

            Enumeration enumeration = Permission.permissions();
            Permission p;
            while ( enumeration.hasMoreElements() )
                {
                p = (Permission)enumeration.nextElement();
                if ( p.equals( Permission.ADMINISTER ) || p.equals( Permission.SYSADMIN ) )
                    {
                    if ( !resource.checkPermission( Permission.SYSADMIN ) )
                        continue;
                    }
                box = breq.getParameter( p.toString() );
                if ( box!=null && box.equals( "on" ) )
                    aclentry.addPermission( p );
                }
                    
            acl.addEntry( aclentry );
            acl.save();
            out.println( " CONFIRMED.</FONT></TD></TR></TABLE>" );
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( " Problem: <PRE>" + ex + "</PRE></FONT></TD></TR></TABLE>" );
		    return;
		    }
		}
		
	private void aclentrychange( Request breq, PrintWriter out, PrimaryKey group_id )
		throws IOException
		{
		String box;

        out.println( "Access Change: " );
		
        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    //out.println( "<PRE>" + acl.toString() );
		    AclEntry aclentry;
            aclentry=acl.getAclEntry( group_id, false );
            if ( aclentry==null )
                {
                out.println( "The selected access control entry no longer exists." );
                return;
                }

            Enumeration enumeration = Permission.permissions();
            Permission p;
            while ( enumeration.hasMoreElements() )
                {
                p = (Permission)enumeration.nextElement();
                if ( aclentry.getGroup().isOwnersGroup() && ( p.equals( Permission.SEE ) || 
                                    p.equals( Permission.VIEW ) || 
                                    p.equals( Permission.MANAGE ) ) )
                    continue;
                if ( p.equals( Permission.ADMINISTER ) || p.equals( Permission.SYSADMIN ) )
                    {
                    if ( !resource.checkPermission( Permission.SYSADMIN ) )
                        continue;
                    }
                box = breq.getParameter( p.toString() );
                if ( box!=null && box.equals( "on" ) )
                    aclentry.addPermission( p );
                else
                    aclentry.removePermission( p );
                }

                    
            aclentry.save();
            out.println( " CONFIRMED.</FONT></TD></TR></TABLE>" );
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( " Problem: <PRE>" + ex + "</PRE></FONT></TD></TR></TABLE>" );
		    return;
		    }
		}
		
	private void aclentryremove( Request breq, PrintWriter out, PrimaryKey group_id )
		throws IOException
		{
		String box;

        out.println( "Remove access : " );
		
        try
            {
		    Resource resource = breq.getResource();
		    org.bodington.server.realm.Acl acl = resource.getAcl();
		    //out.println( "<PRE>" + acl.toString() );
		    AclEntry aclentry;
            aclentry=acl.getAclEntry( group_id, false );
            if ( aclentry==null )
                {
                out.println( "The selected access control entry no longer exists.</FONT></TD></TR></TABLE>" );
                return;
                }
           
            if ( aclentry.getGroup().isOwnersGroup() )
                {
                out.println( "You can't remove the owners group from the access control.</FONT></TD></TR></TABLE>" );
                return;
                }

            acl.removeEntry( aclentry );
            acl.save();
                
            out.println( " CONFIRMED.</FONT></TD></TR></TABLE>" );
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( " Problem: <PRE>" + ex + "</PRE></FONT></TD></TR></TABLE>" );
		    return;
		    }
		}
		

	private void groupmenu( Request breq, PrintWriter out )
		throws IOException
		{
		String g;
		PrimaryKey group_id;
		org.bodington.server.realm.Group group;
		
        g=breq.getParameter( "group_id" );
        if ( g==null )
			{
			out.println( "Problem: no group_id was specified." );
			return;
			}
		try
		    {
		    group_id = new PrimaryKey( Integer.parseInt( g ) );
		    }
		catch ( NumberFormatException nfex )
		    {
			out.println( "Problem: invalid group_id was specified." );
			return;
		    }
		    
		if ( !breq.isAuthenticated() )
			{
			out.println( "<br />You haven't checked in to the building so you can't work with this group." );
			return;
			}
		
        try
            {
            group = org.bodington.server.realm.Group.findGroup( group_id );
            if ( group==null )
			    {
			    out.println( "Problem: the specified group wasn't found." );
			    return;
			    }

			//There will be more access checking on the groups when
			//the user tries to edit them.
            if ( !group.checkPermission( Permission.forName( "see" ) ) )
		    	{
				out.println( "<br />You lack the see access right for this group." );
				return;
		    	}


            out.println( "<table CLASS=bs-table-opaque>" );
            out.println( "<tr>" );
            out.println( "<td CLASS=bs-cell-special><strong>Name</strong></td>" );
            out.println( "<td CLASS=bs-cell-special><strong>Description</strong></td>" );
            out.println( "<td CLASS=bs-cell-special><strong>Access Rights</strong></td>" );
            out.println( "</tr>" );
            out.println( "<tr>" );
            out.print( "<td valign=\"top\"><strong>" );
		    if ( group.isLocalGroup() )
		        out.print( group.getLocalName() );
		    else
		        out.print( group.getName() );
            out.println( "</strong></td>" );
            out.print( "<td valign=\"top\">" );
            out.print( group.getDescription() );
            out.println( "</td>" );
            out.println( "<td valign=\"top\">You can select the  " );
            out.println( "group in your access control lists" );
            if ( group.checkPermission( Permission.forName( "view" ) ) )
                out.println( ", list membership of the group" );
            if ( group.checkPermission( Permission.forName( "edit" ) ) )
                out.println( ", edit membership of the group" );
            if ( group.checkPermission( Permission.forName( "manage" ) ) )
                out.println( ", control the rights of others to access the group" );
            out.println( ".</td></tr>" );

            out.println( "<tr>" );
            out.println( "<td colspan=\"3\"><div align=\"center\"><center>" );
            out.println( "<table border=\"0\"><tr>" );
            
            out.println( "<td>" );
            if ( group.checkPermission( Permission.forName( "view" ) ) )
                {
                out.println( "<form method=POST ACTION=bs_template_accessgroup.html>" );
                out.print( "<input type=hidden name=group_id value=" );
                out.print( group_id.toString() ) ;
                out.println( " >" );
                //search in current group for users to remove
                out.print( "<input type=hidden name=search_group_id value=" );
                out.print( group_id.toString() ) ;
                out.println( " >" );
                out.println( "<INPUT type=hidden name=operation value=list>" );
                out.println( "<INPUT TYPE=hidden NAME=in_zone VALUE=zone_any>" );
                out.println( "<input type=hidden name=identifier value=user_id>" );
                out.println( "<input type=hidden name=values value=\"*\">" );
                
                out.println( "<input type=submit value=\"List Members\">" );
                out.println( "</form>" );
                }
            out.println( "</td>" );
            
            out.println( "<td>" );
            if ( group.checkPermission( Permission.forName( "edit" ) ) )
                {
                out.println( "<form method=POST ACTION=bs_template_accessgroup.html>" );
                out.print( "<input type=hidden name=group_id value=" );
                out.print( group_id.toString() ) ;
                out.println( " >" );
                out.println( "<INPUT type=hidden name=operation value=addform>" );
                out.println( "<input type=submit value=\"Add Members\">" );
                out.println( "</form>" );
                }
            out.println( "</td>" );
            
            out.println( "<td>" );
            if ( group.checkPermission( Permission.forName( "edit" ) ) )
                {
                out.println( "<form method=POST ACTION=bs_template_accessgroup.html>" );
                out.print( "<input type=hidden name=group_id value=" );
                out.print( group_id.toString() ) ;
                out.println( " >" );
                //search in current group for users to remove
                out.print( "<input type=hidden name=search_group_id value=" );
                out.print( group_id.toString() ) ;
                out.println( " >" );
                out.println( "<INPUT type=hidden name=operation value=remove>" );
                out.println( "<INPUT TYPE=hidden NAME=in_zone VALUE=zone_any>" );
                out.println( "<input type=hidden name=identifier value=user_id>" );
                out.println( "<input type=hidden name=values value=\"*\">" );
                
                out.println( "<input type=submit value=\"Remove Members\">" );
                out.println( "</form>" );
                }
            out.println( "</td>" );
            out.println( "<td><FORM><INPUT TYPE=BUTTON VALUE=\"Close\" ONCLICK=\"closeme()\"></FORM></TD>" );
/*            
            out.println( "<td>" );
            if ( group.checkPermission( Permission.forName( "manage" ) ) )
                {
                out.println( "<form>" );
                out.println( "<input type=button value = \"Change Access to Group\" onclick=\"alert( 'This button is not yet functional.' )\">" );
                out.println( "</form>" );
                }
            else
                out.println( "&nb;" );
            out.println( "</td>" );
*/
            out.println( "</tr></table></center></div></td></tr></table>" );

                
            return;
		    }
		catch ( Exception ex )
		    {
		    out.println( "<PRE>" + ex + "</PRE>" );
		    return;
		    }

		}


	private void groupoperation( Request breq, PrintWriter out )
	throws IOException
	{
	    Integer temp_int;
	    String g;
	    PrimaryKey group_id;
	    
	    g = breq.getParameter("group_id");
	    if (g == null)
	    {
	        out.println("Problem: no group_id was specified.");
	        return;
	    }
	    try
	    {
	        group_id = new PrimaryKey(Integer.parseInt(g));
	    }
	    catch (NumberFormatException nfex)
	    {
	        out.println("Problem: invalid group_id was specified.");
	        return;
	    }
	    
	    out.println( "<P>Please wait for the selected operation to complete before you " );
	    out.println( "select further operations or navigate away from this window.</P>" );
	    out.flush();
        
        String op=breq.getParameter( "operation" );
        if ( op==null )
        {
            listusers(out, false, null, null, null, null, g,g, "user_id" ,null);
            return;
        }
        
        op=op.trim();
        if ( op.length()==0 ) return;
        
        if ( op.equalsIgnoreCase( "noop" ) )
        {
            return;
        }
        
	    if ( op.equalsIgnoreCase( "list" ) )
	    {
	        out.println( "<h4><strong>Users in this Group</strong></h4>" );
	        
	        listusers( breq, out, false );
	        return;
	    }
	    
	    
	    if ( op.equalsIgnoreCase( "remove" ) )
	    {
	        
	        out.println( "<FORM METHOD=POST ACTION=bs_template_accessgroup.html>" );
	        out.print( "<input type=hidden name=group_id value=" );
	        out.print( group_id.toString() ) ;
	        out.println( " >" );
	        out.println( "<INPUT type=hidden name=operation value=removeconfirm>" );
	        out.println( "<h4><strong>Confirm Removal of Users</strong></h4>" );
	        
	        listusers( breq, out, false );
	        out.println( "</form>" );
	        return;
	        
	    }
	    
	    
	    if ( op.equalsIgnoreCase( "removeconfirm" ) )
	    {
	        org.bodington.server.realm.Group group;
	        org.bodington.server.realm.User user;
	        org.bodington.server.realm.User current_user;
	        
	        current_user=(org.bodington.server.realm.User)BuildingContext.getContext().getUser();
	        
	        if ( !breq.isAuthenticated() )
	        {
	            out.println( "<br />You haven't checked in to the building so you can't change access rights." );
	            return;
	        }
	        
	        try
	        {
	            group = org.bodington.server.realm.Group.findGroup( group_id );
	            if ( group==null )
	            {
	                out.println( "Problem: the specified group wasn't found." );
	                return;
	            }
	            if ( !group.checkPermission( Permission.EDIT ) )
	            {
	                out.println( "<br />You need edit access rights to the selected group to edit membership." );
	                return;
	            }
	            Enumeration plist = breq.getParameterNames();
	            String pname, remove_id;
	            StringBuffer where_clause = new StringBuffer();
	            where_clause.append( "user_id IN (" );
	            boolean first=true;
	            while ( plist.hasMoreElements() )
	            {
	                pname = (String)plist.nextElement();
	                if ( !pname.startsWith( "user_id_" ) )
	                    continue;
	                remove_id=pname.substring( 8 );
	                try
	                {
	                    temp_int = new Integer( remove_id );
	                }
	                catch ( NumberFormatException nfex )
	                {
	                    continue;
	                }
	                if ( !first )
	                    where_clause.append( ", " );
	                where_clause.append( temp_int.toString() );
	                first=false;
	            }
	            where_clause.append( " ) " );
	            
	            if (first)
	            {
	                out.println( "No users were selected so none were removed from the group." );
	                return;
	            }
	            
	            Enumeration enumeration=org.bodington.server.realm.User.findUsers( where_clause.toString(), "surname, initials" );
	            int n=0;
	            while ( enumeration.hasMoreElements() )
	            {
	                user = (org.bodington.server.realm.User)enumeration.nextElement();
	                if ( group.isOwnersGroup() && user.equals( current_user ) )
	                    out.println( "<I>Can't remove self</I>" );
	                else
	                {
	                    group.removeMemberChecked( user );
	                    n++;
	                }
	            }
	            group.save();
	            out.println( "<p>Removed " + n + " user(s).</p>" );
                
	        }
	        catch ( Exception ex )
	        {
	            out.println( "<PRE>" + ex + "</PRE>" );
	        }
            listusers(out, false, null, null, null, null, g,g, "user_id" ,null);
	    }
	    
	    if ( op.equalsIgnoreCase( "add" ) )
	    {
	        
	        out.println( "<FORM METHOD=POST ACTION=bs_template_accessgroup.html>" );
	        out.print( "<input type=hidden name=group_id value=" );
	        out.print( group_id.toString() ) ;
	        out.println( " >" );
	        out.println( "<INPUT type=hidden name=operation value=addconfirm>" );
	        out.println( "<h4><strong>Confirm Addition of Users</strong></h4>" );
	        
	        listusers( breq, out, false );
	        out.println( "</form>" );
	        
	        return;
	        
	    }
	    
	    if ( op.equalsIgnoreCase( "addconfirm" ) )
	    {
	        org.bodington.server.realm.Group group;
	        org.bodington.server.realm.User user;
	        
	        if ( !breq.isAuthenticated() )
	        {
	            out.println( "<br />You haven't checked in to the building so you can't change access rights." );
	            return;
	        }
	        
	        try
	        {
	            group = org.bodington.server.realm.Group.findGroup( group_id );
	            if ( group==null )
	            {
	                out.println( "Problem: the specified group wasn't found." );
	                return;
	            }
	            if ( !group.checkPermission( Permission.EDIT ) )
	            {
	                out.println( "<br />You need edit access rights to the selected group to edit membership." );
	                return;
	            }
	            Enumeration plist = breq.getParameterNames();
	            
	            String pname, remove_id;
	            StringBuffer where_clause = new StringBuffer();
	            where_clause.append( "user_id IN (" );
	            boolean first=true;
	            while ( plist.hasMoreElements() )
	            {
	                pname = (String)plist.nextElement();
	                if ( !pname.startsWith( "user_id_" ) )
	                    continue;
	                remove_id=pname.substring( 8 );
	                try
	                {
	                    temp_int = new Integer( remove_id );
	                }
	                catch ( NumberFormatException nfex )
	                {
	                    continue;
	                }
	                if ( !first )
	                    where_clause.append( ", " );
	                where_clause.append( temp_int.toString() );
	                first=false;
	            }
	            where_clause.append( " ) " );
	            if (first)
	            {
	                out.println( "No users were selected so none were added to the group." );
	                return;
	            }
	            
	            Enumeration enumeration=org.bodington.server.realm.User.findUsers( where_clause.toString(), "surname, initials" );
	            int n=0;
	            while ( enumeration.hasMoreElements() )
	            {
	                user = (org.bodington.server.realm.User)enumeration.nextElement();
	                group.addMember( user );
	                n++;
	            }
	            group.save();
	            
	            out.println( "<P>Added " + n + " user(s)</p>"  );
                listusers(out, false, null, null, null, null, g,g, "user_id" ,null);
	        }
	        catch ( Exception ex )
	        {
	            out.println( "<PRE>" + ex + "</PRE>" );
	            return;
	        }
	    }
	    
	    if ( op.equalsIgnoreCase( "addform" ) || op.equalsIgnoreCase( "addform2" ) )
	    {    
	        if (op.equalsIgnoreCase("addform")) {
	            out.println("<FORM METHOD=POST ACTION=bs_template_accessgroup.html>");
	            out.print("<input type=hidden name=group_id value=");
	            out.print(group_id.toString());
	            out.println(" >");
	            out.println("<INPUT type=hidden name=operation value=addform2>");
	            out.println("<INPUT type=submit value=\"Advanced Search\">");
	            out.println(
	            "(Allows you to limit the search to members of a selected group).");
	            out.println("</FORM>");
	        }
	        userSearchFields(breq, out, group_id, op.equalsIgnoreCase("addform2"), false);
	    }
	    
	}


	public void listusers( Request req, PrintWriter out, boolean link_to_user )
		throws IOException
		{
	    listusers(out, link_to_user,
            req.getParameter("operation"),
            req.getParameter("output_format"),
            req.getParameter("in_zone"),
            req.getParameter( "search_group_name" ),
            req.getParameter( "search_group_id" ),
            req.getParameter( "group_id" ),
            req.getParameter( "identifier" ),
            req.getParameter( "values" )
            );
        }
    
    /**
     * Prints out a list of users.
     * 
     * @param out The output to write the HTML/XML to.
     * @param link_to_user If true then the users name will be a link (HTML).
     * @param operation The operation to perform. (add or remove).
     * Can be null. 
     * @param format The output format (html or xml).
     * Can be null.
     * @param zone The zone to search in. Can be null.
     * @param group_name 
     * @param g 
     * @param gg The group ID.
     * @param identifier The field we should be searching in.
     * @param idlist The values to search for.
     */
    public void listusers(PrintWriter out, boolean link_to_user, String operation,
        String format, String zone, String group_name, String g, String gg,
        String identifier, String idlist)
        {
		try
			{
            boolean in_zone=false;
		    String wildstring;
		    Integer temp_int;
		    PrimaryKey search_group_id=null, group_id = null;
		    Vector user_ids = new Vector();
		    org.bodington.server.realm.Group group=null;
		    org.bodington.server.realm.User user = null;
		    User currentuser = (User)BuildingContext.getContext().getUser();
           	Group search_group=null;
           	boolean xml_output, html_output;
           	int i, j, n, cols=0;
    		
		    org.bodington.server.realm.User current_user;
    		current_user=(org.bodington.server.realm.User)BuildingContext.getContext().getUser();

        	if ( operation!=null )
        		{
        		operation=operation.trim();
	        	if ( operation.length()==0 ) operation=null;
            	}
    		   
            xml_output = format != null && format.equalsIgnoreCase( "xml" );
            html_output = !xml_output && !(format != null && format.equalsIgnoreCase( "text" ));

			in_zone = zone!=null && zone.equalsIgnoreCase( "zone_restricted" );

            if ( group_name!=null && group_name.length() == 0 )
            	group_name=null;

		    try
		        {
    	        if ( g!=null )
			        search_group_id = new PrimaryKey( Integer.parseInt( g ) );
		        }
		    catch ( NumberFormatException nfex )
		        {
		        search_group_id = null;
		        }
    		    
		    try
		        {
    	        if ( gg!=null && gg.length()>0 )
			        group_id = new PrimaryKey( Integer.parseInt( gg ) );
		        }
		    catch ( NumberFormatException nfex2 )
		        {
				out.println( "Technical problem finding group." + nfex2 );
		        return;
		        }
    		    
            if ( identifier==null )
                {
			    out.println( "You must select a type of identifier." );
			    return;
                }
            if ( idlist==null )
                idlist="";
            idlist=idlist.trim();
            if ( idlist.length()==0 )
                {
                if ( search_group_id == null && group_name == null )
                	{
			    	out.println( "Invalid input: you have to select a group and/or type something into the search for box." );
				    return;
				    }
				idlist = "*";
                }
            StringTokenizer tok = new StringTokenizer( idlist );
            StringBuffer where_clause = new StringBuffer( idlist.length() );

            if ( in_zone && currentuser!=null )
            	{
            	where_clause.append( "zone_id = " );
            	where_clause.append( currentuser.getZoneId() );
  				where_clause.append( " AND " );
            	}
            
            try
            	{
            	if ( search_group_id != null )
            		{
            		search_group = Group.findGroup( search_group_id );
            		}
            	else if ( group_name != null )
            		{
            		search_group = Group.findGroupByName(group_name);
            		}
            	}
			catch ( BuildingServerException bsexg )
				{
				out.println( "Technical problem finding search group." + bsexg );
				return;
				}

            try
            	{
            	if ( group_id != null )
            		{
            		group = Group.findGroup( group_id );
            		}
            	}
			catch ( BuildingServerException bsexg )
				{
				out.println( "Technical problem finding group." + bsexg );
				return;
				}

            if ( search_group!=null )
            	{
		        if ( !search_group.checkPermission( Permission.VIEW ) )
		            {
			        out.println( "<p>You need view access rights to the 'search in' group to list it's membership.</p>" );
			        return;
		            }
            	String w = search_group.whereMembers();
            	if ( w!=null )
            		{
            		where_clause.append( w );
            		where_clause.append( " AND " );
            		}
            	}

            if ( tok.countTokens() == 1 )
            	{
                wildstring = tok.nextToken();
                wildstring=wildstring.replace( '*', '%' );
                
            	if ( identifier.equals( "user_id" ) )
            		{
            		if ( wildstring.equals( "%" ) )
            			{
            			if ( search_group != null )
            				where_clause.append( "user_id IS NOT NULL" );
            			else
            				{
            				out.println( "<p>You can only wild card bodington user IDs when you search for users within a group.</p>" );
            				return;
            				}
            			}
            		else
            			{
			        	try
			        		{
			        		Integer.parseInt( wildstring );
			        		}
			        	catch ( NumberFormatException nfex )
			        		{
						    out.println( "<p>Invalid identifier type.</p>" );
			    			return;
			        		}
			        	where_clause.append( "user_id = " + wildstring );
            			}
            		}
            	else
            		{
            		if ( identifier.equals( "surname" ) )
                		where_clause.append( "surname LIKE " );
            		else
                		{
                		if ( identifier.equals( "user_name" ) )
                    		where_clause.append( "user_id IN ( SELECT user_id FROM pass_phrases WHERE user_name LIKE " );
                        else if ( ("webauth_user_name").equals( identifier) )
                            where_clause.append( "user_id IN ( SELECT user_id FROM web_auth_user WHERE user_name LIKE " );
                		else
                			{
			        		try
			        			{
			        			temp_int = new Integer( identifier );
			        			}
			        		catch ( NumberFormatException nfex )
			        			{
						    	out.println( "Invalid identifier type." );
			    				return;
			        			}
                        	where_clause.append( "user_id IN (SELECT user_id FROM alias_entries WHERE alias_id = " );
                        	where_clause.append( temp_int.toString() );
                        	where_clause.append( " AND user_alias LIKE " );
                    		}
                		}
                	}
                	
                if ( !identifier.equals( "user_id" ) )
                	{
                	where_clause.append( SqlDatabase.quotedSQL( wildstring ) );
	            	if ( !identifier.equals( "surname" ) )
    	            	where_clause.append( " )" );
    	            }
            	}
            else
            	{
            	if ( identifier.equals( "surname" ) )
                	where_clause.append( "surname IN ( " );
            	else
                	{
                	if ( identifier.equals( "user_name" ) )
                    	where_clause.append( "user_id IN ( SELECT user_id FROM pass_phrases WHERE user_name IN ( " );
                    else if ( ("webauth_user_name").equals( identifier) )
                        where_clause.append( "user_id IN ( SELECT user_id FROM web_auth_user WHERE user_name IN ( " );
                	else
                    	{
                    	if ( identifier.equals( "user_id" ) )
                        	where_clause.append( "user_id IN ( " );
                    	else
                        	{
			        		try
			        			{
			        			temp_int = new Integer( identifier );
			        			}
			        		catch ( NumberFormatException nfex )
			        			{
						    	out.println( "Invalid identifier type - must be numeric." );
			    				return;
			        			}
                        	where_clause.append( "user_id IN (SELECT user_id FROM alias_entries WHERE alias_id = " );
                        	where_clause.append( temp_int.toString() );
                        	where_clause.append( " AND user_alias IN (" );
                        	}
                    	}
                	}
	                
            	boolean first=true;
            	while ( tok.hasMoreTokens() )
                	{
                	if ( !first )
                    	where_clause.append( ", " );
                	first=false;
                	
                	if ( identifier.equals( "user_id" ) )
                		{
			        	try
			        		{
			        		temp_int = new Integer( tok.nextToken() );
			        		}
			        	catch ( NumberFormatException nfex )
			        		{
						    out.println( "Invalid user id: must be a number." );
			    			return;
			        		}
			        	where_clause.append( temp_int.toString() );
                		}
					else
	                	where_clause.append( SqlDatabase.quotedSQL( tok.nextToken() ) );
						
                	}
            	if ( !identifier.equals( "user_id" ) && !identifier.equals( "surname" ) )
                	where_clause.append( " ) " );
            	where_clause.append( " ) " );
				}
				
				
            try
                {
                // look for aliases that could be included
                // at present narrow it down by using only primary
                // aliases that can be used to create students.
			    Enumeration enumeration=Alias.findAliases( "alias_id IS NOT NULL" );
			    Alias alias;
			    AliasEntry alias_entry;
			    Vector aliases = new Vector();
			    PassPhrase pass_phrase;
                WebAuthUser web_auth_user;
			    
			    while ( enumeration.hasMoreElements() )
			        {
			        alias = (Alias)enumeration.nextElement();
			        if ( alias.isPrimary() && 
			        		(alias.hasUserCategory( "student" ) || 
			        		 alias.hasUserCategory( "students" )  ) )
			        	aliases.addElement( alias );
			        }
                

			    enumeration=org.bodington.server.realm.User.findUserPrimaryKeys( where_clause.toString(), "name, user_id" );
			    while ( enumeration.hasMoreElements() )
			        {
			        user_ids.addElement( enumeration.nextElement() );
			        }
			    
	            if ( html_output )
	            	{
			    	out.print( "<p>" );
			    	out.print( user_ids.size() );
			    	out.print( " user" + (user_ids.size() > 1 ? "s" : "") );
                    out.print(" found.</p>" );
	            	}
	            	
			    if ( user_ids.size() > 2000 )
			    	{
			    	out.println( "<p>It is not possible to list more than 2000 users on this page.</p>" );
			    	return;
			    	}

				if ( xml_output )
					{
					out.println( "<?xml version=\"1.0\"?>" );
					if ( search_group == null )
						out.println( "<group>" );
					else
						{
						out.print( "<group group_id=\"" );
						out.print( search_group.getGroupId().toString() );
						out.print( "\" group_name=\"" );
						out.print( search_group.getName() );
						out.println( "\">" );
						}
					}
				else
					{
					if ( html_output )
						{
            			out.println( "<table CLASS=bs-table-opaque><tr>" );
            			if ( ("add".equals( operation ) || "remove".equals( operation )) 
                            && user_ids.size() > 1 )
            				{
	            			out.println( "<td CLASS=bs-cell-special><strong>Select</strong></td>" );
							cols=5;
	            			}
	            		else
							cols=4;
            			out.println( "<td CLASS=bs-cell-special><strong>WebLearn User ID</strong></td>" );
            			out.println( "<td CLASS=bs-cell-special><strong>Name</strong></td>" );
            			out.println( "<td CLASS=bs-cell-special><strong>External User Name</strong></td>" );
                        out.println( "<td CLASS=bs-cell-special><strong>Oxford User Name</strong></td>" );
            			}
            		else
            			out.print( "user_id\tname\tuser_name\twebauth" );

            		
					for ( j=0; j<aliases.size(); j++ )
						{
						if ( html_output )
            				out.print( "<td CLASS=bs-cell-special><strong>" );
            			else
            				out.print( "\t" );
						alias = (Alias)aliases.elementAt( j );
						out.print( alias.getZone().getPrefix() );
						if ( html_output )
							out.print( " " );
						else
							out.print( "_" );
						out.print( alias.getAliasName() );
						if ( html_output )
							{
	            			out.println( "</strong></td>" );
	            			cols++;
	            			}
						}
						
					if ( html_output )
	            		out.println( "</tr>" );
	            	else
	            		out.println();
            		}

				// Insert the (de)select all buttons (when no. of users > 1):            	
                if ( html_output && ("add".equals(  operation ) || "remove".equals(  operation )) 
                    && user_ids.size() > 1)
                    {
                    out.print( "<TR><TD COLSPAN=" );
                    out.print( cols + ">" );
                    out.println( "<INPUT TYPE=button VALUE = \"Select All\" ONCLICK=\"checkall(true)\">"    );
                    out.println( "<INPUT TYPE=button VALUE = \"Unselect All\" ONCLICK=\"checkall(false)\">" );
                    out.println( "</TD></TR>" );
                    }
			    
			    n=0;
			    for ( i=0; i<user_ids.size(); i++ )
			        {
			        user = org.bodington.server.realm.User.findUser( (PrimaryKey)user_ids.elementAt( i ) );
		            if ( user == null )
		            	continue;
		            if ( user.getSurname().equals( "Anonymous" ) )
		            	continue;
		            
		            //break up table into chunks so page display is more responsive.
					if ( html_output && i>0 && (i%50)==0 )
						{
						out.println( "</table>" );
            			out.println( "<table CLASS=bs-table-opaque><tr>" );
            			if ( ("add".equals( operation ) || "remove".equals( operation )) 
                            && user_ids.size() > 1 )
                        {
            				out.println( "<td CLASS=bs-cell-special><strong>Select</strong></td>" );
                        }
            			out.println( "<td CLASS=bs-cell-special><strong>User ID</strong></td>" );
            			out.println( "<td CLASS=bs-cell-special><strong>Name</strong></td>" );
                        out.println( "<td CLASS=bs-cell-special><strong>External User Name</strong></td>" );
                        out.println( "<td CLASS=bs-cell-special><strong>Oxford User Name</strong></td>" );

	            		
						for ( j=0; j<aliases.size(); j++ )
							{
           					out.print( "<td CLASS=bs-cell-special><strong>" );
							alias = (Alias)aliases.elementAt( j );
							out.print( alias.getZone().getPrefix() );
							out.print( " " );
							out.print( alias.getAliasName() );
            				out.println( "</strong></td>" );
							}
							
            			out.println( "</tr>" );

						// Insert the (de)select all buttons (when no. of users > 1):                        
                        if ( user_ids.size() > 1)
                            {
                            out.print( "<TR><TD COLSPAN=" );
                            out.print( cols + ">" );
                            out.println( "<INPUT TYPE=button VALUE = \"Select All\" ONCLICK=\"checkall(true)\">"    );
                            out.println( "<INPUT TYPE=button VALUE = \"Unselect All\" ONCLICK=\"checkall(false)\">" );
                            out.println( "</TD></TR>" );
                            }
            			}
                    
		            if ( xml_output )
		            	out.print( "\t<user>\n\t\t<user_id>" );
		            else if ( html_output )
			        	out.print( "<tr><td>" );

		            if ( html_output )
		            	{
		            	if ( operation!=null && operation.equals( "add" ) )
		            		{
			        		if ( group.isMember( user ) )
			            		out.println( "Already member." );
			        		else
 			            		{
			            		if ( user.getSurname().equals( "Anonymous" ) )
			            			out.println( "Can't add.</td><td>" );
                                else if (user_ids.size() == 1 )
                                    n = 1;
			            		else
			            			{
			            			out.print( "<INPUT NAME=\"user_id_" );
			            			out.print( user.getUserId().toString() );
			            			out.print( "\" TYPE=CHECKBOX CHECKED VALUE=\"On\">" );
                                    out.println( "</td><td>");
			            			n++;
			            			}
			            		}
			            	}
			            	
		            	if ( operation!=null && operation.equals( "remove" ) )
		            		{
		            		if ( group.isOwnersGroup() &&
		            			 user.equals( currentuser ) )
		            			{
		            			out.println( "Can't remove.</td><td>" );
		            			}
                            else if (user_ids.size() == 1 )
                                n = 1;
		            		else 
		            			{
			            		out.print( "<INPUT NAME=\"user_id_" );
			            		out.print( user.getUserId() );
                                out.print( "\" TYPE=CHECKBOX VALUE=\"On\">" );
                                out.println( "</td><td>");
			            		n++;
		            			}
		            		}
		            	}

				    out.print( user.getUserId().toString() );
				    
		            if ( xml_output )
		            	out.print( "</user_id>\n\t\t<name>" );
		            else if ( html_output )
		            	{
		            	out.println( "</td><td>" );
		            	if ( link_to_user )
		            		{
		            		out.print( "<a href = \"" );
					    	out.print( user.getUserId().toString() );
				        	out.print( "/bs_template_userdata.html\">" );
				        	}
				        }
				    else
				    	out.print( "\t" );
		            	
				    out.print( user.getName() );

		            if ( xml_output )
		            	out.println( "</name>" );
		            else if ( html_output )
		            	{
		            	if ( link_to_user )
		            		out.println( "</a>" );
		            	out.println( "</td>" );
		            	}
				    
					pass_phrase = PassPhrase.findPassPhrase( user );
					if ( xml_output )
						{
						if ( pass_phrase!=null && pass_phrase.getUserName()!=null )
							{
							out.print( "\t\t<user_name>" );
							out.print( pass_phrase.getUserName() );
							out.println( "</user_name>" );
							}
						}
					else
						{
						if ( html_output )
							out.print( "<td>" );
						else
							out.print( "\t" );
						if ( pass_phrase!=null && pass_phrase.getUserName()!=null )
							out.print( pass_phrase.getUserName() );
						if ( html_output )
							out.print( "</td>" );
						}
						
                    web_auth_user = WebAuthUser.findWebAuthUser( user );
                    if ( xml_output )
                        {
                        if ( web_auth_user!=null && web_auth_user.getUserName()!=null )
                            {
                            out.print( "\t\t<webauth>" );
                            out.print( web_auth_user.getUserName() );
                            out.println( "</webauth>" );
                            }
                        }
                    else
                        {
                        if ( html_output )
                            out.print( "<td>" );
                        else
                            out.print( "\t" );
                        if ( web_auth_user!=null && web_auth_user.getUserName()!=null )
                            out.print( web_auth_user.getUserName() );
                        if ( html_output )
                            out.print( "</td>" );
                        }
                    
					for ( j=0; j<aliases.size(); j++ )
						{
						alias = (Alias)aliases.elementAt( j );
						
						if ( !xml_output )
							{
							if ( html_output )
								out.print( "<td>" );
							else 
								out.print( "\t" );
							}

						alias_entry = AliasEntry.findAliasEntry(alias, user);

						if ( alias_entry!=null )
							{
							if ( xml_output )
								{
								out.print( "\t\t<alias_entry zone_id=\"" );
								out.print( alias.getZoneId() );
								out.print( "\" prefix=\"" );
								out.print( alias.getZone().getPrefix() );
								out.print( "\" alias_id=\"" );
								out.print( alias.getAliasId() );
								out.print( "\" alias_name=\"" );
								out.print( alias.getAliasName() );
								out.print( "\">" );
								out.print( alias_entry.getUserAlias() );
								out.println( "</alias_entry>" );
								}
							else
								{
								out.print( alias_entry.getUserAlias() );
								}
							}
							
						if ( html_output )
							out.print( "</td>" );
						}

					if ( xml_output )
						out.println( "\t</user>" );
					else if ( html_output )
						out.println( "</tr>" );
					else
						out.println();

			        }
			    }
		    catch ( Exception ex )
		        {
		        out.println( "</TABLE><PRE>" + ex + "</PRE>" );
		        return;
		        }

			if ( xml_output )
			    out.println( "</group>" );
			else if ( html_output )
				{
				if ( "add".equals( operation ) || "remove".equals( operation ) )
					{
			    	if ( n==0 )
			        	{
			        	if ( user_ids.size()==0 )
			        		{
                    		out.print( "<TR><TD COLSPAN=" );
                    		out.print( cols );
			            	out.println( ">No users were found.</TD></TR>" );
			            	}
			        	else
			        		{
                    		out.print( "<TR><TD COLSPAN=" );
                    		out.print( cols );
			            	out.println( ">Only users who cannot be processed were found.</TD></TR>" );
			            	}
			        	}
                	else
                    	{
                        if (user_ids.size() > 1)
                        {
                        	out.print( "<TR><TD COLSPAN=" );
                        	out.print( cols );
                        	out.print( ">These users have not yet been processed. " );
                        	out.println( "Please use the check boxes to select the users you wish to process.</TD></TR>" );
                        }
                    	out.print( "<TR><TD COLSPAN=" );
                    	out.print( cols );
                        if (user_ids.size() > 1)
                        	out.println( "><INPUT TYPE=SUBMIT VALUE=\"Confirm selected users\">" );
                        else
                        {
                            out.print( "><INPUT NAME=\"user_id_" );
                            out.print( user.getUserId() );
                            out.println( "\" TYPE=HIDDEN VALUE=\"On\">" );
                            out.println( "<INPUT TYPE=SUBMIT VALUE=\"Confirm matching user\">" );
                        }
                    	out.println( "</td></tr>" );
                    	}
                    }
			    out.println( "</table>" );
			    }

            return;
			}
		catch ( Exception ex )
			{
			out.println( "<HR><H4>There was a problem finding users.</H4><HR>\n" );
			return;
			}
		}

	// Puts in a table and form input fields for searching for users.
	// Doesn't put in <FORM> tag or </FORM>
	public void userSearchFields( Request breq, PrintWriter out, boolean withGroups, boolean withOutputOpts )
		throws IOException
		{
		int i;
		Group group;
	    User user;
    		
    	user=(User)BuildingContext.getContext().getUser();
		
		try
		    {
            out.println( "<table CLASS=bs-table-opaque><tr>" );
            out.println( "<td COLSPAN=3 CLASS=bs-cell-special><strong>Find Users</TD></tr>" );

            out.println( "<TR><TD VALIGN=TOP><B>Administrative Zone</B></TD><TD>" );
            out.println( "<INPUT TYPE=RADIO NAME=in_zone VALUE=zone_any CHECKED>Search entire site.<BR>" );
            out.println( "<INPUT TYPE=RADIO NAME=in_zone VALUE=zone_restricted>Search within " );
            out.println( user.getZone().getName() );
            out.print( "</TD><TD ROWSPAN=4 VALIGN=TOP>" );
            out.print( "<input type=submit value=\"Search\">" );
            
            if ( withGroups )
                {
                out.println( "</TD></TR>" );
			    out.println( "<TR><TD VALIGN=TOP><B>Search within group:</B></TD><TD>" );
                try
                    {
		    	    Enumeration enumeration = org.bodington.server.realm.Group.findGroups( 
		    									    "name NOT LIKE 'localgroup.%'", "name" );

            	    out.println( "<SELECT NAME=search_group_id size=5>" );
            	    out.println( "<OPTION VALUE=null SELECTED>ignore group membership</OPTION>" );
                		
		    	    for ( i=0; enumeration.hasMoreElements(); i++ )
		    		    {
		        	    group = (org.bodington.server.realm.Group)enumeration.nextElement();
		        	    //don't list group if current user hasn't got view access to it.
		        	    if ( !group.checkPermission( Permission.VIEW ) )
		        		    continue;
		        	    out.print( "<OPTION VALUE=\"" );
		        	    out.print( group.getGroupId().toString() );
		        	    out.print( "\">" );
	        		    out.print( group.getName() );
		        	    out.println( "</OPTION>" );
		        	    }
		            if ( i==0 )
		        	    out.println( "<OPTION VALUE=null>No groups available</OPTION>" );
		    	    out.println( "</SELECT></TD></TR>" );
		    	    }
                catch ( BuildingServerException bsex )
                    {
		    logException( out, "Facility", "userSearchFields", 
			"Technical error trying to produce list of groups. ",
			    bsex );
                    }
    			out.println( "</TD></TR>" );                
                }
            else
                {
                out.println( "<INPUT TYPE=HIDDEN NAME=search_group_id VALUE==null>" );
                out.println( "</TD></TR>" );
                }
                
			out.println( "<TR><TD VALIGN=TOP>" );                
                
            out.println( "<B>Search By Name or Unique Identifier:</B></TD><TD>" );
            out.println( "<select name=identifier size=5>" );
            out.println( "<option value=surname>Surname</option>" );
            out.println( "<option value=user_id>WebLearn User ID number</option>" );
            out.println( "<option value=user_name>External User Name</option>" );
            out.println( "<option value=webauth_user_name SELECTED>Oxford User Name</option>" );
			Enumeration z_enum;
			if ( BuildingContext.getContext().checkPermission( Permission.SYSADMIN ) )
                z_enum = Zone.findZones( "zone_id IS NOT NULL" );
            else
                z_enum = Zone.findZones( "zone_id = " + user.getZoneId() );
                	
            Enumeration a_enum;
            Zone z;
            Alias a;
            while ( z_enum.hasMoreElements() )
                {
                z = (Zone) z_enum.nextElement();
                a_enum = Alias.findAliases( "zone_id = " + z.getZoneId() );
                while ( a_enum.hasMoreElements() )
                    {
                    a = (Alias)a_enum.nextElement();
                    out.print( "<option value=" );
                    out.print( a.getAliasId().toString() );
                    out.print( ">" );
                    out.print( z.getName() );
                    out.print( ":" );
                    out.print( a.getAliasName() );
                    out.println( "</option>" );
                    }
                }
                
            out.println( "</select></TD></TR><TR><TD VALIGN=TOP><B>Search For:</B><BR>" );
            out.println("(Enter a list of values one per line or a single"
                + "value with or without a * wildcard.");
            out.println("Use the _ underscore character to represent the " +
                  "space between unhyphenated double-barrelled surnames.)");
            out.println("</TD><TD>" );
            out.println( "<SPAN CLASS=bs-textarea><textarea name=values rows=10 cols=20></textarea></SPAN>" );
            out.println( "</td></tr>" );
            if ( withOutputOpts )
            	{
            	out.println( "<tr><td VALIGN=\"top\"><b>Output:</b></td><td>" );
            	out.println( "<INPUT TYPE=RADIO NAME=output_format VALUE=html CHECKED>Web Page<BR>" );
            	out.println( "<INPUT TYPE=RADIO NAME=output_format VALUE=text>TAB delimited text<br> " );
            	out.println( "<INPUT TYPE=RADIO NAME=output_format VALUE=xml>XML file " );
            	out.println( "</td></tr>" );
            	}
           	out.println( "</TABLE>" );
            }
        catch ( BuildingServerException bsex )
            {
            out.println( "Technical error trying to produce form: " );
		    logException( out, "Facility", "userSearchFields", 
			"Technical error trying to produce form: ",
			    bsex );
            }
		}


              /**
                * Creates a table and form input fields for searching for users.
                * Groups are sorted by the various nodes so that it loads quickly.
                * Method derived from {@link #addaclentry(Request, PrintWriter)}. <p>
                * <i>(WebLearn; A Corfield 02/12/2003)</i>
                */
                private void userSearchFields( Request breq, PrintWriter out, PrimaryKey group_id, boolean withGroups, boolean withOutputOpts )
                        throws IOException
                        {
                        int i;
                        Group group;
                        User user;


                    user=(User)BuildingContext.getContext().getUser();
                        try
                            {
                    out.println( "<table border=0 CLASS=bs-table-opaque><tr>" );
                    out.println( "<td COLSPAN=3 CLASS=bs-cell-special><strong>Find Users</TD></tr>" );

                    if ( withGroups )
                        {

                        if (!breq.isAuthenticated()) {
                          out.println("<tr><td><HR>You haven't checked in to the building so you can't change access rights.<HR></TD></TR></table>");
                          return;
                        }

                        if (!BuildingContext.getContext().checkPermission(Permission.MANAGE)) {
                          out.println(
                              "<tr><td><HR>You need manage access rights to view access rights.<HR></TD><TR></table>");
                          return;
                        }

                        String base = breq.getParameter("category");
                        if (base == null)
                          base = "*";
                        base = base.trim();
                        if (!base.endsWith("*"))
                          base = null;
                        else
                          base = base.substring(0, base.length() - 1);

                        try {
                          Resource resource = breq.getResource();
                          org.bodington.server.realm.Acl acl = resource.getAcl();
                          //out.println( "<PRE>" + acl.toString() );
                          //Enumeration enumeration = acl.entries();
                          AclEntry aclentry;

                          Vector groups = new Vector();
                          Vector headings = new Vector();

                          Enumeration enumeration;
                          if (base == null || base.length() == 0) {
                            enumeration = org.bodington.server.realm.Group.findGroups(
                                 "name LIKE 'localgroup." + resource.getResourceId() + ".%'", "name");
                            while (enumeration.hasMoreElements())
                              groups.addElement(enumeration.nextElement());
                          }

                          if (base == null || base.length() == 0)
                            enumeration = org.bodington.server.realm.Group.findGroups(
                                "name NOT LIKE 'localgroup.%'", "name");
                          else
                            enumeration = org.bodington.server.realm.Group.findGroups(
                                "name LIKE '" + base + "%'", "name");

                          String name;
                          int dot_offset;
                          while (enumeration.hasMoreElements()) {
                            if (base == null)
                              groups.addElement(enumeration.nextElement());
                            else {
                              group = (org.bodington.server.realm.Group) enumeration.nextElement();
                              name = group.getName();
                              if (!name.startsWith(base))
                                continue;
                              dot_offset = name.indexOf('.', base.length());
                              if (dot_offset < 0 ||
                                  (base.length() == 0 && name.startsWith("localgroup.")))
                                groups.addElement(group);
                              else {
                                name = base + name.substring(base.length(), dot_offset) + ".*";
                                if (!headings.contains(name))
                                  headings.addElement(name);
                              }
                            }
                          }

                          out.println("<TR><TD align=left VALIGN=TOP><B>Search within group:</B></TD><td align=right VALIGN=TOP>");
                          out.print("<form method=POST ACTION=\"");
                          out.print(breq.absoluteURL());
                          out.println("bs_template_accessgroup.html\">");
                          out.print("<input type=hidden name=group_id value=");
                          out.print(group_id.toString());
                          out.println(" >");
                          out.println( "<INPUT type=hidden name=operation value=addform2>" );

                          if (base != null) {
                            out.print("<INPUT TYPE=HIDDEN NAME=category_url VALUE=\"");
                            out.print(breq.absoluteURL());
                            out.print("\"><INPUT TYPE=HIDDEN NAME=category_page VALUE=\"bs_template_");
                            out.print(breq.getPageName());
                            out.println("\">");
                            out.println("<SELECT NAME=category size=6>");
                            if (base.length() > 0) {
                              out.print("<OPTION VALUE=\"*\">*</OPTION>");
                            }
                            out.print("<OPTION SELECTED VALUE=\"");
                            out.print(base);
                            out.println("*\">");
                            out.print(base);
                            out.print("*");
                            out.println("</OPTION>");
                            for (i = 0; i < headings.size(); i++) {
                              out.print("<OPTION VALUE=\"");
                              out.print(headings.elementAt(i).toString());
                              out.print("\">");
                              out.print(headings.elementAt(i).toString());
                              out.println("</OPTION>");
                            }
                            out.println("</SELECT>");
                            out.println( "<br><INPUT TYPE=SUBMIT VALUE=\"Expand\">" );
                          }
                          else
                            out.println("<I>All categories</I>");
                          out.println("</form></td>");

                          out.println("<td valign=top align=right>");
                          out.println("<FORM METHOD=POST ACTION=bs_template_accessgroup.html>");
                          out.print("<input type=hidden name=operation value=add>");
                          out.print("<input type=hidden name=group_id value=");
                          out.print(group_id.toString());
                          out.println(" >");
                          out.println("<SELECT NAME=search_group_id size=10>");

                          for (i = 0; i < groups.size(); i++) {
                            group = (org.bodington.server.realm.Group) groups.elementAt(i);
                            //don't list group if current user hasn't got see access to it.
                            if (!group.checkPermission(Permission.SEE))
                              continue;
                            aclentry = acl.getAclEntry(group.getGroupId(), false);
                            //don't list group if it is in acl already
                            if (aclentry != null)
                              continue;
                            out.print("<OPTION VALUE=\"");
                            out.print(group.getGroupId().toString());
                            out.print("\">");
                            if (group.isLocalGroup())
                              out.print(group.getLocalName());
                            else
                              out.print(group.getName());
                            out.println("</OPTION>");
                          }
                          out.println("</SELECT></td>");
                        }
                        catch (Exception ex) {
                          out.println("<PRE>" + ex + "</PRE>");
                          return;
                        }
                      }
                      else {
                        out.println("<FORM METHOD=POST ACTION=bs_template_accessgroup.html>");
                        out.print("<input type=hidden name=operation value=add>");
                        out.print("<input type=hidden name=group_id value=");
                        out.print(group_id.toString());
                        out.println(" >");
                        out.println("<INPUT TYPE=HIDDEN NAME=search_group_id VALUE=null></td><tr>");
                      }
                      out.println("<TR><TD align=left VALIGN=TOP>");
                      out.println("<B>Search By Name or Unique Identifier:</B></TD><TD colspan=2 align=right VALIGN=TOP>");
                      out.println("<select name=identifier size=5>");
                      out.println("<option value=surname>Surname</option>");
                      out.println("<option value=user_id>WebLearn User ID</option>");
                      out.println( "<option value=user_name>External User Name</option>");
                      out.println( "<option value=webauth_user_name SELECTED>Oxford User Name</option>" );
                      Enumeration z_enum;
                      if (BuildingContext.getContext().checkPermission(Permission.SYSADMIN))
                        z_enum = Zone.findZones("zone_id IS NOT NULL");
                      else
                        z_enum = Zone.findZones("zone_id = " + user.getZoneId());

                      Enumeration a_enum;
                      Zone z;
                      Alias a;
                      while (z_enum.hasMoreElements()) {
                        z = (Zone) z_enum.nextElement();
                        a_enum = Alias.findAliases("zone_id = " + z.getZoneId());
                        while (a_enum.hasMoreElements()) {
                          a = (Alias) a_enum.nextElement();
                          out.print("<option value=");
                          out.print(a.getAliasId().toString());
                          out.print(">");
                          out.print(z.getName());
                          out.print(":");
                          out.print(a.getAliasName());
                          out.println("</option>");
                        }
                      }

                      out.println("</select></TD></TR><TR><TD VALIGN=TOP><B>Search For:</B><BR>");
                      out.println("(Enter a list of values one per line or a single"+
                            "value with or without a * wildcard.");
                      out.println("Use the _ underscore character to represent the " +
                            "space between unhyphenated double-barrelled surnames.)"
                          );
                      out.println("</TD><TD colspan=2 align=right VALIGN=TOP>");
                      out.println("<SPAN CLASS=bs-textarea><textarea name=values rows=10 cols=20></textarea></SPAN>");
                      out.println("</td></tr>");
                      if (withOutputOpts) {
                        out.println("<tr><td VALIGN=\"top\"><b>Output:</b></td><td>");
                        out.println(
                            "<INPUT TYPE=RADIO NAME=output_format VALUE=html CHECKED>Web Page<BR>");
                        out.println(
                            "<INPUT TYPE=RADIO NAME=output_format VALUE=text>TAB delimited text<br> ");
                        out.println("<INPUT TYPE=RADIO NAME=output_format VALUE=xml>XML file ");
                        out.println("</td></tr>");
                      }

                      out.println("<TR><TD VALIGN=TOP><B>Administrative Zone</B></TD><TD colspan=2 align=left VALIGN=TOP>");
                      out.println("<INPUT TYPE=RADIO NAME=in_zone VALUE=zone_any CHECKED>Search entire site.<BR>");
                      out.println("<INPUT TYPE=RADIO NAME=in_zone VALUE=zone_restricted>Search within ");
                      out.println(user.getZone().getName());
                      out.print("</TD></tr>");
                      out.print("<tr><TD colspan=3 align=right VALIGN=TOP><input type=submit value=\"Search\"></TD></tr>");

                      out.println("</form></TABLE>");
                    }
                    catch (BuildingServerException bsex) {
                      out.println("Technical error trying to produce form: ");
                      logException(out, "Facility", "userSearchFields",
                                   "Technical error trying to produce form: ",
                                   bsex);
                    }
                  }


	/* (non-Javadoc)
     * @see org.bodington.servlet.facilities.ResourceCreator#createCheck(org.bodington.servlet.Request, java.io.PrintWriter)
     */
	public List validate( Resource newResource )throws BuildingServerException
	{
	    List errors = new LinkedList();
        String url = newResource.getName();
        if ( url == null || url.length()<1 )
        {
            errors.add("Name cannot be empty");
        }
        else if (!url.matches("[0-9a-z][0-9a-z_]+"))
        {
            errors.add("Name can only contain the characters a to z, 0 to 9 and underscore");
        }
        String title = newResource.getTitle();
        if (title == null || title.length() == 0) 
        {
            errors.add("Title cannot be empty");
        }
        
        Parser parser;
        ReportingVisitor visitor;
        
        parser = HtmlFilterFactory.getParser(newResource.getDescription());
        visitor = HtmlFilterFactory.filter(parser,
            HtmlFilterFactory.FRAGMENT);
        if (visitor.getMessages().size() > 0)
        {
            for(Iterator it = visitor.getMessages().iterator(); it.hasNext();)
            {
                errors.add("Problem with Description: "+ it.next());
            }
        }
        
        parser = HtmlFilterFactory.getParser(newResource.getIntroduction());
        visitor = HtmlFilterFactory.filter(parser,
            HtmlFilterFactory.BODY);
        if (visitor.getMessages().size() > 0)
        {

            for(Iterator it = visitor.getMessages().iterator(); it.hasNext();)
            {
                errors.add("Problem with Introduction: "+ it.next());
            }
        }
        
        return errors;
     }
            

	/*
     * (non-Javadoc)
     * @see org.bodington.servlet.facilities.ResourceCreator#create(org.bodington.servlet.Request,
     *      java.io.PrintWriter, java.sql.Connection,
     *      org.bodington.server.resources.Resource)
     */
    public List postCreate(Request breq, Resource newResource) throws Exception
    {
        String show = breq.getParameter("show");
        if (show != null && show.length() > 0)
        {
            Group publicGroup = Group.findGroupByName("public");
            if (publicGroup != null)
            {
                Acl acl = newResource.getAcl();
                AclEntry publicAcl = new AclEntry();
                publicAcl.addPermission(Permission.SEE);
                publicAcl.setGroup(publicGroup);
                acl.addEntry(publicAcl);
                acl.save();
            }
            else
            {
                log.error("Couldn't find public group.");
            }
        }
        return Collections.EMPTY_LIST;
    }


	/**
     * Attempt to create a new resource. This checks that the caller has
     * permission to create the resource. <br />
     * Default values can be specified in the calling template, which will be
     * used if the form field has been left blank. <br />
     * If the name supplied for the new resource is already in use at this
     * location, a modified version of the name is used.
     * @param breq The {@link Request}from which the resource values are
     *        pulled.
	 * @param out PrintWriter to write any messages.
     * @param name The shortname of the resource to create. This should refer to
     *        the facility name in the {@link FacilityList}.
     * @param navigation_links Flag to indicate whether to display links to
     *  new resource and parent after creation.
     * @return The newly created resource, or null.
	 * @throws IOException
	 */
	protected Resource createconfirm( Request breq, PrintWriter out, String name, boolean navigation_links )
		throws IOException
		{
	    FacilityList fl;
	    Facility f;
	    Connection con=null;
	    ResourceHierarchy tree=null;
	    String url, str;
	    String title, description, introduction;
	    boolean parentacl;
	    Resource new_resource=null;
	    boolean completed=false;
	    BuildingContext context = BuildingContext.getContext();

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

	    if ( !breq.isAuthenticated() )
	    {
	        out.println( "<br />Problem finding user." );
	        return null;
	    }


	    fl=FacilityList.getFacilities();
	    if ( fl==null )
	    {
	        out.println( "<br />Problem finding facilities." );
	        return null;
	    }
	    f=fl.get( name );

	    if ( f==null )
	    {
	        out.println( "<br />Unable to create this type of resource." );
	        return null;
	    }

	    try
	    {

	    	tree = new ResourceHierarchyImpl();
	    	new_resource=f.newResource();
	    	new_resource.setHttpFacilityNo( f.id.intValue() );

	    	// XXX Evil hack but the resource restrictions need to be sorted out.
	    	boolean groupInGroup = 
	    		Resource.RESOURCE_GROUPCONTAIN == new_resource.getResourceType() 
	    		&&
	    		Resource.RESOURCE_GROUPCONTAIN == breq.getResource().getResourceType(); 
	    	if (!groupInGroup
	    			&& ( f.isSysadmin()
	    					&& !breq.getResource().checkPermission(Permission.SYSADMIN)))
	    	{
	    		out.println( "<br />Unable to create new resource.");
	    		out.println( "<br />You need to have Sysadmin rights to create this resource." );
	    		return null;
	    	}

	    	if (!willAcceptFacility(f))
	    	{
	    		out.println("<br />Unable to create resource.<br />" );
	    		out.println("You cannot create that resource here.");
	    		return null;
	    	}

	    	if (!f.isCreate())
	    	{
	    		out.println("<br />Unable to create resource.<br />" );
	    		out.println("You cannot create new resources of this type.");
	    	}

	    	List errors;
	    	errors = f.initResource(breq, new_resource);
	    	if (errors.size() != 0)
	    	{
	    		printErrors(out, errors.iterator());
	    		return null;
	    	}

	    	errors = f.validate(new_resource);
	    	if (errors.size() != 0)
	    	{
	    		printErrors(out, errors.iterator());
	    		return null;
	    	}
	    	if (ResourceTreeManager.getInstance().findResource(breq.getResource().getFullName()+"/"+new_resource.getName())!= null)
	    	{
	    		out.println( "<br />Unable to create new location - an item already exists here with the a name the same as the one you specified for the new item.<br />" );
	    		out.println( "Please use the backtrack facility of your browser to return to the form." );
	    		return null;
	    	}

	    	if (!tree.addResource( breq.getResource(), new_resource ))
	    	{
	    		throw new BuildingServerException("Problem adding resource to tree.");
	    	}

	    	//specific creation stuff
	    	errors = f.postCreate(breq, new_resource);
	    	if ( errors.size() != 0)
	    	{
	    		printErrors(out, errors.iterator());
	    		throw new BuildingServerException( "Problem creating resource specific data in database." );
	    	}

	    	completed=true;
	    	out.println( "<B>New resource created successfully.</B>" );

	    	if ( navigation_links )
	    	{
	    		// uhi:awc start update look & feel
	    		out.print("<br /><br />Go to <A TARGET=_top title=\"New resource\" HREF=\"");
	    		out.print( breq.getContextPath() );
	    		out.print( breq.getServletPath() );
	    		out.print( new_resource.getFullName() );
	    		out.print( "\">New</A> resource");

	    		out.print(" or back to <A TARGET=_top title=\"Resource menu\" HREF=\"");
	    		out.print( breq.getContextPath() );
	    		out.print( breq.getServletPath() );
	    		out.print( breq.getResource().getFullName() );
	    		out.print( "\">Menu</A> of resources");
	    		// uhi:awc end update look & feel

	    	} 

	    }
	    catch (QuotaExceededException qee)
	    {
	        out.println("<br />Sorry the resource could not be created "
                + "because you have reached your maximum quota of "
                + qee.getQuota().getQuota()+ " resources");
	    }
	    catch ( Exception ex )
	    {
	        out.println( "<br />Unable to create resource." + ex );
	        logException( out, "Facility", "createConfirm", 
	            "Unable to create resource.",
	            ex );
	        //just drop through;
	    }
	    if ( breq.getParameter( "keywords" ) != null )
	    {
	        String []keywords = 
	            XMLMetadataUtils.extractKeywordsFromString( breq.getParameter( "keywords" ) );
	        try
	        {
	        	BuildingSession session = BuildingSessionManagerImpl.getSession(new_resource);
	        	session.updateMetadataKeywords(keywords, "en");
	        }
	        catch (BuildingServerException bse)
	        {
	        	log.warn("Failed to update keywords");
	        }
	    }
	    return new_resource;
		}

	/**
	 * Gets the value of the named request parameter.
	 * If it has no value, a default hidden parameter value is used, if present.
	 * Responsible for trimming param value, and checking that it is not an empty string.
	 * @param breq the Request object.
	 * @param paramName The name of the parameter to check.
	 */
	protected String getParamValue( HttpServletRequest breq, String paramName)
	{
	    String returnValue;
	    String defaultParamName = "default" + paramName;
	    
	    if ( breq.getParameter(paramName) != null )
	    {
	        returnValue = breq.getParameter(paramName).trim();
	        if ( returnValue.length() > 0 )
	            return returnValue;  
	    }
	    
	    if ( breq.getParameter( defaultParamName ) != null )
	    {
	        returnValue = breq.getParameter(defaultParamName).trim();
	        if ( returnValue.length() > 0 )
	            return returnValue; 
	    }
	    
	    return null;
	}
	
	protected void confirmmodify( Request breq, PrintWriter out )
		{
		Resource resource, parent, otherresource;
		ResourceTree tree;
        
        if ( !checkModifyTemplateParameters( breq, out ) ) 
            return;

		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
			{
			out.println( "<PRE>You don't have permission to modify this location.</PRE>\n" );
			return;
			}
		
		if ( !breq.isAuthenticated() )
			{
			out.println( "<HR>Problem finding user.<HR>" );
			return;
			}

		try
			{
			resource = BuildingContext.getContext().getResource();
			tree = ResourceTreeManager.getInstance();

			String url, title, description, introduction, keywords;
			url=breq.getParameter( "url" );
			if ( url==null ) url="";
			else url=url.trim();
			if ( url.length()<1 )
				{
				out.println( "<HR>Unable to modify location - you must supply a name.<HR>" );
				return;
				}
			if ( url.length()>12 )
				{
				out.println( "<HR>Unable to modify location - name is more than 12 characters.<HR>" );
				return;
				}
			for ( int i=0; i<url.length(); i++ )
				{
				char c=url.charAt( i );
				if ( c>='0' && c<='9' )
					continue;
				if ( c>='a' && c<='z' )
					continue;
				if ( c != '_' )
					{
					out.println( "<HR>Unable to modify location - the name contains invalid characters.<HR>" );
					return;
					}
				}
			
			//test new url to see if it exists
			parent = resource.getParent();
			if ( parent!=null )
				{
				String new_full_name = parent.getFullName() + url + "/";
				otherresource = tree.findResource( new_full_name );
				if ( otherresource!=null && otherresource!=resource )
					{
					out.println( "<HR>Unable to modify location - the name is already in use.<HR>" );
					return;
					}
				}
			
			title=breq.getParameter( "title" ).trim();
			description=breq.getParameter( "description" ).trim();
			introduction=breq.getParameter( "introduction" ).trim();
            keywords=breq.getParameter( "keywords" );

			if ( title.length()==0 )
				{
				out.println( "<HR>Unable to modify location - there must be text for title.<HR>" );
				return;
				}
            
            Parser parser;
            ReportingVisitor visitor;
            
            parser = HtmlFilterFactory.getParser(description);
            visitor = HtmlFilterFactory.filter(parser,
                HtmlFilterFactory.FRAGMENT);
            if (visitor.getMessages().size() > 0)
            {
                printErrors(out, "Description", visitor.getMessages()
                    .iterator());
                return;
            }
            
            parser = HtmlFilterFactory.getParser(introduction);
            visitor = HtmlFilterFactory.filter(parser,
                HtmlFilterFactory.BODY);
            if (visitor.getMessages().size() > 0)
            {
                printErrors(out, "Introduction", visitor.getMessages()
                    .iterator());
                return;
            }
            
            parser = HtmlFilterFactory.getParser(keywords);
            visitor = HtmlFilterFactory.filter(parser,
                HtmlFilterFactory.FRAGMENT);
            if (visitor.getMessages().size() > 0)
            {
                printErrors(out, "Keywords", visitor.getMessages()
                    .iterator());
                return;
            }
            
            resource.setName( url );
			resource.setTitle( EscapedHtmlWriter.filter(title) );
			resource.setIntroduction( introduction );
			resource.setDescription( description );
			resource.save();
			
			// modify metadata record
			try
   			{
			    refreshMetadata(resource, XMLMetadataUtils.extractKeywordsFromString(keywords));
   			}
			catch ( BuildingServerException bsex )
				{
				logException( out, "Facility", "confirmmodify", 
				    "Unable to save the metadata: " +bsex.getMessage(),
				    bsex );
				}
            
			ResourceEvent event = new ResourceEvent(ResourceEvent.EVENT_EDIT, resource);
            event.setActiveUser((User)BuildingContext.getContext().getUser());
            event.save();
            
			out.println("<B>Resource modified successfully.</B><BR />" );
            out.println("<br />Please use one of the links below to reload all frames " +
                      "(the pages you are looking at may not reflect the new URL of the resource)<br /><br />" );
            
            out.print("Return to <A TARGET=_top title=\"Current resource\" HREF=\"");
            out.print( breq.getContextPath() );
            out.print( breq.getServletPath() );
            out.print( breq.getResource().getFullName() );
            out.print( "\">current</A> resource");

            if (breq.getResource().getParent() != null)
            {
            	out.print(", or back to containing ");
                out.print("<A TARGET=_top title=\"Parent resource\" HREF=\"");
                out.print(breq.getContextPath());
                out.print(breq.getServletPath());
                out.print(breq.getResource().getParent().getFullName());
                out.print("\">parent</A> resource");
            }
            out.println(".");            
            return;
			}
		catch ( BuildingServerException bex )
			{
			out.println( "<br />Unable to modify resource due to technical problem." );
			}

		}
    
    /**
     * Check the template parameters of the modify request. This is called from
     * the method {@link #confirmmodify(Request, PrintWriter)}. If this method
     * returns <code>true</code>, the calling method proceeds. If this method
     * returns <code>false</code>, then the calling method will exit. By
     * default, if any template parameters are found then this method will
     * return <code>false</code>, as this probably reflects the fact that
     * these are not template parameters as such, but in fact form the path to a
     * non-existent or already modified resource. However, this method is
     * provided as an override point to subclasses, as this method will clearly
     * fail for facilities that use template parameters as a normal part of
     * their operation.
     * @param request the current request.
     * @param out an object to write any message to.
     * @return <code>true</code> if the template parameters of the modify
     *         request are legitimate, otherwise <code>false</code>.
     * @see #confirmmodify(Request, PrintWriter)
     * @see Request#getTemplateParameterCount()
     * @see Request#getTemplateParameter(int)
     */
    protected boolean checkModifyTemplateParameters( Request request,
        PrintWriter out )
    {
        if ( request.getTemplateParameterCount() > 0 )
        {
            out.println( "<HR>The resource was NOT modified because the web "
                + "address of the 'modify resource' page you came from is "
                + "invalid.  This may have happened because you already "
                + "changed the resource and you backtracked to an out-of-date "
                + "page.  Please use the navigation bar to exit from here and "
                + "review what changes have been made.<HR>" );
            return false;
        }

        return true;
    }

    /**
     * Prints out errors in validation associated with a field.
     * @param out The printwriter to write to.
     * @param field The field that the user should edit.
     * @param iterator An iterator containing the errors to write.
     */
    protected void printErrors(PrintWriter out, String field, Iterator iterator)
    {
        out.println("<HR>");
        out.println(field);
        out.println(" is not valid.<P>");
        EscapedHtmlWriter htmlOut = new EscapedHtmlWriter(out);
        for (; iterator.hasNext();)
        {
            htmlOut.print(iterator.next().toString());
            out.println("<br/>");
        }
        out.println("<HR>");
    }

    /**
     * Prints out errors.
     * @param out The printwriter to write to.
     * @param iterator An iterator containing the errors to write.
     */
    protected void printErrors(PrintWriter out, Iterator iterator)
    {
        out.println("<HR>");
        EscapedHtmlWriter htmlOut = new EscapedHtmlWriter(out);
        for (; iterator.hasNext();)
        {
            htmlOut.print(iterator.next().toString());
            out.println("<br/>");
        }
        out.println("<HR>");
    }
    

    /**
     * Used to handle timed resource date properties on the current resource.
     * This can be used to set and get timed resource date properties. This
     * method is invoked from
     * {@link #insert(Request, PrintWriter, String, String)}.
     * @param breq the request object.
     * @param out the writer object.
     * @param name parameter which can accept the values <code>open_field</code>,
     *        <code>close_field</code> and <code>set</code>.
     * @see Resource#getOpenDate()
     * @see Resource#getCloseDate()
     * @see #insert(Request, PrintWriter, String, String)
     */
    private void releaseDate( Request breq, PrintWriter out, String name )
        throws IOException
    {
        Resource resource = null;
        java.util.Date openDate = null, closeDate = null;
        String open_date = null, close_date = null;

        try
        {
            resource = BuildingContext.getContext().getResource();
        }
        catch ( BuildingServerException bex )
        {
            out.println( "<HR>Unable to modify resource due to technical "
                + "problem - resource not found<HR>" );
            return;
        }

        if ( name.equalsIgnoreCase( "open_field" ) )
        {
            openDate = resource.getOpenDate();
            if ( openDate == null )
                open_date = "none";
            else
                open_date = DateFormatter.formatDate(openDate, DateFormatter.MEDIUM );
            out.print( "<INPUT CLASS=\"datetime\" NAME=\"open_date\" VALUE=\"" );
            out.print( open_date );
            out.println( "\">" );
            return;
        }

        if ( name.equalsIgnoreCase( "close_field" ) )
        {
            closeDate = resource.getCloseDate();
            if ( closeDate == null )
                close_date = "none";
            else
                close_date = DateFormatter.formatDate(closeDate, DateFormatter.MEDIUM );
            out.print( "<INPUT CLASS=\"datetime\" NAME=\"close_date\" VALUE=\"" );
            out.print( close_date );
            out.println( "\">" );
            return;
        }

        if ( name.equalsIgnoreCase( "set" ) )
        {
            if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
            {
                out.println( "<PRE>You do not have permission to modify this "
                    + "location.</PRE>" );
                return;
            }

            if ( !breq.isAuthenticated() )
            {
                out.println( "<br />Problem finding user." );
                return;
            }
            try
            {
                boolean openOk = false, closeOk = false;
                java.sql.Timestamp openTimestamp = null, closeTimestamp = null;

                open_date = breq.getParameter( "open_date" );
                if ( open_date != null && open_date.length() > 0 )
                {
                    open_date = open_date.trim();
                    if ( open_date.equalsIgnoreCase( "none" ) )
                        openOk = true;
                    else
                    {
                        openDate = DateParser.parse( open_date );
                        if ( openDate != null )
                        {
                            openTimestamp = new Timestamp( openDate.getTime() );
                            open_date = DateFormatter.formatDate( openDate, DateFormatter.MEDIUM );
                            openOk = true;
                        }
                    }
                }

                close_date = breq.getParameter( "close_date" );
                if ( close_date != null && close_date.length() > 0 )
                {
                    close_date = close_date.trim();
                    if ( close_date.equalsIgnoreCase( "none" ) )
                        closeOk = true;
                    else
                    {
                        closeDate = DateParser.parse( close_date );
                        if ( closeDate != null )
                        {
                            closeTimestamp = new Timestamp( closeDate.getTime() );
                            close_date = DateFormatter.formatDate( closeDate, DateFormatter.MEDIUM );
                            closeOk = true;
                        }
                    }
                }

                if ( openTimestamp != null && closeTimestamp != null
                    && closeTimestamp.compareTo( openTimestamp ) < 1 )
                {
                    openOk = false;
                    closeOk = false;
                }

                if ( !(openOk && closeOk) )
                {
                    if ( openTimestamp != null && closeTimestamp != null )
                        out.print( "<br />Unable to modify location - the close "
                            + "date and time must be later than the open date" );
                    else
                        out.print( "<br />Unable to modify location - the date "
                            + "and time may not have been formatted correctly" );
                    if ( !openOk ) 
                        out.print( "<br />open date: " + open_date );
                    if ( !closeOk )
                        out.print( "<br />close date: " + close_date );
                    out.print( "<br />Enter the date and time as "
                        + "<b>dd/mm/yy hh:mm</b> or \"none\" if no date is set");
                    return;
                }

                resource.setOpenTimestamp( openTimestamp );
                resource.setCloseTimestamp( closeTimestamp );
                resource.save();

                out.print( "<b>Resource modified successfully.</b><br /> "
                    + "Select <a target=_top href=\"" );
                out.print( breq.getContextPath() );
                out.print( breq.getServletPath() );
                out.print( resource.getFullName() );
                out.print( "\">this link</a> to reload all frames." );

                if ( openTimestamp == null && closeTimestamp == null )
                {
                    out.print( "<br />This resource will always be available - "
                        + "neither an open time nor a close time has been set." );
                }
                else
                {
                    if ( closeTimestamp == null )
                        out.print( "<br />This resource will be available from "
                            + open_date + " - no close time has been set." );
                    else if ( openTimestamp == null )
                        out.print( "<br />This resource is available until "
                            + close_date + " - no open time has been set." );
                    else
                        out.print( "<br />This resource will be available from "
                            + open_date + " until " + close_date + "." );
                    if ( openTimestamp != null
                        && openTimestamp.getTime() <= System.currentTimeMillis() )
                        out.print( "<br /><b>N.B.</b> The open date has already "
                            + "passed." );
                    if ( closeTimestamp != null 
                        && closeTimestamp.getTime() <= System.currentTimeMillis() )
                        out.print( "<br /><b>N.B.</b> The close date has already "
                            + "passed." );
                }

            }
            catch ( BuildingServerException bex )
            {
                out.println( "<br />Unable to modify resource due to technical "
                    + "problem." );
            }
            catch ( NumberFormatException nfex )
            {
                out.println( "<br />Unable to modify resource due to invalid "
                    + "ordinal." );
            }
        }
    }
	
	public boolean willAcceptFacility( Facility f )
		{
		return contains.contains(f);
		}
	
	
	private void moveconfirm( Request breq, PrintWriter out )
		throws IOException
		{
		String item, dst;
		Resource srcres, itemres = null;
		Resource dstres;

        
        if ( breq.getTemplateParameterCount() > 0 )
        {
            out.println( "<br />The resource was NOT moved because it seems the destination resource has been " +
                         "changed while you were filling in the form.  Please use the navigation bar to reset " +
                         "your view onto the location where you want to move resources." );
            return;
        }        
        
		try
			{
			dstres = BuildingContext.getContext().getResource();

			log.debug( "moveconfirm() A" );
			
			item=breq.getParameter( "source" );
			dst=breq.getParameter( "destination" );
			
			if ( dst!=null )
				{
				out.println( "<br />Moving objects by pushing is no longer supported." );
				return;
				}
			
			if ( item==null )
				{
				out.println( "<br />Item to be moved must be specified." );
				return;
				}

            if ( !item.endsWith( "/" ) )
                item= item + "/";

            BodingtonURL bodURL = new BodingtonURL( breq );
            if (bodURL.isBodingtonUrl( item ))
                itemres = bodURL.getResource( item );
            else
            {
                if ( !item.startsWith( "/" ) )
                    item= "/" + item;
                if ( item.length() < 3 )
                {
                    out.println( "<br />Name too short." );
                    return;
                }

                itemres=ResourceTreeManager.getInstance().findResource( item );
            }

            if ( itemres==null )
				{
				out.println( "<br />The item you want to move wasn't found." );
				return;
				}

			srcres=itemres.getParent();

			if ( srcres==null )
				{
				out.println( "<br />The source location you specified wasn't found." );
				return;
				}
			if ( dstres==null )
				{
				out.println( "<br />The destination location you specified wasn't found." );
				return;
				}


			// does the destination facility accept this type of facility?
			// (e.g. you can't put a discussion room inside an MCQ test paper)
			FacilityList fl = FacilityList.getFacilities();
			Facility dstfac = fl.get( new Integer( dstres.getHttpFacilityNo() ) );
			Facility itemfac = fl.get( new Integer( itemres.getHttpFacilityNo() ) );
			if ( !dstfac.willAcceptFacility( itemfac ) )
				{
				out.println( "<br />The destination location cannot contain the type of resource you want to move." );
				return;
				}

			if ( !itemres.checkPermission( Permission.MANAGE ) )
				{
				out.println( "<br />You don't have management permission for the item to be moved." );
				return;
				}
			if ( !dstres.checkPermission( Permission.CREATE ) )
				{
				out.println( "<br />You don't have creation permission for the destination location." );
				return;
				}

			// we have to check whether dstloc is 'inside' itemloc
			if ( dstres.isInside( itemres ) )
				{
				out.println( "<br />The destination location you specified is inside the item being moved." );
				return;
				}

			if ( dstres.findChild(itemres.getName()) != null )
				{
					out.println( "<br />The destination location already contains an item with the same name as the item you are trying to move." );
					return;
					}


			
            ResourceTreeManager.getInstance().moveResource( dstres, itemres );
			out.println( "<br />The requested item was moved." );
			return;
			}
		catch ( BuildingServerException bex )
			{
			out.println( "<br />There was an unexpected problem moving the item.<br />" + bex );
			}

		return;
		}

	
    /**
     * Makes a copy of an existing Resource. It is possible to copy a resource
     * to the same location as the original, and multiple copies can be made. 
     * <br />
     * If a resource already exists at the destination location with the same
     * name as the resource to be copied, a number is appended to the duplicated
     * resource name (which is truncated if necessary).<br />
     * Request parameters are used to set the number of copies to be made, and
     * whether to copy child resources, access permissions, content and uploaded
     * files.
     * @param breq The building HTTP request, which is used to determine which
     *        type of content to copy.
     * @param out A Print writer, to display messages to the user interface.
     */
	private void copyconfirm( Request breq, PrintWriter out )
	    {
        if ( breq.getTemplateParameterCount() > 0 )
        {
            out.println( "<br />The resource was NOT copied because it seems the destination resource has been " +
                         "changed while you were filling in the form.  Please use the navigation bar to reset " +
                         "your view onto the location where you want to copy resources." );
            return;
        }            

         if ( copy_in_progress )
	     {
	       out.println( "<br />A copy operation is in progress on this web site and another cannot be started until it has completed." );
	       return;
	     }
            
            
	     String sourceURL;
 	     Resource original = null, destination;
	     FacilityList facilityList;
	     Facility facility;
	     int multiple_copies;

	     log.debug( "starting copyconfirm()" );

	     sourceURL=breq.getParameter( "source" );

	     if ( sourceURL == null )
	     {
	       out.println( "<br />Item to be copied must be specified." );
	       return;
	     }

	     try
	     {
	         BodingtonURL bodURL = new BodingtonURL( breq );
	         if (bodURL.isBodingtonUrl( sourceURL ))
	             original = bodURL.getResource( sourceURL );
	         else
	         {
	             if ( !sourceURL.startsWith( "/" ) )
	                 sourceURL = "/" + sourceURL;
	             if ( sourceURL.length() < 3 )
	             {
	                 out.println( "<HR>Name too short.<HR>" );
	                 return;
	             }
                 
	             original = ResourceTreeManager.getInstance().findResource( sourceURL );
             }
             	         
	         if ( original == null )
	         {
	             out.println( "<br/ >The item you want to copy wasn't found." );
	             return;
	         }

	       BuildingContext context = BuildingContext.getContext();
	       destination = context.getResource();

	       facilityList = FacilityList.getFacilities();
	       if ( facilityList==null )
	       {
	           out.println( "<br />Problem finding facilities." );
	           return;
	       }

	       if ( destination==null )
	       {
	           out.println( "<br />The destination location wasn't found." );
	           return;
	       }

	       if ( destination.isInside( original ) )
	       {
	           out.println( "<br />The destination location you specified is inside the item being copied." );
	           return;
	       }

	       if ( destination.equals( original ) )
	       {
	           out.println( "<br />The destination location you specified is inside the item being copied and is not allowed.<br />");
	           out.println( "If you have a query about this, then please contact the Administrator or Help Desk for this web site." );
	           return;
	       }

	       Facility dstfac = facilityList.get( new Integer( destination.getHttpFacilityNo() ) );
	       facility = facilityList.get( new Integer( original.getHttpFacilityNo() ) );
	       if ( !dstfac.willAcceptFacility( facility ) )
	       {
	        out.println( "<br />The destination location cannot contain the type of resource you want to copy." );
	        return;
	       }

           if ( !original.checkPermission( Permission.MANAGE ) )
	       {
	        out.println( "<br />You don't have management permission for the item to be copied." );
	        return;
	       }
	       if ( !destination.checkPermission( Permission.CREATE ) )
	       {
	        out.println( "<br />You don't have creation permission for the destination location." );
	        return;
	       }

           // JRM added check for resources that can't be copied.
           if ( !facility.canCopy( original ) )
           {
	        out.println( "<br />The selected resource is of a type that cannot be copied." );
	        return;               
           }
           if ( "true".equalsIgnoreCase( breq.getParameter("recursive") ) &&
                !facility.canCopyWithChildren( original ) )
           {
	        out.println( "<br />The selected resource contains one or more resources that cannot be copied because you don't have manage access or they are of a type that cannot be copied." );
	        return;               
           }
           
           // JRM added sanity check for resource to be copied
           if ( "true".equalsIgnoreCase( breq.getParameter("recursive") ) )
           {
               int descendents = ResourceTreeManager.getInstance().countDescendents( original.getResourceId() );
               if ( descendents<0 )
               {
    	        out.println( "<br />Error determining how many resources need to be copied." );
                return;               
               }
               if ( descendents>100 )
               {
    	        out.println( "<br />You opted to copy resources inside the selected resource but there are too many ("+descendents+") and the limit is 100." );
                return;               
               }
           }

           
	     }

	     catch ( BuildingServerException ex )
	     {
	       out.println( "<br />Unable to copy resource (Copying not started)<br />" + ex );
	       logException( out, "Facility", "copyConfirm",
			    "Unable to copy resource.", ex );
	       return;
	     }

	     try
	     {

	       log.debug( "copyconfirm(), starting to copy resources... " );

           // JRM - added checks to user input here in case a different template
           // form is used in the future and in case some user puts together their
           // own form to make a denial of service attack.
           try
           {
               multiple_copies=Integer.parseInt(breq.getParameter( "multiple" ));
           }
           catch ( NumberFormatException nfe )
           {
               out.println( "<br />Unable to determine the number of copies of resource to make." );
	           logException( out, "Facility", "copyConfirm",
			        "Unable to determine the number of copies of resource to make.", nfe );
	           return;
           }
           
           if ( multiple_copies<1 )
           {
               out.println( "<br />Unable to determine the number of copies of resource to make." );
               log.error( "Unable to determine the number of copies of resource to make." );
	           return;               
           }
           if ( multiple_copies>20 )
           {
               out.println( "<br />Unable to make that many copies." );
               log.error( "Unable to make that many copies." );
	           return;               
           }
           
           String originalName = original.getName();
           
           out.println( "<p>");
	       for ( int i = 1; i <= multiple_copies; i++ )
	       {
	        String name = breq.getBuildingSession().findUniqueName(originalName);

            // impossible for two threads to enter this block of code
            synchronized ( copy_synch )
            {
                try
                {
                    copy_in_progress = true;
	                copyResource( original, destination, breq, name, out );
                }
                finally
                {
                    copy_in_progress = false;                
                }
            }

            if ( multiple_copies>1 )
            {
                out.println( "<br />Completed " + i + (i==1?" copy":" copies") + "." );
                out.flush();
            }
           }

            if ( multiple_copies>1 )
                out.println( "<br /><br /><strong>All copies completed.</strong>" );
            else
                out.println( "<br /><br /><strong>New resource/s copied successfully.</strong>" );
	        out.println( "</p>");
	     }

	     catch ( BuildingServerException ex )
	     {
	       out.println( "<br />Error in attempting to copy resource.<br />" );
	       logException( out, "Facility", "copyConfirm",
			    "Error in attempting to copy resource.", ex );
	     }
	}


    /**
     * Copy a resource. Depending on the values of the specified parameters,
     * this can be recursive, include access permissions and uploaded content.
     * Used by {@link #copyconfirm(Request, PrintWriter)}.
     * @param original The resource to be copied.
     * @param destination The resource which will be the parent of the newly
     *        created resource.
     * @param breq The building HTTP request, which is used to determine which
     *        type of content to copy.
     * @param name The name to be used for the new resource (or null if the name
     *        is to be copied from the original.)
     * @param out A Print writer, to display messages to the user interface.
     * @exception BuildingServerException Thrown if there is any problem copying
     *            the resource.
     */
	private void copyResource( Resource original, Resource destination, Request breq, String name, PrintWriter out )
	        throws BuildingServerException
	        {
	         ResourceTree tree = null;
	         BuildingContext context = BuildingContext.getContext();
	         FacilityList facilityList = FacilityList.getFacilities();
	         Facility facility = facilityList.get( new Integer( original.getHttpFacilityNo() ) );
	         Resource new_resource = null;
	         Connection con = null;

	         try
	         {
	          tree = ResourceTreeManager.getInstance();
	          synchronized ( tree ) // some comments copied from createconfirm() method...
	          {
	           new_resource = facility.newResource();
               new_resource.setZoneId( original.getZoneId() ); // added JRM
	           if ( name==null )
	             name = original.getName();
	           new_resource.setName( name );
	           new_resource.setTitle( original.getTitle() );
	           new_resource.setDescription( original.getDescription() );
	           new_resource.setIntroduction( original.getIntroduction() );
	           new_resource.setHttpFacilityNo( original.getHttpFacilityNo() );
               new_resource.setHttpUIStyle( original.getHttpUIStyle() );
               new_resource.setHasCustomMenu( original.getHasCustomMenu() ); // added JRM

               // initResource sets fields in subclass of Resource BEFORE saving it.
	           //new version of initResource(), takes original as arg:
	           facility.initResource( original, new_resource );

	           con = context.getConnection();
	           //may need to rollback after several operations
	           con.setAutoCommit( false );
	           tree.addResource( destination, new_resource );
	           //tree.updateIndices();
	           //tree.saveAll();

	           //specific creation stuff

	           con.commit();
	           con.setAutoCommit( true );
	          }
	         }
	         catch (QuotaExceededException qee)
	         {        
	             // Special case as we don't want to try to create any more.
	             try
	             {
	                 tree.removeResource( new_resource );
	                 con.rollback();
	             }
	             catch ( Exception ex2 )
	             {
	                 log.error( "Failed to rollback.", ex2);
	             }
	             throw qee;
	         }
	         catch ( Exception ex )
	         {
	          String message = "Resource creation failed, ";
	          try
	          {
	           tree.removeResource( new_resource );
	           con.rollback();
	          }
	          catch ( Exception ex2 )
	          {
	           message += "unable to roll back database operations.";
	           log.error( message );
	           throw new BuildingServerException(ex.getMessage(), message );
	          }
	          message += "database rolled back.";
	          log.error( message );
	          throw new BuildingServerException(ex.getMessage(), message  );
	         }

	         if ( breq.getParameter("permissions") != null )
	           copyAccessPermissions( original, new_resource, out );

	         if ( breq.getParameter("uploaded") != null )
	           copyUploadedContent( original, new_resource, out, breq);

             // copy authored content and user data:
	         try
	         {
	          facility.copyContent( original, new_resource, breq );
	         }
	         catch (BuildingServerException ex)
	         {
	          String message = "Error copying content for: "+ new_resource.getName();
	          // using getFullName() could throw another exception...
	          out.print( "<br />"+ message +"<br />"+ ex.getMessage());
	          log.warn( message + ex.getMessage() );
	         }

	         // create metadata record
	         try
	         {
	             BuildingSession session = BuildingSessionManagerImpl.getSession( original );
                 String[] keywords = session.getFieldValuesFromMetadata( "keywords" );
                 
                 refreshMetadata(new_resource, keywords);
              
	         }
	         catch ( Exception ex )// BuildingServerException
	         {
	          out.print( "<HR>Unable to save the associated metadata for the newly created item.");
	         }

	         if ( breq.getParameter("recursive").equalsIgnoreCase("true") )
	         {
	          Enumeration enumeration = original.findChildren();
	          while ( enumeration.hasMoreElements() )
	          {
	           Resource child = (Resource)enumeration.nextElement();
		   if ( !child.equals(new_resource) )
	           try
	           {
	            copyResource( child, new_resource, breq, null, out );
	           }
		       catch (QuotaExceededException qee)
		       {
		           // If we hit quota problems stop straight away.
		           throw qee;
		       }
	           catch (BuildingServerException ex)
	           {
	            String message = "Error copying child resource: "+ child.getName();
	            out.print( "<br />"+ message +"<br />"+ ex.friendlyMessage());
	            log.error( message + ex.getMessage() );
	           }
	          }
	         }

	}


	/**
	 * Update the metadata on a resource.
	 * @param resource The resource to update the metadata on.
	 * @param keywords The keywords associated with the resource, maybe <code>null</code>
	 */
	protected void refreshMetadata(Resource resource, String[] keywords)
	throws BuildingServerException
	{
	    BuildingSession newSession = BuildingSessionManagerImpl.getSession( resource );
	    newSession.updateBasicMetadata( );
	    if (keywords != null)
	        newSession.updateMetadataKeywords(keywords, "en");
	}


    /**
     * Copies uploaded files from one resource to another. Used by 
     * {@link #copyconfirm(Request, PrintWriter)}.
     * @param original The resource with uploaded files to be copied.
     * @param new_resource The resource to add the uploaded files to.
     * @param out A Print writer, to display messages to the user interface.
     * @param request The request through which this action was made.
     */
	private void copyUploadedContent( Resource original, Resource new_resource, PrintWriter out, Request request)
	{
	    try
	    {
	        BuildingSession orig_session = BuildingSessionManagerImpl.getSession( original );
	        BuildingSession new_session = BuildingSessionManagerImpl.getSession( new_resource );
	        UploadedFileSummary[] summaries = orig_session.getUploadedFileSession().getFileAndDescendentSummaries( null, true );
	        UploadedFile f;
	        String path_to_source_in_os, name_rel_to_res, mime_type;
	        File source_filestore = original.getWebPublishFolder();
	        File source;

	        //  better to fix the fault in BuildingSessionImpl - done
	        //createRootFolder( new_resource );//shouldn't be necessary, see method for notes:

	        // there a simpler way to work out the paths using
	        // convenient methods in UploadedFile and Resource
	        for ( int i = 0; i < summaries.length; i++ )
	        {
	            f = UploadedFile.findUploadedFile(summaries[i].getUploadedFileId());
	            // don't copy deleted files and folders!
	            if ( f==null || f.isDeleted() )
	                continue;

	            mime_type = f.getMimeType();
	            // this is the actual file
	            source = new File( source_filestore, f.getRealNameInResource() );
	            path_to_source_in_os = source.getAbsolutePath();
	            // Bodington style relative name is same for source and destination
	            name_rel_to_res = f.getNameInResource();


	            if ( f.isFolder() )
	                new_session.getUploadedFileSession().createFolder( name_rel_to_res );
	            else
	                new_session.getUploadedFileSession().copyFile( path_to_source_in_os, name_rel_to_res, mime_type );
	        }

	        // don't forget to copy custom menu file if there is one.....
	        if ( original.getHasCustomMenu() )
	        {
	            new_session.transferResourceMenu( original.getGeneratedFileFolder() + "menu.xml" );
	        }

	    }
	    catch (QuotaExceededException qee)
	    {
	        out.print( "<br/> Error copying uploaded files for: "+ new_resource.getName()+ " quota exceeded.");
	    }
	    catch ( Exception ex ) // BuildingServerException
	    {
	        log.error( ex.getMessage(), ex );
	        String message = "Error copying uploaded files for : "+ new_resource.getName();
	        out.print( "<br />"+ message +"<br />"+ ex.getMessage());
	    }
	}


    /**
     * Copies the access permissions from one resource to another. Used
     * by {@link #copyconfirm(Request, PrintWriter)}.
     * @param original The resource with permissions to be copied.
     * @param new_resource The resource whose premissions are to be modified.
     * @param out A Print writer, to display messages to the user interface.
     */
	private void copyAccessPermissions( Resource original, Resource new_resource, PrintWriter out )
		{
		 try
		 {
		  Acl acl, new_acl;
		  AclEntry entry, new_entry;
		  Group group, new_group, owner_group, new_ownergroup;
          String new_name;
          int offset;
		  User user;
          Enumeration members;

		  new_resource.setUseParentAcl( original.getUseParentAcl() );
		  new_resource.save();

		  acl = original.getAcl();
		  new_acl = new_resource.getAcl();

          // copy local groups if not already done
          Vector copy_groups = new Vector();
          Enumeration groups = Group.findGroups( "resource_id = " + original.getResourceId() );
          while ( groups.hasMoreElements() )
          {
              group = (Group)groups.nextElement();
              if ( ! group.isLocalGroup()) continue;
              
              new_name = "localgroup." + new_resource.getResourceId() + "." + group.getLocalName();
              
              // exists already?
              new_group = Group.findGroup( "resource_id = " + new_resource.getResourceId() + " AND name = " + SqlDatabase.quotedSQL( new_name ) );
              // no, needs to be created.
              if ( new_group == null )
              {
                new_group = new Group();
                new_group.setResourceId( new_resource.getResourceId() );
                new_group.setName( new_name );
                new_group.setDescription( group.getDescription() );
                new_group.save();                  
              }
              
              if ( ! group.isOwnersGroup() )
              {
                copy_groups.addElement( group );
                copy_groups.addElement( new_group );
              }
          }
          
          // copy members of local groups but exluding owners group
          for ( int i=0; i<copy_groups.size(); i+=2 )
          {
              group = (Group)copy_groups.elementAt( i );
              new_group = (Group)copy_groups.elementAt( i+1 );
		      members = group.membersChecked();
		      while ( members.hasMoreElements() )
		      {
		       user = (User)members.nextElement();
		       new_group.addMember( user );
		      }
		      new_group.save();
          }
          
          // now copy acl entries taking care to make localgroup
          // references point to local groups of new resource.
          
		  Enumeration entries = acl.entries();

		  while ( entries.hasMoreElements() )
		  {
		    entry = (AclEntry)entries.nextElement();
            new_entry = (AclEntry)entry.clone();
            // So that we get an insert rather than update
            new_entry.setAclEntryId(null);
            group = entry.getGroup();
            if ( group.isLocalGroup() )
            {
                new_name = "localgroup." + new_resource.getResourceId() + "." + group.getLocalName();
                new_group = Group.findGroup( "resource_id = " + new_resource.getResourceId() + " AND name = " + SqlDatabase.quotedSQL( new_name ) );
                new_entry.setGroup(new_group);
            }
		   new_entry.setAcl( new_acl );
		   new_entry.save();
		   new_acl.addEntry( new_entry );
		    
		  }
		  new_acl.save();
		 }
		 catch ( Exception ex ) //BuildingServerException and java.security.acl.NotOwnerException
		 {
		   String message = "Error copying access permissions for : "+ new_resource.getName();
		   out.print( "<br />"+ message +"<br />"+ ex.getMessage());
		   log.warn( message + ex.getMessage() );
		 }
	}


	private void deleteconfirm( Request breq, PrintWriter out )
		throws IOException
		{
		//String item, dst;
		Resource srcres, itemres;
		int n;
		Connection con=null;
		ResourceTree tree;
		Resource dstres;
		String confirm1, confirm2;
        
        if ( !checkDeleteTemplateParameters( breq, out ) ) 
            return;

		confirm1 = breq.getParameter( "confirm1" );
		confirm2 = breq.getParameter( "confirm2" );
		if ( confirm1==null || !confirm1.equalsIgnoreCase( "yes" ) ||
			(confirm2!=null && confirm2.equalsIgnoreCase( "yes" ) )   )
			{
			out.println( "Resource was not deleted because you didn't confirm without doubt your intention to delete." );
			return;
			}
			
		out.flush();
		
		try
			{
			Enumeration enumeration = Resource.findResources( "http_facility_no = 30", "left_index" );
			if ( !enumeration.hasMoreElements() || (dstres=(Resource)enumeration.nextElement())==null )
				{
				out.println( "Unable to locate the recycling building." );
				return;
				}
			itemres = breq.getResource();
			srcres = itemres.getParent();
			if ( srcres == null )
				{
				out.println( "Unable to delete resource because it is the root resource of the site." );
				return;
				}
			
			
			tree = ResourceTreeManager.getInstance();

			log.debug( "deleteconfirm() A" );
			
			if ( !itemres.checkPermission( Permission.MANAGE ) )
				{
				out.println( "You don't have management permission for the item to be deleted." );
				return;
				}
			if ( !dstres.checkPermission( Permission.CREATE ) )
				{
				out.println( "You don't have permission to put things in the recycle building so you can't delete resources." );
				return;
				}

			// we have to check whether dstloc is 'inside' itemloc
			if ( dstres.isInside( itemres ) )
				{
				out.println( "The resource you want to delete contains the recycle building to you can't delete it." );
				return;
				}


			//change name to avoid clashes
			itemres.setName( itemres.getResourceId().toString() );
			itemres.save();

			//To do...
			
			//remove all groups other than owners from ACL
			
			//remove all "notification" flags so noone gets events anymore?????
			
			
			//check for clashes anyway
			if ( dstres.findChild(itemres.getName()) != null)
				{
					out.println( "<br />The destination location already contains an item with the same name as the item you are trying to move." );
					return;
					}


			
			tree.moveResource( dstres, itemres );
			
			out.println("<b>Resource deleted successfully.</b><br />" );
            out.println("<br />Select the link below to move out to the containing resource " +
                      "(Please DO NOT use the backtrack facility of your browser as the pages you are looking at are no longer valid)<br /><br />" );
            out.print("<A TARGET=_top title=\"Parent resource\" HREF=\"");
			out.print( breq.getContextPath() );
			out.print( breq.getServletPath() );
			out.print( srcres.getFullName() );
			out.print( "\">Back</A> to parent resource");
			return;
			}
		catch ( BuildingServerException bex )
			{
			out.println( "There was an unexpected problem deleting the item.<br /><br />" + bex );
			}

		return;
		}
    
    /**
     * Check the template parameters of the delete request. This is called from
     * the method {@link #deleteconfirm(Request, PrintWriter)}. If this method
     * returns <code>true</code>, the calling method proceeds. If this method
     * returns <code>false</code>, then the calling method will exit. By
     * default, if any template parameters are found then this method will
     * return <code>false</code>, as this probably reflects the fact that
     * these are not template parameters as such, but in fact form the path to a
     * non-existent or already modified resource. However, this method is
     * provided as an override point to subclasses, as this method will clearly
     * fail for facilities that use template parameters as a normal part of
     * their operation.
     * @param request the current request.
     * @param out an object to write any message to.
     * @return <code>true</code> if the template parameters of the delete
     *         request are legitimate, otherwise <code>false</code>.
     * @see #deleteconfirm(Request, PrintWriter)
     * @see Request#getTemplateParameterCount()
     * @see Request#getTemplateParameter(int)
     */
    protected boolean checkDeleteTemplateParameters( Request request, PrintWriter out )
    {
        if ( request.getTemplateParameterCount() > 0 )
        {
            out.println( "No resource was deleted because the web address of the 'delete resource' " +
                         "page you came from is invalid.  This may have happened because you already deleted " +
                         "the resource and you backtracked to an out-of-date page.  Please use the navigation " +
                         "bar to exit from here." );
            return false;
        }
        
        return true;
    }
    
    private void deletewarn(Request breq, PrintWriter out)
        {
        try
        {
            int resources = ResourceTreeManager.getInstance().countDescendents(
                breq.getResource().getPrimaryKey()) + 1;

            if (resources > 10)
                out.print("<b>Warning:</b> ");
            out.print("You are about to delete <em>");
            out.print(resources);
            out.print("</em> resource");
            out.print((resources>1)?"s.":".");
        }
        catch (BuildingServerException e)
        {
            out.print("Failed to discover the number of resources being deleted.");
            log.warn("Failed to find number of resource being deleted.", e);
        }
        }


	private void ifloggedin( Request breq )
		throws IOException
		{
		breq.setSwitchedOff(  !breq.isAuthenticated() );
		return;
		}

	private void ifaccess( Request breq, String name )
		throws IOException
		{
		breq.setSwitchedOff(  !BuildingContext.getContext().checkPermission( name ) );
	}

	public boolean hasPermission( String name )
	throws IOException
	{
	    return BuildingContext.getContext().checkPermission( name );
	}
    
	/**
	 * Checks the permission for all of the named access levels for this user.
	 * Returns true if anyone of the permissions is true.
	 * uhi:awc
	 * @param name1 access name
	 * @param name2 access name
	 * @param name3 access name
	 * @return true if the permission for any one of the named access levels for this user is true.
     * @throws IOException
	 */
	public boolean hasPermission( String name1, String name2, String name3 )
	throws IOException
	{
	    return ( BuildingContext.getContext().checkPermission( name1 ) ||
	                    BuildingContext.getContext().checkPermission( name2 ) ||
	                    BuildingContext.getContext().checkPermission( name3 ) );
	}

	private void ifcanreorder( Request breq )
		throws IOException
		{
		BuildingContext context;
		Resource resource;
		context = BuildingContext.getContext();
		int i;
		
		if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
			{
			breq.setSwitchedOff( true );
			return;
			}
		
		try
			{
			resource = context.getResource();
			Enumeration enumeration=resource.findChildren();
			for ( i=0; enumeration.hasMoreElements(); i++ )
				{
				enumeration.nextElement();
				}
			
			breq.setSwitchedOff(  (i<2) );
			}
		catch ( BuildingServerException bsex )
			{
			breq.setSwitchedOff( true );
			}

		return;
		}
    
    /**
     * Indicates whether or not there other child resources for the current user
     * to sort. This method first of all checks that the current user has
     * <code>MANAGE</code> access. If so, the children of the resource
     * associated with the request are examined. If there are <code>2</code>
     * or more children, then these can be sorted and this method returns
     * <code>true</code>.
     * <p>
     * <em>NOTE: This method is intended to be called from templates.</em>
     * </p>
     * @param request the request object.
     * @return <code>true</code> if there are associated objects to sort,
     *         otherwise <code>false</code>.
     */
    public boolean isCanReorder( Request request )
    {
        try
        {
            ifcanreorder( request );
            return !request.isSwitchedOff();
        }
        catch ( IOException e )
        {
        }
        
        return false;
    }

	/**
     * Write the property of a resource associated with the current request.
     * Typically this resource is the associated instance of
     * {@link org.bodington.server.resources.Resource}. If so, the
     * <code>name</code> parameter can take the values <code>title</code>,
     * <code>introduction</code> and <code>description</code>. Another
     * useful value is <code>nameofuser</code> which prints out the full name
     * of the current user ({@link org.bodington.server.realm.User#getName()}).
     * This method is typically called from within
     * {@link #insert(Request, PrintWriter, String, String)}.
     * @param breq the request object.
     * @param out the writer object.
     * @param name the name of the variable.
     * @throws IOException
     * @see #insert(Request, PrintWriter, String, String)
     */
	public void variable( Request breq, PrintWriter out, String name )
		throws IOException
	    {
		variable( breq, out, name, false );
	    }   
	
	/**
     * Write the property of a resource associated with the current request.
     * Typically this resource is the associated instance of
     * {@link org.bodington.server.resources.Resource}. If so, the
     * <code>name</code> parameter can take the values <code>title</code>,
     * <code>introduction</code> and <code>description</code>. Another
     * useful value is <code>nameofuser</code> which prints out the full name
     * of the current user ({@link org.bodington.server.realm.User#getName()}).
     * The <code>form</code> indicates whether or not the characters
     * <code>&lt;</code>, <code>&gt;</code> &amp; <code>&amp;</code>
     * should be escaped. It does this by using a special kind of writer object ({@link org.bodington.servlet.EscapedHtmlWriter}).
     * This method is typically called from within
     * {@link #insert(Request, PrintWriter, String, String)}.
     * @param breq the request object.
     * @param out the writer object.
     * @param name the name of the variable.
     * @param form if <code>true</code> the characters <code>&lt;</code>,
     *        <code>&gt;</code> &amp; <code>&amp;</code> should be escaped.
     * @throws IOException
     * @see org.bodington.servlet.EscapedHtmlWriter
     * @see #insert(Request, PrintWriter, String, String)
     */
	private void variable( Request breq, PrintWriter out, String name, boolean form )
		throws IOException
		{
		if ( out == null )
		    return;
		
		Resource resource;
		try
			{
			resource = BuildingContext.getContext().getResource();
		
		    EscapedHtmlWriter t_out = new EscapedHtmlWriter( out );
		
			if ( name.equalsIgnoreCase( "url" ) )
				{
				if ( resource.getName()!=null )
					out.print( resource.getName() );
				return;
				}
			if ( name.equalsIgnoreCase( "ordinal" ) )
				{
				//if ( breq.location.ordinal!=null )
				//	out.print( breq.location.ordinal.toString() );
				return;
				}
			if ( name.equalsIgnoreCase( "title" ) )
				{
				if ( resource.getTitle()!=null )
				    {
                        AlreadyEscapedHtmlWriter checkOut = new AlreadyEscapedHtmlWriter(out);
                        checkOut.print( resource.getTitle() );
					}
				return;
				}
			if ( name.equalsIgnoreCase( "description" ) )
				{
				if ( resource.getDescription()!=null )
				    {
				    if ( form )
    					t_out.print( resource.getDescription() );
    			    else
    					out.print( resource.getDescription() );
					}
				return;
				}
			if ( name.equalsIgnoreCase( "introduction" ) )
				{
				if ( resource.getIntroduction()!=null )
				    {
				    if ( form )
    					t_out.print( resource.getIntroduction() );
    	            else
    					out.print( resource.getIntroduction() );
    	            }
				return;
				}
			if ( name.equalsIgnoreCase( "keywords" ) )
			{
			    BuildingSession session = BuildingSessionManagerImpl.getSession( resource );
			    String[] fields = session.getFieldValuesFromMetadata( "keywords" );
                if ( form )
                {
                    t_out.print(XMLMetadataUtils.getKeywordStringFromArray( fields ));
                }
                else
                {
                    out.print(XMLMetadataUtils.getKeywordStringFromArray( fields ));
                }
			    return;
			}
			if ( name.equalsIgnoreCase( "nameofuser" ) && breq.isAuthenticated() )
				{
				User user = (User)BuildingContext.getContext().getUser();
				if ( user!=null && user.getName()!=null )
				    {
				    out.print( user.getName() );
				    }
				return;
				}
			if ( name.equalsIgnoreCase( "resource_id" ) )
				{
				if ( breq.getResource().getResourceId()==null )
			    	out.print( "No resource id for this location." );
				else
					out.print( "Resource id = " + breq.getResource().getResourceId() );
				return;
				}
			if ( name.substring( 0, 4 ).equalsIgnoreCase( "type" ) )
				{
				
    			if ( name.equalsIgnoreCase( "typeca" ) )
    			    {
    			    out.print( getTitle( true, true ) );
    			    return;
    			    }
    			if ( name.equalsIgnoreCase( "typea" ) )
    			    {
    			    out.print( getTitle( false, true ) );
    			    return;
    			    }
    			if ( name.equalsIgnoreCase( "typec" ) )
    			    {
    			    out.print( getTitle( true, false ) );
    			    return;
    			    }

   			    out.print( getTitle( false, false ) );
				return;
				}
			if ( name.equalsIgnoreCase( "zone_name" ) )
				{
				Zone zone = breq.getResource().getEffectiveZone();
				if ( zone == null )
			    	out.print( "(<I>undefined</I>)" );
				else
					out.print( zone.getName() );
				return;
				}
			}
		catch ( BuildingServerException bsex )
			{
			return;
			}
		out.print( "<!-- unknown insert -->" );
		}

	
	private void reordercontrol( Request breq, PrintWriter out )
		throws IOException
		{
		BuildingContext context;
		Resource resource, current;
		context = BuildingContext.getContext();
		
		try
			{
			resource = context.getResource();
		    Enumeration enumeration=resource.findChildren();
			for ( int i=0; enumeration.hasMoreElements(); i++ )
				{
				current=(Resource)enumeration.nextElement();

				if ( !current.checkPermission( Permission.SEE ) )
					continue;

				out.print( "<OPTION VALUE=\"" );
				out.print( current.getResourceId().toString() );
				out.print( "\"" );
				if ( i==0 )
					out.print( " SELECTED" );
				out.print( ">" );
				out.print( current.getTitle() );
				out.println( "</OPTION>" );
				}
			}
		catch ( BuildingServerException bsex )
			{
			return;
			}
		}
    	
	private void reorder( Request breq, PrintWriter out )
		throws IOException
		{
		BuildingContext context;
		Resource resource, current;
		context = BuildingContext.getContext();
		
		Vector key_list = new Vector();
		String strrid;
		int r;
		PrimaryKey key;
		String list=breq.getParameter( "thelist" );
		StringTokenizer tok = new StringTokenizer( list, "," );
		while ( tok.hasMoreTokens() )
			{
			strrid = tok.nextToken();
			try
				{
				r = Integer.parseInt( strrid );
				}
			catch ( Exception ex )
				{
				continue;
				}
			key = new PrimaryKey( r );
			key_list.addElement( key );
			}
			
		try
			{
			resource = context.getResource();
			resource.sortChildren( key_list );
			ResourceTree tree = ResourceTreeManager.getInstance();
			//tree.updateIndices();
			//tree.saveAll();

			out.println( "<H4>Confirmation</H4>" );
			out.println( "<P>Resources have been put in the following order:</P>" );
			out.println( "<UL>" );
			for ( int i=0; i<key_list.size(); i++ )
				{
				current=Resource.findResource( (PrimaryKey)key_list.elementAt( i ) );

				if ( current==null )
					continue;

				out.print( "<LI>" );
				out.print( current.getTitle() );
				out.println( "</LI>" );
				}
			out.println( "</UL>" );
			}
		catch ( BuildingServerException bsex )
			{
			out.println( "<P>There was a technical problem attempting to reorder resources</P>" );
			out.println( bsex.toString() );
			return;
			}
		}
    
	protected void resourceMenuItem( Object gen_item, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight )
        throws IOException, BuildingServerException {

      if (gen_item instanceof MenuItem) {
        MenuItem item = (MenuItem) gen_item;
        resourceMenuItem(item, breq, out, state, depth, highlight);
      }
      else if (gen_item instanceof Resource) {
        Resource resource = (Resource) gen_item;
        Facility facility = state.fl.get(new Integer(resource.getHttpFacilityNo()));
        facility.resourceMenuItem(resource, breq, out, state, depth, highlight);
      }
    }

	protected void resourceMenuItem(MenuItem menu_item, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight)
	
	throws IOException, BuildingServerException
	{
	    String href=null, icon_src=null, title=null, target=null;
	    
	    // determine href
	    
	    href = menu_item.getHRef();
	    target = state.target;
	    
	    if ( menu_item.getType().equals( "container" ) )
	        icon_src = state.folder_url;
	    else
	        icon_src = state.file_url;
	    
	    title = menu_item.getTitle();
	    
	    
	    // heading level for title line matches depth but there are only
	    // six heading levels in HTML so stop there.
	    int hlevel = (depth>6)?6:depth;
	    int level = ((depth-1)%10)+1;
	    
	    // title in heading with appropriate class attribute
	    out.print( "          <h" );
	    out.print( hlevel );
	    
	    out.print( " class=\"" + state.css_class_prefix + "_node_title_lev" );
	    out.print( level );
	    out.print( "\">" );
	    
	    // optional link around whole title (inc. icon)
	    if ( href != null )
	    {
	        out.print( "<a" );
	        if ( target!=null )
	        {
	            out.print( " target=\"" );
	            out.print( target );
	            out.print( "\"" );
	        }
	        out.print( " href=\"" );
	        out.print( href );
	        out.print( "\">" );
	    }
	    
	    /*
	     * WebLearn modification <altAndTitle> [26/01/05] Alexis 
	     * O'Connor. Renders more accurate / accessible value for 
	     * alt attribute.
	     */	 
	    out.print( "<img alt=\"Icon for a ");
	    out.print( menu_item.getType().equals("container") ? "Folder" : "File" );
	    out.print( ".\" src=\"");
	    // <<<--- WebLearn modification <altAndTitle>.
	    out.print( icon_src );
	    out.print( "\" " );
	    
	    if ( depth==1 )
	        out.print( "class=\"" + state.css_class_prefix + "_node_icon_without_stalk\" />" );
	    else
	        out.print( "class=\"" + state.css_class_prefix + "_node_icon_no_expander\" />" );
	    
	    // span is used to get everything on one line in browsers that
	    // lack CSS but CSS redfines as block.
	    out.print( "<span class=\"" + state.css_class_prefix + "_node_content" );
	    if ( depth==1 )
	        out.print( "_without_stalk" );
	    out.print( "_lev" );
	    out.print( level );
	    out.print( "_hl" );
	    out.print( highlight );
	    out.print( "\">" );
	    
	    if ( title != null )
	        out.print( title );
	    else
	        if ( href != null )
	            out.print( href );
	        else
	            out.print( "{untitled item}" );

        if ( menu_item.getSize() != 0 )
        {
            out.print( " <span style='font-size: 75%'>(");
            out.print( Utils.toHumanReadableSize(menu_item.getSize()) );
            out.print( ")</span>");
        }    
	    
	    out.print( "</span>" );

        if( href != null )
            out.print( "</a>" );
	    out.print( "</h" );  // end of node_title
	    out.print( hlevel );
	    out.println( ">" );
	    
	    // files have no further content in the node.
	    // but resources have descriptions or introductions
	    
	    // amendment - if big icons files have an empty content section
	    // to take up space next to icon and prevent next (floating)
	    // icon coming up next to current one.
	    
	    if ( state.big_icons )
	    {
	        String content = "&nbsp;";
	        
	            out.print( "<div class=\"" + state.css_class_prefix + "_node_content" );
	            if ( depth==1 )
	                out.print( "_without_stalk" );
	            out.print( "_lev" );
	            out.print( level );
	            out.print( "_hl" );
	            out.print( highlight );
	            out.print( "\">" );
	            
	            out.print( content );
	            
	            out.print( "</div>" );
	        }
	    out.print( "<div class=\"clearer\">&nbsp;</div>\n");
	}
	   
	protected void resourceMenuItem(Resource resource, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight)
	
	throws IOException, BuildingServerException
	{
	    Facility facility;
	    Template template;
	    String href=null, icon_src=null, title=null, target=null;
	    boolean italic=false;  // should really use a redefinable style
	    boolean deny = false;
	    
	    // determine href
	    href = ( !state.rootless && depth==1  )?null:
	        ( breq.getContextPath() +breq.getServletPath() + resource.getFullName() );
	    
	    target = "_top";
	    
	    // using getTemplateGifUrl doesn't work for descendant resources - need to
	    // reference proper resource.
	    facility = state.fl.get( new Integer( resource.getHttpFacilityNo() ) );
	    if ( facility!=null )
	    {
	        template = Template.get( facility.facilityname, resource.getImplicitHttpUIStyle(), resource.getResourceId(), state.big_icons?"icon.gif":"iconsmall.gif" );
	        if ( template != null )
	            icon_src = breq.getContextPath() + state.gif_url + template.getUrl() + state.colour_code;
	    }
	    
	    title = resource.getTitle();
	    
	    if ( !resource.checkPermission( Permission.VIEW ) )
	    {
	        if ( state.anon )
	            // anonymous users are allowed to see link because
	            // they get a message about logging in if they enter
	            italic=true;
	        else
	        {
	            // logged in but no access - don't give link
	            href=null;
	            deny = true;
	        }
	    }
	    
	    // heading level for title line matches depth but there are only
	    // six heading levels in HTML so stop there.
	    int hlevel = (depth>6)?6:depth;
	    int level = ((depth-1)%10)+1;
	    
	    // title in heading with appropriate class attribute
	    out.print( "          <h" );
	    out.print( hlevel );
	    
	    out.print( " class=\"" + state.css_class_prefix + "_node_title_lev" );
	    out.print( level );
	    out.print( "\">" );
	    
	    // optional link around whole title (inc. icon)
	    if ( href != null )
	    {
	        out.print( "<a" );
	        if ( target!=null )
	        {
	            out.print( " target=\"" );
	            out.print( target );
	            out.print( "\"" );
	        }
	        out.print( " href=\"" );
	        out.print( href );
	        out.print( "\">" );
	    }
	    
	    /*
	     * WebLearn modification <altAndTitle> [26/01/05] 
	     * Alexis O'Connor. Renders more accurate / 
	     * accessible value for alt attribute.
	     */
        String resourceName = ResourceUtils.getFacility(resource).getName();
	    out.print( "<img alt=\"Icon for "
	        + (TextUtils.beginsWithVowel(resourceName) ? "an" : "a")
	        + " " + resourceName + ".\" src=\"" );
	    // <<<--- WebLearn modification <altAndTitle>.
	    out.print( icon_src );
	    out.print( "\" " );
	    
	    if ( depth==1 )
	        out.print( "class=\"" + state.css_class_prefix + "_node_icon_without_stalk\" />" );
	    else
	        out.print( "class=\"" + state.css_class_prefix + "_node_icon_no_expander\" />" );
	    
	    // span is used to get everything on one line in browsers that
	    // lack CSS but CSS redfines as block.
	    out.print( "<span class=\"" + state.css_class_prefix + "_node_content" );
	    if ( depth==1 )
	        out.print( "_without_stalk" );
	    out.print( "_lev" );
	    out.print( level );
	    out.print( "_hl" );
	    out.print( highlight );
	    out.print( "\">" );
	    
	    if ( italic )
	        out.print( "<i>" );
	    
	    if ( title != null )
	        out.print( title );
	    else
	        if ( href != null )
	            out.print( href );
	        else
	            out.print( "{untitled item}" );
	    
	    if ( italic )
	        out.print( "</i>" );
	    
	    out.print( "</span>" );
	    
	    if ( href != null )
	        out.print( "</a>" );
	    
	    out.print( "</h" );  // end of node_title
	    out.print( hlevel );
	    out.println( ">" );
	    
	    String content;
	    
	    if ( !state.rootless && depth==1  )
	        content = resource.getIntroduction();
	    else
	        content = resource.getDescription();
	    
	    if ( deny )
	        content = "<em>You are not included on the access list for this item.</em><br />" + content;
	    
        StringBuffer toOutput = new StringBuffer();
        if (content != null && content.length()>0)
            toOutput.append(content);
        String markup = timedResourceText(resource);
        if (markup != null && markup.length()>0)
        {
            if(toOutput.length() > 0)
                toOutput.append("<br />");
            toOutput.append(markup);
        }
        if (toOutput.length() == 0)
        {
            // This is a hack for IE7 so that the text always displays.
            // The problem is that it means the display doesn't shrink when
            // small icons are selected and there is no description.
            toOutput.append("&nbsp;");
        }
        out.print( "<div class=\"" + state.css_class_prefix + "_node_content" );
        if ( depth==1 )
            out.print( "_without_stalk" );
        out.print( "_lev" );
        out.print( level );
        out.print( "_hl" );
        out.print( highlight );
        out.print( "\">" );
        out.print(toOutput);
        out.print( "</div>" );

        out.print( "<div class=\"clearer\">&nbsp;</div>\n");
	}

    /**
     * Create a string that describes any time restraint on the resource. If
     * there is no time constraint on this resource, this method returns
     * <code>null</code>. Otherwise, this method returns a string that
     * describes when this resource becomes available and when it is no longer
     * available (as appropriate).
     * @param resource the resource to be annotated.
     * @return a string that describes any time restraint on the resource.
     */
    protected String timedResourceText( Resource resource ) throws IOException
    {
        java.util.Date openDate = resource.getOpenDate();
        java.util.Date closeDate = resource.getCloseDate();

        if ( openDate == null && closeDate == null ) 
            return null;

        String markup = null;
        markup = "This resource is available";
        markup += (openDate != null) 
            ? " from " + DateFormatter.formatDate( openDate, DateFormatter.MEDIUM ) : "";
        markup += (closeDate != null) 
            ? " until " + DateFormatter.formatDate( closeDate, DateFormatter.MEDIUM ) : "";

        return markup;
    }
 
    
    protected void resourceMenuItemChildren( Object gen_item, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int parent_highlight )
    throws IOException, BuildingServerException {
        
        if (gen_item instanceof MenuItem) {
            MenuItem item = (MenuItem) gen_item;
            resourceMenuItemChildren(item, breq, out, state, depth, parent_highlight);
        }
        
        else if (gen_item instanceof Resource) {
            Resource resource = (Resource) gen_item;
            Facility facility = state.fl.get(new Integer(resource.getHttpFacilityNo()));
            facility.resourceMenuItemChildren(resource, breq, out, state, depth,
                parent_highlight);
        }
    }


    protected void resourceMenuItemChildren( Resource resource, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int parent_highlight )
    throws IOException, BuildingServerException
    {
        int i, highlight, level = ((depth-1)%10)+1;
        Menu menu;
        MenuItem item=null;
        Resource resource_child=null;
        Object gen_child=null;
        MenuItem child; //, sibling;
        Vector children=null, grandchildren=null;
        boolean child_has_visible_child_resources = false;
        boolean is_heading = false;
        
        
        
        // start with interactive resource children
        if ( state.full && resource != null && (level<=state.recursion_depth) )
            children = getVisibleChildResourcesForMenu( resource );
        else
            children = new Vector();
        
        // menuitems for root only
        if ( state.rootless?depth==1:depth==2 )
        {
            menu = state.session.getResourceMenu();
            item = (MenuItem)menu.getRoot();
        }
        for ( i=0; item!=null && i<item.getChildCount(); i++ )
        {
            children.addElement( item.getChildAt( i ) );
        }
        
        // return without outputting any tags if there are no elements
        // to display.
        if ( children.size()<1 )
            return;
        
        StringBuffer indent = new StringBuffer( depth );
        for ( i=2; i<depth; i++ )
            indent.append( "      " );
        
        // children go in a siblings set or whole tree
        if ( state.rootless && level == 1 )
        {
            out.println( "<div class=\"" + state.css_class_prefix + "\">" );
            if ( state.detached_root )
            {
                out.println( "  <div class=\"" + state.css_class_prefix + "_branch_border\">" );
                out.println( "  <div class=\"" + state.css_class_prefix + "_header\">" );
                out.println( "Where you can go from here" );
                out.println( "  </div>" );
                out.println( "  </div>" );
            }
        }
        else
        {
            out.print( indent );
            out.print( "    <div class=\"" + state.css_class_prefix + "_siblings" );
            if ( depth==2 )
                out.print( "_without_stalk" );
            out.println( "_lev" + level + "\">" );
        }
        
        
        for ( i=0; i<children.size(); i++ )
        {
            child_has_visible_child_resources = false;
            is_heading = false;
            child=null;
            resource_child=null;
            gen_child = children.elementAt( i );
            if ( gen_child instanceof MenuItem )
                child = (MenuItem)gen_child;
            if ( gen_child instanceof Resource )
            {
                resource_child = (Resource)gen_child;
                is_heading = resource_child.getResourceType() == Resource.RESOURCE_HEADING;
                if ( (depth+1)>state.recursion_depth )
                    child_has_visible_child_resources=false;
                else
                {
                    grandchildren = getVisibleChildResourcesForMenu( resource_child );
                    child_has_visible_child_resources = grandchildren.size()>0;
                }
            }
            
            //highlight = (((state.root_highlight-1) ^ (depth-1) ^ i) & 1) + 1;
            highlight = (((parent_highlight-1) ^ (i+1)) & 1) + 1;
            
            // put every child in a branch
            // (every branch in branch border)
            out.print( indent );
            out.print( "      <div class=\"" + state.css_class_prefix + "" );
            if ( (i+1)==children.size() ) //|| depth==1 )
                out.print( "_end" );
            out.println( "_branch_border\">" );
            
            out.print( indent );
            out.print( "      <div class=\"" + state.css_class_prefix + "" );
            if ( (i+1)==children.size() ) //|| depth==1 )
                out.print( "_end" );
            out.print( "_branch_lev" );
            out.print( level );
            out.print( "_hl" );
            out.print( highlight );
            out.println( "\">" );
            
            // put every child other than headings in a node too
            if ( !is_heading )
            {
                out.print( indent );
                out.print( "        <div class=\"" + state.css_class_prefix + "_node_s" );
                out.print( ( state.rootless && level == 1 )?"n":"y" );
                out.print( "u" );
                out.print( ( state.rootless && level == 1 )?"n":"y" );
                out.print( "d" );
                if ( state.rootless && level == 1 )
                    out.print( "n" );
                else
                    out.print( ((i+1)<children.size())?"y":"n" );
                out.print( "b" );
                if ( resource_child!=null )
                    out.print( child_has_visible_child_resources?"y":"n" );
                else
                    out.print( (child.getChildCount()>0)?"y":"n" );
                out.print( "_lev" );
                out.print( level );
                out.print( "_hl" );
                out.print( highlight );
                out.println( "\">" );
            }
            
            out.print( indent );
            resourceMenuItem( gen_child, breq, out, state, depth, highlight );
            
            if ( !is_heading )
                out.println( "        </div>" ); // end of node		
            if ( resource_child!=null && (depth+1)<=state.recursion_depth )
                resourceMenuItemChildren( resource_child, breq, out, state, depth+1, highlight );
            
            if ( child!=null && child.getChildCount()>0 )
                resourceMenuItemChildren( child, breq, out, state, depth+1, highlight );
            
            out.println( "      </div>" ); // end of branch
            out.println( "      </div>" ); // end of branch border
        }
        
        if ( !state.rootless || level != 1 )
            out.print( "    <div class=\"" + state.css_class_prefix + "_siblings_padding\"></div>" );
        out.println( "    </div>" );  // end siblings or tree
    }
    
    private void resourceMenuItemChildren( MenuItem menu_item, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int parent_highlight )
    throws IOException, BuildingServerException
    {
        int i, highlight, level = ((depth-1)%10)+1;
        Resource resource_child = null;
        Object gen_child=null;
        MenuItem child; //, sibling;
        Vector children=null, grandchildren=null;
        boolean child_has_visible_child_resources = false;
        
        
        
        children = new Vector();
        
        
        for ( i=0; i<menu_item.getChildCount(); i++ )
        {
            children.addElement( menu_item.getChildAt( i ) );
        }
        
        // return without outputting any tags if there are no elements
        // to display.
        if ( children.size()<1 )
            return;
        
        StringBuffer indent = new StringBuffer( depth );
        for ( i=2; i<depth; i++ )
            indent.append( "      " );
        
        // children go in a siblings set or whole tree
        if ( state.rootless && level == 1 )
        {
            out.println( "<div class=\"" + state.css_class_prefix + "\">" );
            if ( state.detached_root )
            {
                out.println( "  <div class=\"" + state.css_class_prefix + "_branch_border\">" );
                out.println( "  <div class=\"" + state.css_class_prefix + "_header\">" );
                out.println( "Where you can go from here" );
                out.println( "  </div>" );
                out.println( "  </div>" );
            }
        }
        else
        {
            out.print( indent );
            out.print( "    <div class=\"" + state.css_class_prefix + "_siblings" );
            if ( depth==2 )
                out.print( "_without_stalk" );
            out.println( "_lev" + level + "\">" );
        }
        
        
        for ( i=0; i<children.size(); i++ )
        {
            child_has_visible_child_resources = false;
            child = null;
            resource_child = null;
            gen_child = children.elementAt(i);
            if (gen_child instanceof MenuItem)
                child = (MenuItem) gen_child;
            if ( gen_child instanceof Resource )
            {
                resource_child = (Resource) gen_child;
                if ( (depth + 1) > state.recursion_depth)
                    child_has_visible_child_resources = false;
                else
                {
                    grandchildren = getVisibleChildResourcesForMenu(resource_child);
                    child_has_visible_child_resources = grandchildren.size() > 0;
                }
            }
            
            //highlight = (((state.root_highlight-1) ^ (depth-1) ^ i) & 1) + 1;
            highlight = ( ( (parent_highlight - 1) ^ (i + 1)) & 1) + 1;
            
            // put every child in a branch
            // (every branch in branch border)
            out.print(indent);
            out.print("      <div class=\"" + state.css_class_prefix + "");
            if ( (i + 1) == children.size()) //|| depth==1 )
                out.print("_end");
            out.println("_branch_border\">");
            
            out.print(indent);
            out.print("      <div class=\"" + state.css_class_prefix + "");
            if ( (i + 1) == children.size()) //|| depth==1 )
                out.print("_end");
            out.print("_branch_lev");
            out.print(level);
            out.print("_hl");
            out.print(highlight);
            out.println("\">");
            
            // put every child other than headings in a node too
            out.print(indent);
            out.print("        <div class=\"" + state.css_class_prefix + "_node_s");
            out.print( (state.rootless && level == 1) ? "n" : "y");
            out.print("u");
            out.print( (state.rootless && level == 1) ? "n" : "y");
            out.print("d");
            if (state.rootless && level == 1)
                out.print("n");
            else
                out.print( ( (i + 1) < children.size()) ? "y" : "n");
            out.print("b");
            if (resource_child != null)
                out.print(child_has_visible_child_resources ? "y" : "n");
            else
                out.print( (child.getChildCount() > 0) ? "y" : "n");
            out.print("_lev");
            out.print(level);
            out.print("_hl");
            out.print(highlight);
            out.println("\">");
            
            out.print(indent);
            resourceMenuItem(gen_child, breq, out, state, depth, highlight);
            
            out.println("        </div>"); // end of node
            
            if (resource_child != null && (depth + 1) <= state.recursion_depth)
                resourceMenuItemChildren( resource_child, breq, out, state, depth+1, highlight );
            
            if (child != null && child.getChildCount() > 0)
                resourceMenuItemChildren(child, breq, out, state, depth + 1, highlight);
            
            out.println("      </div>"); // end of branch
            out.println("      </div>"); // end of branch border
        }
        
        if ( !state.rootless || level != 1 )
            out.print( "    <div class=\"" + state.css_class_prefix + "_siblings_padding\"></div>" );
        out.println( "    </div>" );  // end siblings or tree
    }

    // inner class used to pass some state information through recursive methods
    class ResourceMenuOutputState
    {
        int root_highlight;
        boolean rootless;
        boolean detached_root;
        boolean full;
        boolean anon;
        boolean big_icons;
        String css_class_prefix;
        int recursion_depth;
        String target;
        String gif_url;
        String file_url;
        String folder_url;
        String colour_code;
        BuildingSession session;
        FacilityList fl;
    }

    private Vector getVisibleChildResourcesForMenu( Resource resource )
        throws BuildingServerException
    {
        Vector children = new Vector();
        // can't see children if can't enter parent
        if ( !resource.checkPermission( Permission.VIEW ) )
            return children;
        
        Enumeration enumeration = resource.findChildren();
        while ( enumeration.hasMoreElements() )
        {
            resource = (Resource)enumeration.nextElement();
            if ( resource.checkPermission( Permission.SEE ) )
                children.addElement( resource );
        }
        return children;
    }
    
    public void fullResourceMenu(  Request breq, PrintWriter out )
		throws IOException
		{
            fullResourceMenu( breq, out, null );
    }
    // outputs integrated menu of interactive resources and uploaded files
    // including introductory text of requested resource
    public void fullResourceMenu(  Request breq, PrintWriter out, String target )
        throws IOException
    {
        try
        {
            resourceMenu( breq, out, true, target );
        }
        catch ( BuildingServerException e )
        {
            log.error( e.getMessage(), e );
            throw new IOException( e.getMessage() );
        }
    }

    // For compatibility with old templates this outputs just uploaded files.
    public void resourceMenu( Request breq, PrintWriter out )
        throws IOException
    {
        try
        {
            resourceMenu( breq, out, false,  null );
        }
        catch ( BuildingServerException e )
        {
            log.error( e.getMessage(), e );
            throw new IOException( e.getMessage() );
        }
    }
    

    // outputs menu of uploaded files and optionally interactive resources
    // so far output of interactive resources isn't implemented so full is
    // ignored.
    public void resourceMenu( Request breq, PrintWriter out, boolean full, String target )
        throws IOException, BuildingServerException
    {
        int i, max_depth=1;
		Menu menu;
		MenuItem item, child;
        Vector visible_children;
		
        try
        {
            ResourceMenuOutputState state = new ResourceMenuOutputState();
            state.full = full;
            state.root_highlight = 1;
            state.target = target;
            state.colour_code = styleSheet.getTemplateGifColourMapperCode( breq, "bodington_navigation_page" );
            state.gif_url = "/processedgif/templates";
            state.session = breq.getBuildingSession();
            String big_icons = state.session.getProperty( "ui_navigation_menu_big_icons", "no" );
            String prefered_big_icons = getAutomaticPreferenceByScreenSize( 
                                        breq, "preference.navigation.menu.big_icons", "no", big_icons );
            state.big_icons =  "yes".equalsIgnoreCase( prefered_big_icons );
            state.file_url = this.getTemplateGifUrl( breq, "bodington_navigation_page", state.big_icons?"bs_template_file.gif":"bs_template_file-small.gif", false );
            state.folder_url = this.getTemplateGifUrl( breq, "bodington_navigation_page", state.big_icons?"bs_template_folder.gif":"bs_template_folder-small.gif", false );
            state.css_class_prefix = state.big_icons?"res_tree_big":"res_tree";
            state.fl = FacilityList.getFacilities();
            state.anon = breq.getServerNavigationSession().isAnonymous();

            String type = state.session.getProperty( "ui_navigation_menu_type", "plain" );
            String prefered_type = getPreference( breq, "preference.navigation.menu.type" );
            if ( "plain".equalsIgnoreCase( prefered_type ) || 
                 "attached".equalsIgnoreCase( prefered_type ) || 
                 "detached".equalsIgnoreCase( prefered_type )    )
                type = prefered_type;

            
            //boolean tree = "tree".equalsIgnoreCase( getPreference( breq, "preference.navigation.menu.tree" ) );
            boolean tree = "attached".equals( type );
            state.detached_root = "detached".equals( type );
            
            // resource property switches on recursion
            max_depth=1;
		try
			{
                String prop = state.session.getProperty( "ui_navigation_menu_ancestors", "1" );
                max_depth = Integer.parseInt( prop );
                if ( max_depth<1 ) max_depth = 1;
                if ( max_depth>5 ) max_depth = 5;
            }
            catch ( NumberFormatException nfe )
            {
                max_depth=1;
            }
            // but user can switch off
            if ( "suppress".equalsIgnoreCase( getPreference( breq, "preference.navigation.menu.ancestors" ) ) )
                max_depth=1;
			
            // suppress tree if old style files only menu
            if ( !full ) {tree = false; max_depth=1;}
			
            // actually this is just the uploaded files in the form of an
            // IMS style contents page or full file listing
            // it doesn't include interactive resources - should be integrated
            // some time.
            menu = state.session.getResourceMenu();
			item = (MenuItem)menu.getRoot();
            visible_children = getVisibleChildResourcesForMenu( breq.getResource() );

            // in tree mode the node representing the resource itself is shown.
            if ( tree )
            {
                state.recursion_depth = max_depth+1;  // add 1 to include root
                state.rootless = false;
                out.println( "<div class=\"" + state.css_class_prefix + "\">" );
                out.println( "  <div class=\"" + state.css_class_prefix + "_end_branch_border\">" );
                out.print( "  <div class=\"" + state.css_class_prefix + "_header\">" );
                out.print( "Where you are" );
                if ( item.getChildCount() > 0 || visible_children.size() > 0 )
                    out.print( " and where you can go from here" );
                out.println( "  </div>" );
                out.println( "  </div>" );
                out.println( "  <div class=\"" + state.css_class_prefix + "_end_branch_border\">" );
                out.println( "  <div class=\"" + state.css_class_prefix + "_end_branch_lev1_hl1\">" );
                out.println( "    <div class=\"" + state.css_class_prefix + "_node_snundnb" + 
                        ((item.getChildCount()>0 || !visible_children.isEmpty())?"y":"n") +
                        "_lev1_hl1\">" );
                resourceMenuItem( breq.getResource(), breq, out, state, 1, 1 );
                out.println( "    </div>" );
                // method will only output div elements if there is at least
                // one node to display
                resourceMenuItemChildren( breq.getResource(), breq, out, state, 2, 1 );
                out.println( "  </div>" );
                out.println( "  </div>" );
                out.println( "</div>" );
			}
            else
            // otherwise each top level item is output in its own tree.
            {
                state.recursion_depth = max_depth;
                if ( full )
                {
                    if ( state.detached_root )
                    {
                        out.println( "<div class=\"" + state.css_class_prefix + "\">" );
                        out.println( "  <div class=\"" + state.css_class_prefix + "_branch_border\">" );
                        out.println( "  <div class=\"" + state.css_class_prefix + "_header\">" );
                        out.println( "Where you are" );
                        out.println( "  </div>" );
                        out.println( "  </div>" );
                        out.println( "  <div class=\"" + state.css_class_prefix + "_end_branch_border\">" );
                        out.println( "  <div class=\"" + state.css_class_prefix + "_end_branch_lev1_hl1\">" );
                        out.println( "    <div class=\"" + state.css_class_prefix + "_node_snundnbn_lev1_hl1\">" );
                        resourceMenuItem( breq.getResource(), breq, out, state, 1, 1 );
                        out.println( "    </div>" );
                        // method will only output div elements if there is at least
                        // one node to display
                        out.println( "  </div>" );
                        out.println( "  </div>" );
                        out.println( "</div>" );
                        out.println( "<div style=\"height: 1em\"></div>" );
                    }
                    else
                    {
                        out.print( "<div class=\"bs-introduction\">" );
                        out.print( breq.getResource().getIntroduction() );
                        out.println( "</div>" );
                    }
                }
                state.rootless = true;
                // method will only output div elements if there is at least
                // one node to display
                resourceMenuItemChildren( breq.getResource(), breq, out, state, 1, 2 );
            }

        }
		catch ( BuildingServerException bsex )
			{
			logException( out, "Facility", "resourceMenu", 
			    "A technical problem occurred.",
			    bsex );
			return;
			}

		}

         /**
         * Writes html anchor tag with href attribute that has username appended to query part of URL.
         * uhi:awc
         * @param breq The Building HTTP request.
         * @param out The PrintWriter.
         * @param url The url used by href attribute.
         * @param text The link text.
         */
        public void writeTemplateUsernameLoginTag( Request breq, PrintWriter out, String target, String url, String text)
                throws IOException
        {

            String loginUrl = getUsernameLoginUrl(breq, url);
            if (target != null && (target.length() == 0 || target.equalsIgnoreCase("null")))
                target = null;

            out.print( "<a ");

            if (target != null)
                out.print("target=\"" + target + "\" ");

            out.print("href=");
            out.print( "\"" + loginUrl + "\"" );
            out.print( ">" );
            out.print(text);
            out.print( "</a>" );
        }

	public void writeTemplateGifUrl( Request breq, PrintWriter out, String html_body_class, String name )
            throws IOException
        {
            String url = getTemplateGifUrl( breq, html_body_class, name );
            out.print( url );
        }

        public void writeTemplateGifTag( Request breq, PrintWriter out, String html_body_class, String tag, String name, String attributes )
            throws IOException
        {
            String url = getTemplateGifUrl( breq, html_body_class, name );
            out.print( "<" );
            out.print( tag );
            out.print( " " );
            out.print( attributes );
            out.print( " src=\"" );
            out.print( url );
            out.print( "\" />" );
        }
        
        public String getTemplateGifUrl( Request breq, String html_body_class, String name )
        {
            return getTemplateGifUrl( breq, html_body_class, name, false );
        }
        
        public String getTemplateGifUrl( Request breq, String html_body_class, String name, boolean rel_to_context )
        {
            if ( !name.startsWith( "bs_template_" ) )
                return name;

            
            BuildingContext context = BuildingContext.getContext();
            Resource resource = null;
            
            String colour_mapper_code="";
            Template template = null;
            
            try
            {
                // get colour mapper for resource properties and user prefs.
                colour_mapper_code = styleSheet.getTemplateGifColourMapperCode( breq, html_body_class );
                // get current resource
                resource = context.getResource();
                
                if ( resource == null )
                    return name;

                // template from current resource, not style root resource
                template = Template.get( 
                    this.facilityname, 
                    resource.getImplicitHttpUIStyle(), 
                    resource.getResourceId(), 
                    name.substring( "bs_template_".length() ) );
            }
            catch ( Exception ex )
            {
                log.error( ex.getMessage(), ex );
            }
            
            if ( template == null )
                return name;
            
                
            StringBuffer rname = new StringBuffer( 128 );
            if ( !rel_to_context )
                rname.append( breq.getContextPath() );
            rname.append( "/processedgif/templates" );
            rname.append( template.getUrl() );
            rname.append( colour_mapper_code );
            
            return rname.toString();
        }
		
		
	private void navigation( Request breq, PrintWriter out, String name )
		throws IOException
		{
                    // this method is only called from legacy templates and we
                    // have to assume that the html page class has been given
                    // us in an attribuate to the BUILDING tag but we expect
                    // the default to be a navigation page.
                    String html_body_class = breq.getInsertAttribute( "html_body_class", "bodington_navigation_page" );
                    navigation( breq, out, html_body_class, name );
                }
            
	public void navigation( Request breq, Response response, String name )
		throws IOException
		{
                    // this method is only called from legacy templates and we
                    // have to assume that the html page class has been given
                    // us in an attribuate to the BUILDING tag but we expect
                    // the default to be a navigation page.
                    String html_body_class = response.getHtmlBodyClass();
                    PrintWriter out = response.getWriter();
                    navigation( breq, out, html_body_class, name );
                }
	
        /**
         * Write out markup concerned with navigating the system. This method is
         * driven by the hierarchical arrangement of resources in the system
         * (e.g. buildings, floors and rooms), to render the view to enable a
         * user to navigate the system. The <code>name</code> parameter can
         * take the values <code>links</code>, <code>linkout</code>,
         * <code>managelink</code> and <code>notifyswitch</code>. This
         * method is typically called from within
         * {@link #insert(Request, PrintWriter, String, String)}.
         * @param breq the request object.
         * @param out the writer object.
         * @param html_body_class the html body class (see
         *        {@link org.bodington.servlet.Response#getHtmlBodyClass()}).
         * @param name the name of the parameter.
         * @throws IOException
         * @see org.bodington.servlet.Response#getHtmlBodyClass()
         * @see #insert(Request, PrintWriter, String, String)
         */
        public void navigation( Request breq, PrintWriter out, String html_body_class, String name )
		throws IOException
		{
    	// TODO: Simplify the way links are handled here.
		// -> create a Link superclass or Interface. 
		if ( out == null )
		    return;
		    
                String icon, smliconurl, iconurl, spacerurl, parentsmliconurl, description, target, available_date;
                boolean range, link, italic, heading, quicklink, sentientlink, newWindow, isDescription;

                boolean textblock, newsfeed, blog;

		int n;
		BuildingContext context;
		Resource resource, parent, current, next;
		context = BuildingContext.getContext();
		FacilityList fl = FacilityList.getFacilities();
		Facility facility;
		Template template;
                String colour_mapper_code="";

                java.util.Date openDate, closeDate;

		org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession)breq.getSession( false );
		NavigationSession nav_session = breq.getServerNavigationSession();
                
                try
                {
                    StyleSheetSessionData sssd = styleSheet.getStyleSheetSessionData( http_session );
                    ColourPreferenceMapper colour_mapper = sssd.getUserColourPreferenceMapper( http_session );
                    
                    BuildingSession session = breq.getBuildingSession();
                    Color f;
                    Color b;
                    if ( "bodington_navigation_page".equals( html_body_class ) )
                    {
                        f = session.getPropertyColor( "style_navigation_graphic_foreground_colour", new Color( 0xeeeeee ) );
                        b = session.getPropertyColor( "style_navigation_graphic_background_colour", new Color( 0x111111 ) );
                    }
                    else
                    {
                        f = session.getPropertyColor( "style_graphic_foreground_colour", new Color( 0xeeeeee ) );
                        b = session.getPropertyColor( "style_graphic_background_colour", new Color( 0x111111 ) );
                    }
                    colour_mapper.setReferenceColours( b, f );
                    colour_mapper_code = "?code=" + colour_mapper.toString();
                }
                catch ( BuildingServerException bsex )
                {
                    logException( out, "Facility", "navigation", 
                        "A technical problem occurred.",
                        bsex );
                    colour_mapper_code = "";
                }
                
                
        String tree_attribute = (String)breq.getInsertAttribute( "tree", null );
        boolean tree =  tree_attribute == null                      || 
                        tree_attribute.equalsIgnoreCase( "yes" )    || 
                        tree_attribute.equalsIgnoreCase( "true" )           ;

		try
			{
			resource = context.getResource();
                        parent = resource.getParent();

			if ( name.substring( 0, 5 ).equalsIgnoreCase( "links" ) )
				{
		    parentsmliconurl = "bs_template_navigation_iconsmall.gif";
                template = Template.get( this.facilityname, resource.getImplicitHttpUIStyle(), resource.getResourceId(), "iconsmall.gif" );
                if ( template != null )
                    parentsmliconurl = breq.getContextPath() + "/processedgif/templates" + template.getUrl() + colour_mapper_code;

				Enumeration enumeration=resource.findChildren();
				n=0;

				current=null;
				while ( enumeration.hasMoreElements() )
					{
					current = (Resource)enumeration.nextElement();
					if ( current.checkPermission( Permission.SEE ) )
						break;
					current=null;
					}

				while( current!=null )
					{
					next=null;
					while ( enumeration.hasMoreElements() )
						{
						next = (Resource)enumeration.nextElement();
						if ( next.checkPermission( Permission.SEE ) )
							break;
						next=null;
						}

                    // find url of small icon for current resource
                    smliconurl = current.getName() + "/bs_template_navigation_iconsmall.gif";
        			facility = fl.get( new Integer( current.getHttpFacilityNo() ) );
        			if ( facility!=null )
        			    {
                        template = Template.get( facility.facilityname, current.getImplicitHttpUIStyle(), current.getResourceId(), "iconsmall.gif" );
                        if ( template != null )
                            smliconurl = breq.getContextPath() + "/processedgif/templates" + template.getUrl() + colour_mapper_code;
                        }
                    
					if ( n==0 )
						{
						// if we have found a resource to list start off the table
						out.print( "<TABLE border=0 cellpadding=0 cellspacing=0>" );
						if ( tree )
						    {
						    // first display
						    // icon for containing resource
						    out.print( "<TR><TD VALIGN=TOP COLSPAN=2>" );
						    out.print( "<IMG alt=\""+ iconName(resource)+"\" ");
						    out.print(" BORDER=0 SRC=\"" );
						    out.print( parentsmliconurl );
						    out.print( "\">" );
						    //out.print( "</TD><TD>" );
						    out.println( "</TD></TR>" );
						    out.println( "<TR><TD VALIGN=TOP BACKGROUND=" + breq.getContextPath() + "/icons/thread-line.gif>" );
						    out.print( "<IMG BORDER=0 SRC=\"" + breq.getContextPath() + "/icons/thread-filler.gif\">" );
						    out.println( "</TD><TD></TD></TR>" );
						    }
						}
					n++;

					if ( current.checkPermission( Permission.VIEW ) )
				    	{
				    	link=true;
				    	italic=false;
				    	}
					else
				    	if ( nav_session.isAnonymous() )
				        	{
				    		link=true;
				    		italic=true;
				    		}
				    	else
				        	{
				    		link=false;
				    		italic=false;
				    		}

                                        description = current.getDescription().trim();
                                        if (description==null || description.length()==0)
                                          isDescription = false;
                                        else
                                          isDescription = true;

                                        heading = (current.getResourceType() == Resource.RESOURCE_HEADING);

                                        textblock = (current.getResourceType() == Resource.RESOURCE_TEXTBLOCK);
                                        newsfeed = (current.getResourceType() == Resource.RESOURCE_NEWSFEED);
                                        blog = (current.getResourceType() == Resource.RESOURCE_BLOG);
                                        
                                        sentientlink = (current.getResourceType() == Resource.RESOURCE_SENTIENTLINK);

                                        quicklink = (current.getResourceType() == Resource.RESOURCE_QUICKLINK);

                                        if (quicklink)
                                          newWindow = ((QuickLink)current).isNewWindow();
					else if (sentientlink)
                                          newWindow = ((SentientLink)current).isNewWindow();
                                        else
                                          newWindow = false;

                                        available_date = timedResourceText(resource);
                                        if (available_date != null)
                                            out.print("<br />" + available_date);

					out.println( "<TR>" );
					
					if ( tree )
					    {
					    if ( next==null )
						    out.println( "<TD VALIGN=TOP>" );
					    else
						    out.println( "<TD VALIGN=TOP BACKGROUND=" + breq.getContextPath() + "/icons/thread-line.gif>" );
    	
					    if ( current.getResourceType() != Resource.RESOURCE_HEADING )
					    out.println( "<IMG SRC=" + breq.getContextPath() + "/icons/resource-arrow.gif>" );
					    else
						    out.println( "<IMG BORDER=0 SRC=" + breq.getContextPath() + "/icons/thread-filler.gif>" );
					    
					    out.println( "</TD>" );
                        }

					if ( current.getResourceType() == Resource.RESOURCE_HEADING )
						{
						out.print( "<TD VALIGN=TOP COLSPAN=2>" );
						out.print( "<H4 CLASS=bs-links-heading>" );
						out.print( current.getTitle() );
                                                out.print( "</H4>" );

                                                target = " <A TARGET=_top HREF=\"" + current.getName() + "/\" alt=\"Manage heading\">&gt;</A>";
						
                                                if (isDescription) {
                                                  out.println("<DIV CLASS=bs-links-heading-description>");
                                                  out.print(description);
                                                  if (current.checkPermission(Permission.MANAGE)) {
                                                    out.print(target);
                                                  if (available_date != null)
                                                      out.println(available_date);
								}
    						out.println( "</DIV>" );
    					    }

                                                if ( !isDescription && current.checkPermission( Permission.MANAGE ) ) {
                                                  out.print( target );
                                                  if (available_date != null)
                                                    out.println(available_date);
                                                }

						//if ( next!=null && next.getResourceType() != Resource.RESOURCE_HEADING )
						out.println( "</TD>" );
						}

                                                else if ( blog || newsfeed ) /** @todo add textblock.... */
                                                {
//                                                  out.println( current.getMenuItemDisplay( current, smliconurl ) );/** @todo make method static? */
                                                }



					else
						{

						out.print( "<TD VALIGN=TOP>" );
						//out.println( "<IMG SRC=" + breq.getContextPath() + "/icons/thread-filler.gif><BR>" );

                                                /*
                                                 * WebLearn inserted code; A Corfield 10/12/03
                                                 * Handles QuickLink newWindow property
                                                 */
                                                if ( link ) {
                                                  if ( quicklink ) {
                                                    String href = QuickLinkFacility.getLink((QuickLink)current, breq, true);
                                                    if (href.equals("") || !newWindow)
                                                      target = "<A TARGET=_top HREF=\"" + href + "\">";
                                                    else
                                                      target = "<A TARGET=_new HREF=\"" + href + "\">";
                                                  } else if ( sentientlink ) {
						    String href = ((SentientLink)current).getUrl();
						    if (href.equals("") || !newWindow)
						      target = "<A TARGET=\"menu\" HREF=\"" + href + "\">";
						    else
                                                      target = "<A TARGET=_blank HREF=\"" + href + "\">";
                                                  } else
                                                    target = "<A TARGET=_top HREF=\"" + current.getName() + "/\">";
                                                  out.print( target );
                                                } else
                                                  target = null;

                                              
                        
                        out.print( "<IMG alt=\""+ iconName(current)+
                            "\" CLASS=bs-links-icon BORDER=0 SRC=\"" );
						out.print( smliconurl );
						out.print( "\">" );
						if ( link )
							out.print( "</A>" );
						out.println( "</TD>" );

						out.print( "<TD VALIGN=TOP>" );
						//out.println( "<IMG SRC=" + breq.getContextPath() + "/icons/thread-filler.gif><BR>" );

						if ( link )
   							out.print( "<A TARGET=_top HREF=\"" + current.getName() + "/\">" );
	    				//out.print( "<NOBR>" );
						out.print( "<SPAN CLASS=bs-links-title>" );
						if ( italic )
							out.print( "<I>" );

						out.print( current.getTitle() );

						if ( italic )
							out.print( "</I>" );
	    				//out.println( "</NOBR>" );
						out.println( "</SPAN>" );
						if ( link )
							out.print( "</A>" );
						else
	    					out.println( "<BR><SPAN CLASS=bs-links-no-access><I>You are not included on the access list for this item.</SPAN>" );

                                                if (quicklink || sentientlink)/* @todo link directly to manage.html? */
                                                  target = " <A TARGET=_top HREF=\"" + current.getName() + "/\" alt=\"Manage link\">&gt;</A>";

                                                if (isDescription) {
							out.print( "<DIV CLASS=bs-links-description>" );
							out.print( current.getDescription()  );
                                                  if (current.checkPermission(Permission.MANAGE)) {
                                                    if (quicklink || sentientlink)
                                                      out.print(target);
                                                    if (available_date != null)
                                                      out.println(available_date);
                                                  }
							out.print( "</DIV>" );
							}

                                                if (!isDescription && current.checkPermission(Permission.MANAGE)) {
                                                  if (quicklink || sentientlink)
                                                    out.println(target);
                                                  if (available_date != null)
                                                    out.println(available_date);
                                                }

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


					current = next;
					}
				if ( n>0 )
					out.println( "</TABLE>" );
				return;
				}

			if ( name.equalsIgnoreCase( "linkout" ) )
				{
                                String nav_bar_height = getAutomaticPreferenceByScreenSize( 
                                        breq, "preference.navigation.nav_bar_height", "small", "big" );
                                boolean small_bar = "small".equalsIgnoreCase( nav_bar_height );
                                String nav_bar_icons = getAutomaticPreferenceByScreenSize( 
                                        breq, "preference.navigation.nav_bar_icon_list", "reduced", "full" );
                                boolean reduced_icons = "reduced".equalsIgnoreCase( nav_bar_icons );
				String absurl= breq.getContextPath() +
						breq.getServletPath() + "/";
				Enumeration branch;
				try
					{
					branch = resource.findAncestors();
					}
				catch ( BuildingServerException bsex )
					{
					out.println( bsex.toString() );
					return;
					}


				//make sure there is no white space between tags
				//to prevent line breaks.
				//iterate the location tree adding icons to backtrack out
				//include the current location and put a link on it (which
				//will reload current location)
                template = Template.get( this.facilityname, resource.getImplicitHttpUIStyle(), resource.getResourceId(), "spacer.gif" );
                if ( template != null )
                    spacerurl = breq.getContextPath() + "/processedgif/templates" + template.getUrl() + colour_mapper_code;
                else
                    spacerurl = "bs_template_navigation_spacer.gif";
                    
				out.print( "<NOBR>"   );
				for ( int i=0; branch.hasMoreElements(); i++ )
					{
					current=(Resource)branch.nextElement();
					if ( current.getParentResourceId() != null )
				    	absurl=absurl+current.getName()+"/";

                                        // skip if reduced list of icons option on
                                        // and this is neither selected resource or parent
                                        if ( reduced_icons && current!=resource && current != parent )
                                            continue;

                    iconurl = absurl + (small_bar?"bs_template_navigation_iconsmall.gif":"bs_template_navigation_icon.gif");

        			facility = fl.get( new Integer( current.getHttpFacilityNo() ) );
        			if ( facility!=null )
        			    {
                        template = Template.get( facility.facilityname, current.getImplicitHttpUIStyle(), current.getResourceId(), 
                                (small_bar?"iconsmall.gif":"icon.gif") );
                        if ( template != null )
                            iconurl = breq.getContextPath() + "/processedgif/templates" + template.getUrl() + colour_mapper_code;
                        }
                                if ( !small_bar )
                                {
					out.print( "<IMG ALT=\"*\" ALIGN=LEFT HSPACE=0 VSPACE=0 BORDER=0 SRC=\"" );
    				out.print( spacerurl );
					out.print( "\">" );
                                }
					out.print( "<A TARGET=_top HREF=\"" + absurl + "\">" );
					out.print( "<IMG ALIGN=LEFT HSPACE=0 VSPACE=0 BORDER=0 SRC=\"" );
					out.print( iconurl );
					out.print( "\" " );
					out.print( "ALT=\"" );
                    out.print( iconName(current));
					out.print( "\" " );
                    // A user might not have SEE permission for all parent resources
                    if (current.checkPermission(Permission.SEE))
                    {
                        out.print( "TITLE=\"" );
                        if ( branch.hasMoreElements() )
                            out.print( "Jump out to: " );
                        else
                            out.print( "Refresh view onto: " );
                        out.print( current.getTitle() );
                    }
                    out.print( "\">" );
					out.print( "</A>" );
					}

				out.println( "</NOBR>"   );
				return;
				}




			if ( name.equalsIgnoreCase( "MANAGELINK" ) )
				{
				out.println( "<A TARGET=buildmain HREF=\"" +
						breq.absoluteURL() +
						"bs_template_manage.html\">Manage</A> this resource." );
				}

			if ( name.equalsIgnoreCase( "notifyswitch" ) )
				{
				User user = (User)context.getUser();
				if ( nav_session.isAnonymous() )
					return;

				UserEventSetup ues=null;
				try
					{
       				ues = UserEventSetup.findOrCreate(user);

					//check input to see if it is switched
					String str=breq.getParameter( "switch.x" );
					if ( str!=null )
						{
						if ( ues.containsResource( breq.getResource() ) )
							{
							ues.removeResource( breq.getResource() );
							out.println( "<SCRIPT LANGUAGE=JavaScript>" );
							out.println( "message = \"From now on you will not be notified of events in this location.\";" );
							out.println( "</SCRIPT>" );
							}
						else
							{
							ues.addResource( breq.getResource() );
							out.println( "<SCRIPT LANGUAGE=JavaScript>" );
							out.println( "message = \"From now on you will be notified of events in this location.\" ;" );
							out.println( "</SCRIPT>" );
							}
						}

                    out.print("<a href=\"bs_template_top.html?switch.x=0\">");
                    if ( ues.containsResource( breq.getResource() ) )
                        out.print("Don't Notify Me");
                    else
                        out.print("Notify Me");
                    out.print( "</a>" );

					}
				catch ( Exception ex )
					{
					//do nowt
					return;
					}
				}
			}
		catch ( BuildingServerException bsex )
			{
			return;
			}

		out.print( "<! navigation HTML inserted here>" );
		}


    /**
     * Utility method for displaying the icon ALT text.
     * @param resource The resource to display the alt text for.
     * @return The string for the alt text.
     */
    public static String iconName(Resource resource)
        {
        String resourceName = ResourceUtils.getFacility(resource).getName();
       	return "Icon for "
            + (TextUtils.beginsWithVowel(resourceName) ? "an" : "a") + " "
            + resourceName + ".";
        }

    public String fileParameter( Request req )
        {
	String url;
	StringBuffer dest_filename = new StringBuffer();

	for ( int i=0; i<req.getTemplateParameterCount(); i++ )
	    {
	    dest_filename.append( "/" );
	    url = (String)req.getTemplateParameter( i );
	    dest_filename.append( url );
	    }

	return dest_filename.toString();
        }

    /**
     * Uploads file from user's computer.
     * @param req The Request object
     * @param out Writer to notify user with messages/errors
     * @param rawconfirmation Use extremely brief confirmation message, or
     *        slightly less extremely brief confirmation message!
     * @throws ServletException
     * @throws IOException
     * @return boolean indicating successful file upload.
     */
    
    protected boolean upload( Request req, PrintWriter out, boolean rawconfirmation )
	throws ServletException, IOException
	{
	return upload( req, out, req.getResource(), rawconfirmation );
	}

    /**
     * Uploads file from user's computer.
     * @param req The Request object
     * @param out Writer to notify user with messages/errors
     * @param parent The resource to upload the file to 
     * @param rawconfirmation Use extremely brief confirmation message, or
     *        slightly less extremely brief confirmation message!
     * @throws ServletException
     * @throws IOException
     * @return boolean indicating successful file upload.
     */
    
	protected boolean upload( Request req, PrintWriter out, Resource parent, boolean rawconfirmation )
		throws ServletException, IOException
		{
		StringBuffer dest_filename=new StringBuffer();
		String file, file_name;
		BuildingSession session;
		UploadedFileSummary summary;

		if ( rawconfirmation )
			out.println( "<PRE>" );
		
		file = req.getParameterFileLocation( "file" );
		file_name = req.getParameterFileName( "file" );

		if ( file_name == null || file_name.length() == 0 )
			{
			if ( rawconfirmation )
				out.print( "ERROR:\t" );
			out.println( "Upload failed because no file name was supplied in the form." );
			if ( rawconfirmation )
				out.println( "</PRE>" );
			return false;
			}

		
		dest_filename.append( fileParameter( req ) );
		if ( dest_filename.length()>0 )
			dest_filename.append( "/" );
		
		
		try
			{
   			session = BuildingSessionManagerImpl.getSession( parent );
   			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 )
					{
					if ( rawconfirmation )
						out.print( "ERROR:\t" );
					out.println( "File names may not contain any slash or backslash characters." );
					if ( rawconfirmation )
						out.println( "</PRE>" );
					return false;
					}
				dest_filename.append( file_name );
                
				summary = session.getUploadedFileSession().transferFile( file, dest_filename.toString(), null );
				
				if ( rawconfirmation )
					out.println( "OK" );
				else
					out.println( "<br />File upload succeeded." );

	       		UserFileEvent event = new UserFileEvent(
                    UserFileEvent.EVENT_UPLOAD, req.getResource(), summary);
				event.save();

				}
			}
		catch (BuildingServerUserException bsue)
		{
            if (rawconfirmation)
                Utils.writeUserExceptionRaw(bsue, out);
            else
                Utils.writeUserExceptionHTML(bsue, out);
		}
		catch ( Exception ex )
			{
			if ( rawconfirmation )
			    {
			    out.println( "ERROR: Exception: " + ex.getMessage() );
			    logException( null, "Facility", "upload", 
				    "A technical problem occurred.",
				    ex );
			    }
			else
			    logException( out, "Facility", "upload", 
				    "A technical problem occurred.",
				    ex );
			return false;
			}
		if ( rawconfirmation )
			out.println( "</PRE>" );

		return true;
		}
		

    /**
     * Upload a zipfile into the resource.
     * Currently this doesn't use
     * {@link UploadedFileSession#transferZipFile(ZipFile, String)} because it wants
     * to give the user feedback as the unpacking progresses.
     * @param req The servlet request.
     * @param out The PrintWriter to output the progress messages to.
     */
	private void uploadzip( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		String dest_filename;
		String zipfile, file, file_name, mime_type, startfilename;
		BuildingSession session;
		PrimaryKey id;
		UploadedFileSummary summary;
		ZipFile archive=null;
		ZipEntry zipentry;
		InputStream zipinput;
		OutputStream fout;
		byte buffer[] = new byte[1024];

		zipfile = req.getParameterFileLocation( "file" );
		
		startfilename = fileParameter( req );
		if ( startfilename.length()>0 )
			startfilename = startfilename + "/";

		try
			{
   			session = BuildingSessionManagerImpl.getSession( req.getResource() );
   			if ( session==null )
   				throw new BuildingServerException( "Unable to access the destination resource." );
   			}
		catch ( Exception ex )
			{
			logException( out, "Facility", "uploadzip", 
			    "A technical problem occurred.",
			    ex );
			return;
			}
			
	out.println( "<p>Zip file uploaded, started to unpack.<br />" );

	    try
	    {
            archive = new ZipFile( zipfile );
	    }
	    catch (ZipException e)
	    {
	        out.println("There was a problem expanding the zip file. Maybe it "
	            + "isn't a zip file?");
	        log.error("Problem expanding zip file: " + zipfile);
	        return;
	    }
        for ( Enumeration enumeration=archive.entries(); enumeration.hasMoreElements(); )
            {
            zipentry = (ZipEntry)enumeration.nextElement();
            log.debug( "ZIP Entry = [" + zipentry + "]" );

            //ignore directories - don't create empty folders
			file_name = zipentry.getName();
            if ( zipentry.isDirectory() || file_name==null )
            	continue;

            log.debug( "ZIP file = [" + file_name + "]" );

	    if ( file_name.indexOf( '\\' ) >=0 )
		{
		out.print( "<P>Skipped ZIP file entry \"" );
		out.print( file_name );
		out.print( "\" because the name contained a back slash.</P><HR>" );
		continue;
		}
	    
	    if ( file_name.startsWith( "/" ) )
		dest_filename = startfilename + file_name.substring( 1 );
	    else
		dest_filename = startfilename + file_name;
	    
			zipinput = new BufferedInputStream(archive.getInputStream( zipentry ));
			if ( zipinput==null )
			    {
			    out.println( "<P>Unreadable ZIP file entry: " );
			    out.println( zipentry.toString() );
			    out.println( "</P><HR>" );
			    continue;
			    }
			    
			file = BuildingContext.createTempFile( "zipentry", ".zbin" ).getAbsolutePath();
			fout = new BufferedOutputStream(new FileOutputStream( file ));
            int bytesRead;
			while ( (bytesRead = zipinput.read(buffer)) >= 0 )
				fout.write(buffer, 0, bytesRead);
			fout.close();
			zipinput.close();

           
			try
				{
				summary = session.getUploadedFileSession().transferFile( file, dest_filename, null );
					
				out.println( "Unpacked another file, looking for next....<br />" );
				out.flush();

	       		UserFileEvent event = new UserFileEvent(
                    UserFileEvent.EVENT_UPLOAD, req.getResource(), summary);
				event.save();
				}
            catch (BuildingServerUserException bsue)
                {
                out.print("Problem with file: "+ dest_filename);
                Utils.writeUserExceptionHTML(bsue, out);
                }
			catch ( Exception ex )
				{
				logException( out, "Facility", "uploadzip", 
				    "A technical problem occurred.",
				    ex );
				return;
				}
			}

		out.println( "Finished unpacking the ZIP file.<p>" );
		out.println( "<SCRIPT LANGUAGE=JavaScript>" );
		out.println( "window.parent.frames[\"managefileindex\"].location.reload()" );
		out.println( "</SCRIPT>" );
		out.flush();
			
		archive.close();
		(new File( zipfile )).delete();
		}
		
	/**
	 * Gets details about an uploaded file.
	 * @param req The request.
	 * @param out The ouput to write HTML to.
	 * @throws ServletException Shouldn't be thrown
	 * @throws IOException Thrown when we have problems doing RMI.
	 */
	private void fileinfo( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		String dest_filename=null;
		String dest_url=null;
		String property, url;
		BuildingSession session;
		Object attribute;
		PrimaryKey id;
		UploadedFileSummary summary;
		
		
		property = req.getInsertAttribute( "property", null );
		if ( property==null )
			return;
			
		try
			{
			session = req.getBuildingSession();
            
			summary = (UploadedFileSummary)req.getRequestProperty( "uploadedfilesummary" );
			if ( summary==null )
				{
				dest_filename = fileParameter( req );
				
				log.debug( "File url is " + dest_filename.toString()  );

				summary = session.getUploadedFileSession().getFileSummary( dest_filename.toString() );

				// pretend there's a root folder even if there isn't one.
				if ( summary==null && dest_filename.length()==0 )
					{
					log.debug( "making pretend root folder" );
                    UploadedFile uploadedFile = new UploadedFile();
                    uploadedFile.setFlags(UploadedFileSummary.FLAG_FOLDER);
                    summary = uploadedFile.getSummary();
					}
					
				if ( summary==null )
					{
					if ( property.equalsIgnoreCase( "name" ) )
						out.print( "<I>file not found</I>" );
					log.info("Unable to find uploaded file summary");
					return;
					}
				
				// if its a made up root folder the url is blank
				// otherwise the session can provide the url
				if ( summary.getUploadedFileId() == null )
				    dest_url = "";
				else
				    dest_url = session.getUploadedFileSession().getPublishedURL( summary.getUploadedFileId() );

				req.setRequestProperty( "uploadedfilepath", dest_url );
				req.setRequestProperty( "uploadedfilesummary", summary );
				}
			else
				dest_url = (String)req.getRequestProperty( "uploadedfilepath" );

			/*
			 * WebLearn inserted code; A Corfield 02/12/2003
			 * Prints UploadedFileId
			 */
			if ( property.equalsIgnoreCase( "id" ) )
			    {
			    if ( summary.getParentUploadedFileId()==null )
			        out.print( "<I>Root folder for this resource</I>" );
			    else
			        out.print( summary.getUploadedFileId() );
			    }
				
			if ( property.equalsIgnoreCase( "name" ) )
				{
				if ( summary.getParentUploadedFileId()==null )
					out.print( "<I>Root folder for this resource</I>" );
				else
					out.print( summary.getName() );
				}
			
			if ( property.equalsIgnoreCase( "url" ) )
				{
				if ( summary.getParentUploadedFileId()==null )
					out.print( "<I>Root folder for this resource</I>" );
				else
					out.print( summary.getUrl() );
				}
			
			if ( property.equalsIgnoreCase( "webaddress" ) )
				{
				BodingtonURL bodUrl = new BodingtonURL(req);
                if (summary.getParentUploadedFileId() == null)
                    out.print(bodUrl.getResourceUrl(req.getResource()));
                else
                    {   
                    UploadedFile file = UploadedFile.findUploadedFile(summary.getUploadedFileId());
                    out.print( bodUrl.getUploadedFileUrl(file) );
                    }
				}
			
			if ( property.equalsIgnoreCase( "size" ) )
				out.print( Utils.toHumanReadableSize(summary.getSize()) );
			
			if ( property.equalsIgnoreCase( "updatedtime" ) )
				{
				if ( summary.getUpdatedTime() != null )
					/* WebLearn (Bug-fix) A O'Connor [13/12/04]
					 * Displays date in more user-oriented way */
					out.print( DateFormatter.formatDate( summary.getUpdatedTime(), DateFormatter.MEDIUM) );
				else
					out.print( "<CENTER>~</CENTER>" );
				}

			if ( property.equalsIgnoreCase( "updateuser" ) )
				{
				PrimaryKey uid = summary.getUpdateUserId();
				if ( uid!=null )
					out.print( session.getNameOfUser( uid ) );
				else
					out.print( "<CENTER>~</CENTER>" );
				}
            if ( property.equalsIgnoreCase( "mimetype" ) )
                {
                String mimeType = summary.getMimeType();
                if (mimeType != null && mimeType.length() > 0)
                    out.print(mimeType);
                else
                    out.print("auto");
                }
			}
        
		catch ( Exception ex )
			{
			logException( out, "Facility", "fileinfo", 
			    "A technical problem occurred.",
			    ex );
			}
		}

	private void filerename( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		StringBuffer dest_filename=null;
		String newname;
		BuildingSession session;
		Object attribute;
		PrimaryKey id;
		UploadedFileSummary summary;
		
		newname = req.getParameter( "name" );
		if ( newname==null || newname.length()==0 )
			{
			out.println( "<P>You have to supply a name</P>" );
			return;
			}
		
		if ( newname.length()>255 )
			{
			out.println( "<P>Name is too long</P>" );
			return;
			}
			
		if ( newname.indexOf( '/' ) >=0 || newname.indexOf( '\\' ) >=0 )
			{
			out.println( "<P>Name cannot contain forward or backward slashes.</P>" );
			return;
			}
		
			
		try
			{
   			session = BuildingSessionManagerImpl.getSession( req.getResource() );
   			if ( session==null )
   				throw new BuildingServerException( "Unable to access the destination resource." );

   			dest_filename=new StringBuffer();
			dest_filename.append( fileParameter( req ) );

			session.getUploadedFileSession().renameFile( dest_filename.toString(), newname );
			}
		catch (BuildingServerUserException bsue)
		{
		    out.write(bsue.getMessage());
		}
		catch ( Exception ex )
			{
			logException( out, "Facility", "filerename", 
			    "A technical problem occurred.",
			    ex );
			}
		}



	private boolean fileisdeleted( Request req )
		throws ServletException, IOException
		{
		StringBuffer dest_filename=null;
		String property;
		BuildingSession session;
		Object attribute;
		PrimaryKey id;
		UploadedFileSummary summary;
		
		
		try
			{
			session = req.getBuildingSession();

			summary = (UploadedFileSummary)req.getRequestProperty( "uploadedfilesummary" );
			if ( summary==null )
				{
   				dest_filename=new StringBuffer();
        		dest_filename.append( fileParameter( req ) );
					
				summary = session.getUploadedFileSession().getFileSummary( dest_filename.toString() );

				// pretend there's a root folder even if there isn't one.
				if ( summary==null && dest_filename.length()==0 )
					{
                    UploadedFile uploadedFile = new UploadedFile();
                    uploadedFile.setFlags(UploadedFileSummary.FLAG_FOLDER);
                    summary = uploadedFile.getSummary();
					}
					
				if ( summary==null )
					{
					return false;
					}
				
				if ( summary.isFolder() )
					dest_filename.append( "/" );
				req.setRequestProperty( "uploadedfilepath", dest_filename.toString() );
				req.setRequestProperty( "uploadedfilesummary", summary );
				}

			if ( dest_filename==null )
				{
				dest_filename = new StringBuffer();
				dest_filename.append( req.getRequestProperty( "uploadedfilepath" ) );
				}
				
			return summary.isDeleted();
			}
		catch ( Exception ex )
			{
			logException( null, "Facility", "fileisdeleted", 
			    "A technical problem occurred.",
			    ex );
			}
		return false;
		}


	private void foldercreate( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		StringBuffer dest_filename=new StringBuffer();
		String folder;
		BuildingSession session;
		UploadedFileSummary summary;
		
		folder = req.getParameter( "subfolder" );
		if ( folder==null || folder.length()<=0 )
			{
			out.println( "<P>Folder name must have one or more characters.</P>" );
			return;
			}

		if ( folder.length()>255 )
			{
			out.println( "<P>Name is too long</P>" );
			return;
			}
			
		
		dest_filename.append( fileParameter( req ) );
		if ( dest_filename.length()>0 )
			dest_filename.append( "/" );
		dest_filename.append( folder );		
		
		try
			{
		    session = BuildingSessionManagerImpl.getSession( req.getResource() );
		    if ( session==null )
		        throw new BuildingServerException( "Unable to access the destination resource." );
		    
		    summary = session.getUploadedFileSession().createFolder( dest_filename.toString() );
		    
		    UserFileEvent event = new UserFileEvent(UserFileEvent.EVENT_FOLDER_CREATED,
		        req.getResource(), summary);
		    event.save();
			}
		catch ( Exception ex )
			{
			logException( out, "Facility", "foldercreate", 
			    "A technical problem occurred.",
			    ex );
			}
		}


	protected void rawfileproperties( PrintWriter out, Vector path )
		throws IOException
		{
		UploadedFileSummary ufs=null;
		
		if ( path.size()<2 )
			{
			return;
			}
			
		for ( int i=1; i< path.size(); i++ )
			{
			ufs = (UploadedFileSummary)path.elementAt( i );
			out.print( "/" );
			out.print( ufs.getUrl() );
			}
		
		if ( ufs.isFolder() )
			out.print( "/" );
			
		out.print( "\t" );

		out.print( ufs.getSize() );

		out.print( "\t" );

		out.print( ufs.getUpdatedTime().toGMTString() );

		out.print( "\n" );
		}

	protected void rawfileindex( Request req, PrintWriter out )
		throws IOException, ServletException
		{
		int i, j, k, n;
		StringBuffer address= new StringBuffer();
		BuildingSession session;
		UploadedFileSummary[] summaries;
		UploadedFileSummary parent;

		Vector path = new Vector();


		out.println( "<PRE>" );
		try
			{
			session = req.getBuildingSession();
   			//get ALL files including those marked as deleted.
			summaries = session.getUploadedFileSession().getFileAndDescendentSummaries( null, false );
			}
		catch ( Exception ex )
			{
			out.println( "ERROR:\tTechnical Problem finding file list " + ex );
			out.println( "</PRE>" );
			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] );
		
		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] );
			
			rawfileproperties( out, path );
			}
		out.println( "</PRE>" );
		}


	/**
	 * Outputs a tree diagram of files in the resource.
	 * 
	 * @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 fileindextitle( Request req, PrintWriter out, String basepath )
		throws IOException, ServletException
		{
		//out.print( "Filespace for this location." );
		}






	/**
	 * Outputs a tree diagram of files in the resource.
	 *
	 * @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 fileindex( Request req, PrintWriter out, String basepath, boolean show_root, boolean hide_deleted, int link_type, String target )
		throws IOException, ServletException
		{
		int i, j, k, old_level, top_level;
		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;

		try
			{
			session = req.getBuildingSession();
			summaries = session.getUploadedFileSession().getFileAndDescendentSummaries( basepath, hide_deleted );
   				}
		catch ( Exception ex )
			{
			out.println( "<br />Technical Problem finding file list" + ex );
			return;
			}

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

		out.print( "<TABLE border=0 cellpadding=0 cellspacing=4>" );

		if ( show_root )
			{
			out.println( "<TR><TD VALIGN=TOP></TD><TD VALIGN=TOP>" );
			if ( link_type==1 )
            {
			    out.print( "<A HREF=" + req.absoluteURL() 
                    + "bs_template_managefolder.html" );
			    if (target != null)
			        out.print( " TARGET=\"" + target + "\"" );
			    out.print( ">" );
            }
			out.print( "<NOBR><IMG BORDER=0 SRC=bs_template_folder-small.gif> <B>");
            if (summaries[0].isDeleted())
                out.print("<FONT COLOR=GRAY>/</FONT>");
            else
                out.print("/");
            out.print( "</NOBR>" );
			if ( link_type==1 )
				out.println( "</A>" );
			out.println( "</TD></TR>" );
			top_level=1;
			
			if ( summaries.length<2 )
				{
				out.println( "<TR><TD VALIGN=TOP></TD><TD><IMG SRC=" + req.getContextPath() + "/icons/thread-empty.gif> " );
				out.println( "This resource contains no uploaded files.</TD></TR>" );
				out.println( "</TABLE>" );
				return;
				}
			}
		else
			top_level=2;

		path.addElement( summaries[0] );
		old_level=1;
		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] );


			//work out first whether we need a continuation line from previous
			//node onto a later sibling of that node.
			for ( j=i+1; j<summaries.length; j++ )
				if ( i>1 && summaries[i-1].getParentUploadedFileId().equals(
								summaries[j].getParentUploadedFileId() ) )
					break;

			// make a new row with continuation bar in first cell and
			// put table in second cell if this file within previous
			// (except first top level node)
			if ( path.size()>old_level && path.size()>top_level )
				{
				out.print( "<TR>" );
				if ( j == summaries.length || old_level<=top_level )
					{
					out.println( "<TD VALIGN=TOP>" );
					}
				else
					{
					out.println( "<TD VALIGN=TOP BACKGROUND=" + req.getContextPath() + "/icons/thread-line.gif>" );
					out.println( "<IMG SRC=" + req.getContextPath() + "/icons/thread-blank.gif>" );
					}
				out.println( "</TD><TD><TABLE border=0 cellpadding=0 cellspacing=0>" );
				}
			//close tables until back to level of this message
			for ( k=path.size(); k<old_level; k++ )
				{
				out.print( "</TABLE></TD></TR>" );
				}

			old_level = path.size();

			out.println( "<TR>" );

			//work out first whether we need a continuation line from
			//node onto a later sibling.
			for ( j=i+1; j<summaries.length; j++ )
				if ( summaries[i].getParentUploadedFileId().equals(
								summaries[j].getParentUploadedFileId() ) )
					break;
			if ( j == summaries.length || path.size()<=top_level )
				out.println( "<TD VALIGN=TOP>" );
			else
				out.println( "<TD VALIGN=TOP BACKGROUND=" + req.getContextPath() + "/icons/thread-line.gif>" );


			//choose icon depending on whether node has children
			//later we will need to cope with nodes that are closed and
			//need a plus icon.
			//if ( (summaries[i].getLeftIndex()+1) == summaries[i].getRightIndex() )
			if ( path.size()>top_level )
				out.println( "<IMG SRC=" + req.getContextPath() + "/icons/thread-empty.gif>" );
			//else
			//	out.print( "<IMG BORDER=0 SRC=" + breq.getContextPath() + "/icons/thread-minus.gif>" );
			out.print( "</TD><TD>" );


			out.println( "<IMG SRC=" + req.getContextPath() + "/icons/thread-filler.gif><BR>" );

			if ( !summaries[i].isFolder() || link_type==1 )
			{
			    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 ( link_type==1 )
			    {
			        if ( summaries[i].isFolder() )
			            address.append( "/bs_template_managefolder.html" );
			        else
			            address.append( "/bs_template_managefileinfo.html" );
			    }
			    out.print( "<A HREF=\"" );
			    out.print( address.toString() );
			    out.print( "\"" );
			    if (target != null)
			        out.print( " TARGET=\"" + target + "\"" );
			    out.print( ">" );
			}
			out.print( "<NOBR>" );
            
			if ( summaries[i].isFolder() )
				out.print( "<IMG BORDER=0 SRC=bs_template_folder-small.gif> " );
			else
				out.print( "<IMG BORDER=0 SRC=bs_template_file-small.gif>" );


			if ( summaries[i].isDeleted() )
				out.print( "<FONT COLOR=GRAY>" );

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

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

			if ( !summaries[i].isFolder() || link_type==1 )
				out.print( "</A>" );


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

		//close tables
		for ( i=top_level; i<old_level; i++ )
			{
			out.print( "</TABLE></TD></TR>" );
			}

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

	private void filedelete( Request req, PrintWriter out, boolean delete )
		throws ServletException, IOException
		{
		StringBuffer dest_filename=null;
		BuildingSession session;
		Object attribute;
		PrimaryKey id;
		UploadedFileSummary summary;
		
		
		try
			{
   			session = BuildingSessionManagerImpl.getSession( req.getResource() );
   			if ( session==null )
   				throw new BuildingServerException( "Unable to access the destination resource." );
					
   			dest_filename=new StringBuffer();
    		dest_filename.append( fileParameter( req ) );
			if ( delete )
				session.getUploadedFileSession().deleteFile( dest_filename.toString() );
			else
				session.getUploadedFileSession().undeleteFile( 
						dest_filename.toString(), 
						req.getInsertAttribute( "recurse", "no" ).equalsIgnoreCase( "yes" ) );
			}
		catch ( Exception ex )
			{
			logException( out, "Facility", "filedelete", 
			    "A technical problem occurred.",
			    ex );
			}
		}



	private void metadataupload(  Request req, PrintWriter out )
		throws IOException
		{
		BuildingSession session;

		String file = req.getParameterFileLocation( "file" );
		if ( file == null || file.length() == 0 )
		    {
		    out.println( "<P>Import failed: no file was supplied.</P>" );
		    return;
		    }

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



		    session.importMetadata( file );
    		out.println( "DONE" );
		    }
		catch ( BuildingServerException bsex )
		    {
    		out.println( "<PRE>" );
    		out.println( bsex.getMessage() );
    		out.println( "</PRE>" );
		    }
		
		
		return;
		}


	private void metadata(  Request req, PrintWriter out )
		throws IOException
		{
		BuildingSession session;

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

			String file_name = session.exportMetadata();
			if ( file_name == null )
				{
				out.println( "<P>There is currently no metadata defined for this resource.</P>" );
				return;
				}
			
			InputStreamReader in = new InputStreamReader( new FileInputStream ( file_name ), "UTF8" );
			out.print( "<FORM><SPAN CLASS=bs-textarea><TEXTAREA COLS=40 ROWS=20 CLASS=text>" );
			int b;
			while ( (b = in.read()) >= 0 )
				out.print( (char)b );
			out.println( "</TEXTAREA></SPAN></FORM>" );
			in.close();
			File f = new File( file_name );
			f.delete();
			
		    }
		catch ( BuildingServerException bsex )
		    {
    		out.println( "<PRE>" );
    		out.println( bsex.getMessage() );
    		out.println( "</PRE>" );
		    }
		
		
		return;
		}


	private void metadatasearch(  Request req, PrintWriter out )
	throws IOException
	{
	    BuildingSession nav_session;
        HttpSession http_session;
        Hashtable search_settings;
        Vector list;
	    
	    try
	    {
            nav_session = req.getBuildingSession();
	        http_session = (HttpSession)req.getSession();
	        
            // no submit button pressed, display last set of results in Advanced Search window:
            if (req.getParameter( "search" ) == null)
            {
            list = (Vector)http_session.getAttribute( "org.bodington.servlet.search_results" );
            displayMetadataSearchResults( req, out, list );
            return;
            }
            
            search_settings = SearchFacility.getSearchSettings( req );
            
	        String search_terms = (String)search_settings.get( "search_terms" );
	        if ( search_terms.length() < 1 )
	        {
	        	displayMetadataSearchResults( req, out, null );
                http_session.setAttribute( "org.bodington.servlet.search_results", new Vector() );
	        	return;
	        }
            
            StringTokenizer tok = new StringTokenizer( search_terms );
            
            if ( tok.countTokens() > 3 )
            {
                out.print("The maximum number of search terms that can be used is"
                    + " currently limited to 3.");
                http_session.setAttribute( "org.bodington.servlet.search_results", new Vector() );
                return;
            }
            
	        String[] fields = (String[])search_settings.get( "fields" );
	        String any_all_opt = (String)search_settings.get( "any_all_opt" );
	        String general_strength = (String)search_settings.get( "general_strength" );
	        int strength;
	        
	        XMLRepository rep = BuildingContext.getContext().getXMLRepository();
	        XMLQuery q_model = rep.getQueryInstance();
	        
	        if ( any_all_opt == null || any_all_opt.equals( "any" ) )
                q_model.combination = XMLQuery.COMBINE_UNION;
	        else
                q_model.combination = XMLQuery.COMBINE_INTERCEPT;
	        
	        if ( general_strength == null || general_strength.equalsIgnoreCase( "primary" ) )
	            strength = java.text.Collator.PRIMARY;
	        else if ( general_strength.equalsIgnoreCase( "secondary" ) )
	            strength = java.text.Collator.SECONDARY;
	        else if ( general_strength.equalsIgnoreCase( "tertiary" ) )
	            strength = java.text.Collator.TERTIARY;
	        else
	            strength = java.text.Collator.IDENTICAL;
	        
            q_model.word_strength = strength;
            q_model.title_criterion = "? = 'resource_id'";	//search only for metadata attached to resources
            q_model.relationship = XMLQuery.RELATE_CHILD;
	        
	        
	        while ( tok.hasMoreTokens() )
	        {
                q_model.words.addElement( tok.nextToken() );
	        }

	        XMLQuery q_top = rep.getQueryInstance();
	        q_top.combination = XMLQuery.COMBINE_UNION;
	        
	        
	        for (int i = 0; i < fields.length; i++)
	        {
                XMLQuery q_string = (XMLQuery)q_model.clone();
                
	            if ( fields[i].equalsIgnoreCase( "title") ||
	                            fields[i].equalsIgnoreCase( "description"))
	            {
	                buildGeneralXMLQuery( q_string, fields[i]);
	            }
	            else if ( fields[i].equalsIgnoreCase( "keywords") )
	            {
	                buildClassificationXMLQuery( q_string, fields[i]);
	            }
	            if ( fields.length == 1)
	            {
	                q_top = (XMLQuery)q_string.clone();
	                continue;
	            }
	            
	            q_top.addElement( q_string );
	        }
	        
	        list = nav_session.searchMetadata( q_top );
	        displayMetadataSearchResults(req, out, list );
	        http_session.setAttribute( "org.bodington.servlet.search_results", list );
	        
	    }
	    catch ( BuildingServerException bsex )
	    {
	        out.println( "<PRE>" );
	        out.println( bsex.getMessage() );
	        out.println( "</PRE>" );
	    }
	    
	    return;
	}
    
	private XMLQuery buildGeneralXMLQuery( XMLQuery q_string, String field )
	{
	    
	    XMLQuery q_langstring, q_field, q_general;
	    
	    q_langstring = metadatasearchChildComponent( "langstring" );
	    q_string.addElement( q_langstring );
	    q_field = metadatasearchChildComponent( field );
	    q_langstring.addElement( q_field );
	    q_general = metadatasearchGeneralComponent();
	    q_field.addElement( q_general );
	    
	    return q_string;
	}
	
	private XMLQuery buildClassificationXMLQuery( XMLQuery q_string, String field )
	{
	    
	    XMLQuery q_langstring, q_field, q_classification;
	    
	    q_langstring = metadatasearchChildComponent( "langstring" );
	    q_string.addElement( q_langstring );
	    q_field = metadatasearchChildComponent( field );
	    q_langstring.addElement( q_field );
	    q_classification = metadatasearchClassificationComponent();
	    q_field.addElement( q_classification );
	    
	    return q_string;
	}


    /**
     * Takes a list of XMLObjectRecords, and displays the Title as a link, and the
     * description of each one.
     * Display options are passed in as a Hashtable (hiding deleted resources, 
     * resources of a certain type, etc).<br />
     * Links produced for Headings, QuickLinks, TextBlocks and ImageBlocks are to the parent resource.<br />
     * Maximum of 100 records displayed.
     * @param req The web request.
     * @param out The PrintWriter to output the HTML to.
     * @param resultsList Vector of XMLObjectRecords matching query
     * @throws BuildingServerException
     */
    private void displayMetadataSearchResults(Request req, PrintWriter out, Vector resultsList)
    throws BuildingServerException
    {
        XMLObjectRecord record;
        ResourceTree tree;
        Resource resource;
        Hashtable displayOptions;
        List displayList, displayResourceTypes;
        String resourceType, name;
        boolean displayDeleted, displayDescription, displayKeywords;
        boolean viewable;
        
        if ( resultsList == null || resultsList.size()<1 )
        {
            out.println( "<p>No records were found.</p>" );
            if ( req.isAnonymous() )
                out.print( "<p>Searching after logging in may return more results.</p>" );
            return;
        }
        
        // extract display options:
        displayOptions = SearchFacility.getDisplayOptions( req );
        
        // contains the short names of any resource types that would normally be
        // excluded from the display of results, but that should be included:
        displayResourceTypes = (List)displayOptions.get( "displayResourceTypes" );
        
        // flags to control display of deleted resources, descriptions and keywords:
        displayDeleted = displayOptions.containsKey( "displayDeleted" );
        displayDescription = displayOptions.containsKey( "displayDescription" );
        displayKeywords = displayOptions.containsKey( "displayKeywords" );
            
        tree = ResourceTreeManager.getInstance();
        displayList = new Vector();
        
        // retrieve Resource objects for each reference in resultsList that satisfies 
        // the display requirements, and add it to the list of resources to be displayed.
        // NB: Limit of 101 resources set in BuildingSsessionImpl, but enforced here too, to 100:
        for ( int i=0; i<resultsList.size() && i<= 100; i++ )
        {
            record = (XMLObjectRecord)resultsList.elementAt( i );
        	resource = Resource.findResource( new PrimaryKey( record.getReference().intValue() ) );
            
        	if ( resource!=null && resource.checkPermission( Permission.SEE ) )
        	{
        	    if ( !displayDeleted && tree.isDeleted( resource ) )
        	        continue;
                
        	    if ( displayResourceTypes != null )
        	    {
        	        resourceType = Resource.getShortName( resource.getResourceType() );
        	        
                    // the following resource types are excluded by default,
                    // unless there is a request to include them:
        	        if ( resourceType == Resource.getShortName( Resource.RESOURCE_PERMISSION )
        	                        || resourceType == Resource.getShortName( Resource.RESOURCE_GROUPCONTAIN ))
        	        {
        	            
        	            if ( !displayResourceTypes.contains( resourceType ) )
        	            {
        	                continue;
        	            }
        	        }
        	    }
                
                displayList.add( resource );
        	}
        }

        if ( resultsList.size() != displayList.size() )
        {
            out.print( "<p>Some search results are hidden. This is either because you don't have access" );
            if ( req.isAnonymous() )
                out.print( ", are not logged in," );
            out.println( " or need to change the display options in Advanced Search.</p>" );
        }
        
        if ( resultsList.size()>100 )
        {
            out.println( "<p><strong>A total of more than one hundred resources were found." +
            "You should refine your search.</strong></p>");
            out.println(" Displaying "+ displayList.size() + " of more than 100 resources found.</p>" );
        }
        else
        out.println( "<p>Displaying "+ displayList.size()+ " of " + resultsList.size() + " resources found.</p>" );

                
        for ( int i=0; i<displayList.size(); i++ )
        {
            
            resource = (Resource)displayList.get( i );
            viewable = resource.checkPermission( Permission.VIEW );
            
            out.print( "<hr>" );
            out.print( "<h4>" );
            if ( viewable )
            {
                if ( resource.getResourceType() == Resource.RESOURCE_HEADING
                                || resource.getResourceType() == Resource.RESOURCE_QUICKLINK
                                || resource.getResourceType() == Resource.RESOURCE_TEXTBLOCK
                                || resource.getResourceType() == Resource.RESOURCE_IMAGEBLOCK )
                {
                    name = resource.getParent().getFullName();
                }
                
                else
                {
                    name = resource.getFullName();
                }
                out.print( "<a target=_top href=\"" );
                out.print( req.getContextPath() );
                out.print( req.getServletPath() );
                out.print( name );
                out.print( "\">" );
            }
            out.print( resource.getTitle() );
            if ( viewable )
                out.println( "</a>" );
            
            out.print( "</h4>" );
            
            if ( !viewable )
                out.println( "<p><i>You are not on the access list for this item.</i></p>" );
            
            if ( displayDescription )
            {
                out.print( "<p>" );
                out.print( resource.getDescription() );
                out.println( "</p>" );
            }
            
            if ( displayKeywords )
            {
                BuildingSession session = BuildingSessionManagerImpl.getSession( resource );
                try
                {
                    String[] keywords = session.getFieldValuesFromMetadata( "keywords" );
                    String string = XMLMetadataUtils.getKeywordStringFromArray( keywords );
                    if ( string.length() > 0 )
                    {
                        out.print( "<p>" );
                        out.print( "keywords: ");
                        out.print( string );
                        out.println( "</p>" );
                    }
                    
                }
                catch ( Exception e )
                {
                    log.error( "Problem getting keywords to display in search results." );
                    displayKeywords = false; // Avoid attempting this for rest of results in list
                }
            }
        }
    }

    /**
     * Display of keywords with links to execute search.
     * Method called from template to display current resource's keywords,
     *  with links to execute a keyword search for each of them.
     *  
     * @param req Request being processed by template
     * @param out PrintWriter to write HTML to interface
     */
    public void displayKeywordswithLinkToSearch ( Request req, PrintWriter out )
    {
        StringBuffer linkset = new StringBuffer();
        String link;
        String[] keywords;
        
        try
        {
            BuildingSession session = BuildingSessionManagerImpl.getSession( req.getResource() );
            keywords = session.getFieldValuesFromMetadata( "keywords" );
        }
        catch ( BuildingServerException e )
        {
            // do nothing, not even log...
            return;
        }
        
        if ( keywords.length == 0) out.print( "(none)" );
        
        for (int i = 0; i < keywords.length; i++)
        {
            link = "<a target=\"buildmain\" href=\"bs_template_searchresults.html?"
                + "field=keywords&displayKeywords=on&search=search&search_terms=" + keywords[i] + "\">"
                + keywords[i] + "</a>";
            if (i>0) linkset.append(", ");
            linkset.append( link );
        }
        
        out.print( linkset.toString() );
    }

    private XMLQuery metadatasearchChildComponent( String elementName )
	{
	    XMLRepository rep = BuildingContext.getContext().getXMLRepository();
	    XMLQuery query = rep.getQueryInstance();
	    query.element_name = elementName;
        query.relationship = XMLQuery.RELATE_CHILD;
        
        return query;
	}
	
	private XMLQuery metadatasearchGeneralComponent()
	{
	    XMLRepository rep = BuildingContext.getContext().getXMLRepository();
	    XMLQuery query = rep.getQueryInstance();
	    query.element_name = "general";
	    
	    return query;
	}
	
	private XMLQuery metadatasearchClassificationComponent()
	{
	    XMLRepository rep = BuildingContext.getContext().getXMLRepository();
	    XMLQuery query = rep.getQueryInstance();
	    query.element_name = "classification";
	    
	    return query;
	}
	
	private void importresource( Request req, PrintWriter out, String name )
		throws ServletException, IOException
		{
		if ( name.equals( "imspackage" ) )
			{
			importimspackage( req, out );
			return;
			}
			
		out.println( "<P>Unknown resource import option.</P>" );
		}
		
	private void importimspackage( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		String zipfile, str;
		boolean parentacl;
		BuildingSession session;

		String url=req.getParameter( "url" );
		

		zipfile = req.getParameterFileLocation( "file" );
		if ( zipfile==null || zipfile.length()<1 )
			{
			out.println( "No package was uploaded for import." );
			return;
			}

		str = req.getParameter( "parentacl" );
		parentacl = str!=null && str.equalsIgnoreCase( "yes" );

		
		try
			{
			session = BuildingSessionManagerImpl.getSession( req.getResource() );
			if ( session==null )
  				throw new BuildingServerException( "Unable to access the destination resource." );
			}
		catch ( Exception ex )
			{
			logException( out, "Facility", "importimspackage", 
			    "A technical problem occurred.",
			    ex );
			return;
			}


		        
		try
			{
			session.importImsPackage( url, parentacl, zipfile );
								
			out.println( "<br />Package import succeeded." );
			out.flush();
			}
		catch ( Exception ex )
			{
			logException( out, "Facility", "importimspackage", 
			    "A technical problem occurred.",
			    ex );
			return;
			}
		}
		
	private void zonecontrol( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		Zone z;
		PrimaryKey zid = req.getResource().getZoneId();
		Enumeration zones=null;

		try
			{
			zones = Zone.findZones( "zone_id IS NOT NULL" );
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "Facility", "zonecontrol", 
			    "Technical problem finding zone list.",
			    bsex );
			return;
			}
		
		out.println( "<SELECT NAME=zone_id>" );
		out.print( "<OPTION VALUE=\"null\"" );
		if ( zid == null )
			out.print( " SELECTED" );
		out.print( ">" );
		out.println( "Zone Inherited</OPTION>" );

		
		while ( zones.hasMoreElements() )
			{
			z = (Zone)zones.nextElement();
			out.print( "<OPTION VALUE=\"" );
			out.print( z.getZoneId().toString() );
			out.print( "\"" );
			if ( zid != null && zid.equals( z.getZoneId() ) )
				out.print( " SELECTED" );
			out.print( ">" );
			out.print( z.getName() );
			out.println( "</OPTION>" );
			}
			
		out.print( "</SELECT>" );
		}
		
	private void zoneconfirm( Request req, PrintWriter out )
		throws ServletException, IOException
		{
		PrimaryKey old = req.getResource().getZoneId();
		
		try
			{
			String str_zid = req.getParameter( "zone_id" );
			
			if ( str_zid == null || str_zid.length()==0 )
				return;
			
			if ( str_zid.equals( "null" ) )
				{
				req.getResource().setZoneId( null );
				req.getResource().save();
				out.println( "Specific zone cleared - zone will be inherited from container." );
				return;
				}

			
			PrimaryKey zid;
			Zone z;
			
			zid = new PrimaryKey( Integer.parseInt( str_zid ) );
			z = Zone.findZone( zid );
			
			if ( z== null )
				{
				out.println( "Unknown zone - changes not saved." );
				return;
				}
				
			req.getResource().setZoneId( zid );
			req.getResource().save();
			out.println( "Zone selection saved." );
			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "Facility", "zonecontrol", 
			    "Technical problem finding zone list.",
			    bsex );
			req.getResource().setZoneId( old );
			return;
			}
		}
		
	/**
	 * Returns a url with the current authenticated username appended to query
	 * uhi:awc
	 *
	 * @param req The Building HTTP request.
	 * @param loginUrl the url (with query)
	 * @return the current authenticated username appended to url query
	 */
    private String getUsernameLoginUrl(Request req, String loginUrl)
        {

        String username = getUsername(req);
        if (username == null)
            return loginUrl;

        int i = loginUrl.indexOf('=');
        int j = loginUrl.indexOf('?');

        if (i > 0 && j > 0 && i > j)
        {
            loginUrl = loginUrl.substring(0, i + 1);
            loginUrl += username;
        }
        else
        {
            log.info("Bad url query syntax");
        }

        return loginUrl;
        }

    /**
	 * Rturns the current authenticated username
	 * uhi:awc
	 *
	 * @param req The Building HTTP request.
	 * @return the current authenticated username or NULL if user is anonymous or has not been authenticated
	 */
    public String getUsername(Request req)
		{
        String msg;

        try {
            NavigationSession nav_session = req.getServerNavigationSession();
            if ( nav_session.isAnonymous() )
                return null;

            if (!nav_session.isAuthenticated() )
                msg = "Session is not authenticated - username unknown.";
            else
            {
                User user = (User)BuildingContext.getContext().getUser();
                if ( user==null || user.getUserId()==null )
                    msg = "User object or Userid is null - username not found.";
                else
                {
                    PassPhrase phrase = (PassPhrase)PersistentObject.findPersistentObject(
                            "user_id = " + user.getUserId(), "org.bodington.server.realm.PassPhrase" );
                    if (phrase == null || phrase.getUserName() == null)
                        msg ="PassPhrase object or UserName is null - username not found.";
                    else
                        return phrase.getUserName();
                }
            }
        } catch (Exception ex) {
            msg = "username not found: " + ex.toString();
            // just log exception
        }
         log.info(msg);

        return null;
        }

	private void generatemanifest(  Request req, PrintWriter out )
		throws IOException
		{
		BuildingSession session;

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

			session.generateImsManifest();
			out.println( "<P>Manifest generated OK.</P>" );
		    }
		catch ( BuildingServerException bsex )
		    {
		    out.println( "<P>An error occurred trying to generate the manifest.</P>" );
    		out.println( "<PRE>" );
    		out.println( bsex.getMessage() );
    		out.println( "</PRE>" );
		    }
		
		
		return;
		}


        public String getHttpSessionCount()
        {
            return Integer.toString( org.bodington.servlet.HttpSession.getSessionCount() );
        }
    public String getPreference( HttpServletRequest req, String key )
    {
        org.bodington.servlet.HttpSession http_session = 
            (org.bodington.servlet.HttpSession)req.getSession( false );
        if ( http_session == null )
            return null;
        return http_session.getPreference( key );
    }
    
    public String getAutomaticPreferenceByScreenSize( 
        Request req, 
        String key, 
        String small_default, 
        String big_default )
    {
        org.bodington.servlet.HttpSession http_session = 
            (org.bodington.servlet.HttpSession)req.getSession( false );
        if ( http_session == null )
            return null;
        return http_session.getAutomaticPreferenceByScreenSize( key, small_default, big_default );
    }
    
    
    /** This method is for development and test use since
     * it processes form data where user directly enters
     * string values for user preferences.
     * @param req The Bodington Servlet request from which
     * form data will be fetched.
     * @param key The name of the user preference.
     */    
    public void processPreference( org.bodington.servlet.Request req, String key )
	{
            org.bodington.servlet.HttpSession http_session;
            http_session = (org.bodington.servlet.HttpSession)req.getSession( false );
            if ( http_session == null )
                return;

            String value = req.getParameter( key );
            if ( value == null )
                return;

            http_session.setPreference( key, value );
        }
       
        public Locale getLocale( Request req )
        {
            ResourceBundleHelper resource_bundle_helper;
            resource_bundle_helper = 
             ResourceBundleHelper.getResourceBundleHelper( "", req.getHeader( "Accept-Language" ) );
            if ( resource_bundle_helper == null )
                return null;
            return resource_bundle_helper.getTopLocale();
        }
    
	/**
	 * Returns a localised string from a resource bundle according to language
         * preferences determined from the HTTP request headers.
         */
        public String getLocalisedString( Request req, String properties_name, String id )
        {
            ResourceBundleHelper resource_bundle_helper;
            resource_bundle_helper = 
             ResourceBundleHelper.getResourceBundleHelper( properties_name, req.getHeader( "Accept-Language" ) );
            if ( resource_bundle_helper == null )
                return null;
            return resource_bundle_helper.getString( id );
        }

        /**
	 * writes a localised string from a resource bundle according to language
         * preferences determined from the HTTP request headers.
         */
        public void writeLocalisedString( Request req, PrintWriter writer, String properties_name, String id )
        {
            String str = getLocalisedString( req, properties_name, id );
            if ( str != null )
                writer.print( str );
        }
        
        	
        /**
         * Outputs a Javascript &lt;script&gt; tag to import utils.js no 
         * matter how long the URL is.
         * @param request The Request, needed to work out the context path.
         * @param out A PrintWriter to which we output the tag.
         */
        	public void javascript ( HttpServletRequest request, PrintWriter out)
        	{
                String editor = getPreference(request, "preference.editor");
                javascriptTag(out, request.getContextPath()+"/static/calendar/calendar.js");
                javascriptTag(out, request.getContextPath()+"/static/calendar/calendar-en.js");
        		javascriptTag(out, request.getContextPath()+"/static/utils.js");
                
                // Pull in editor
                if ("none".equals(editor))
                {
                    javascriptTag(out, request.getContextPath()+"/static/none/editor.js");
                }
                else if ("rtwedit".equals(editor))
                {
                    javascriptTag(out, request.getContextPath()+"/static/rtwedit/editor.js");
                    javascriptTag(out, request.getContextPath()+"/static/rtwedit/rtwedit.js");
                }
                else
                {
                    String path = "";
                    try
                    {
                        path = ((Resource)request.getAttribute(Request.RESOURCE_ATTRIBUTE)).getFullName();
                    }
                    catch (BuildingServerException e)
                    {
                        log.warn("Failed to get full path.", e);
                    }
                    out.print("<script type=\"text/javascript\">");
                    out.print("fckeditorBase='"+request.getContextPath()+"/static';");
                    out.print("resourceUrl='"+ path + "';");
                    out.print("</script>\n");
                    javascriptTag(out, request.getContextPath()+"/static/fckeditor/editor.js");
                    javascriptTag(out, request.getContextPath()+"/static/fckeditor/fckeditor.js");
                }
        	}
            
            private static void javascriptTag(PrintWriter out, String file) 
            {
                out.write("<script type=\"text/javascript\" src='");
                out.write(file);
                out.write("'></script>\n");
            }

        	/**
        	 * Facade to the method in StyleSheetUtils.
        	 * @see StyleSheets#getSessionDataPreference(Request, String)
        	 */
        	public String getSessionDataPreference( Request req, String key )
        	{
        	    return styleSheet.getSessionDataPreference(req, key);
        	}
        	
        	/**
        	 * Facade to the method in StyleSheetUtils.
        	 * @see StyleSheets#processStylesheetPreferences(Request)
        	 */
        	public void processStylesheetPreferences(Request req)
        	{
        	    styleSheet.processStylesheetPreferences(req);
        	}
        	
        	/**
        	 * Facade to the method in StyleSheetUtils.
        	 * @see StyleSheets#stylesheetField(Request, PrintWriter, String)
        	 */
        	public void stylesheetField(Request req, PrintWriter writer, String name)
        	throws IOException, ServletException
        	{
        	    styleSheet.stylesheetField(req, writer, name);
        	}
        	
        	/**
        	 * Facade to the outputting of the ACL display.
        	 * @see AclDisplayFacility#outputAclTable(PrintWriter, Request)
        	 */
        	public void outputAclTable(PrintWriter out, Request request) throws ServletException
        	{
        	    AclDisplayFacility.outputAclTable(out, request);
        	}
            
        	public void addAclPermissions(PrintWriter out, Request request) throws ServletException
        	{
        	    try
        	    {
                    String groupName = request.getParameter("group_name");
                    if (groupName == null)
                        return;
                    Resource resource = request.getResource();
                    if (!resource.checkPermission(Permission.MANAGE))
                    {
                        out.println("You don't have manage permission");
                        return;
                    }
                    Acl acl = resource.getAcl();
                    Group group = Group.findGroupByName(groupName );
                    AclEntry aclEntry = AclEntry.findAclEntry(group.getPrimaryKey(), acl.getPrimaryKey());
                    if (aclEntry == null)
                    {
                        aclEntry=new AclEntry();
                        aclEntry.setPrincipal( group );
                        acl.addEntry( aclEntry );
                    }
                    String ability = request.getParameter("ability");
                    if (("look").equals(ability))
                    {
                        if (aclEntry.addPermission(Permission.VIEW) | aclEntry
                                        .addPermission(Permission.SEE))
                        {
                            Utils.writeMessage("Access added.", out);
                        }
                        else
                        {
                            Utils.writeError("Access was already granted", out);
                        }
                    }
                    else if (("change").equals(ability))
                    {
                        if (aclEntry.addPermission(Permission.VIEW)
                                        | aclEntry.addPermission(Permission.SEE) | aclEntry
                                        .addPermission(Permission.EDIT))
                        {
                            Utils.writeMessage("Access added.", out);
                        }
                        else
                        {
                            Utils.writeError("Access was already granted", out);
                        }
                    }
                    else
                    {
                        Utils.writeError("Bad Ability", out);
                    }
                    acl.save();
        	    }
                catch ( Exception e )
                {
                    Utils.writeError(e.getMessage(), out);
                    log.error(e);
                }
        	}
        	
            /**
             * Write out a list of resources it is possible to create for the current resource.
             * @param out The PrintWriter to output the HTML to.
             * @param request The request in which we find the current resource.
             * @param admin If set to <code>true</code> then output admin resources,
             * otherwise output the user resources.
             */
        	public void writeCreateMenu(PrintWriter out, Request request, final boolean admin)
        	{
                // Iterator that filters on isSysadmin() and isCreate()
                Iterator it = new Iterator()
                {
                    Facility next;
                    Iterator internal = contains.iterator();

                    public boolean hasNext()
                    {
                        loadNext();
                        return next != null;
                    }

                    public Object next()
                    {
                        loadNext();
                        Facility copy = next;
                        next = null;
                        return copy;
                    }
                    
                    private void loadNext()
                    {
                        while (next == null && internal.hasNext())
                        {
                            Facility test = (Facility)internal.next();
                            if (test.isCreate() && test.isSysadmin() == admin)
                            {
                                next = test;
                            }
                        }
                    }

                    public void remove()
                    {
                        throw new UnsupportedOperationException();
                    }
                    
                };
                
        		if (it.hasNext())
        		{
                    while(it.hasNext())
        		    {
            		        Facility facility = (Facility)it.next();
                            String name = facility.getName();
                            
                            out.println("<!-- " + name + " -->");
                            out.println("<div id=\"" + name + "ExpanderIcon\" class=\"ExpanderIconClosed\" " +
                                    "onclick=\"changeHelpBox('"+ name + "')\">&nbsp;</div>" );
                            if (facility.isSpringCreate())
                            {
                                out.print("<a href=\"bs_spring"+ "_create_"
                                + facility.facilityname + "\" title=\"Create "+ name +"\">");
                            } 
                            else
                            {
                                out.print("<a href=\"bs_template"+ "_create"
                                    + facility.facilityname + ".html\" title=\"Create "+ name +"\">");
                            }
                            out.print(name);
                            out.print("</a>");
                            out.println("<br />");
                            out.println("<div id=\"" + name + "\" class=\"helpBox\">");        		        
            		        out.print(facility.getDescription());
            		        out.println("</div>");
        		    }
        		}
        		else
        		{
        		    out.println("<ul><li>No resources found.</li></ul>");
        		}
        	}
        	
        	/**
        	 * Should the creation be handled by Spring?
        	 * @return <code>true</code> if the creation template for this Facility 
        	 * is a Spring one.
        	 */
            public boolean isSpringCreate() {				
				return false;
			}


			/**
             * Tracks this request as a NavigationEvent. This shouldn't be called
             * by all templates. Currently top.html uses it.
             * @param request The current request.
             */
            public void track (Request request)
            {
                if (("no").equalsIgnoreCase(BuildingContext.getProperty(
                    "buildingservlet.tracking", "yes")))
                    return;
                Resource resource = request.getResource();
                // Don't track users of the questionnaire tool as it is anonymous.
                if (resource.getClass().equals(Questionnaire.class))
                    return;
                HttpSession session = (HttpSession)request.getSession();
                PrimaryKey last = (PrimaryKey)session.getAttribute(Request.RESOURCE_ATTRIBUTE);
                PrimaryKey current = resource.getPrimaryKey();
                if (! current.equals(last))
                {
                    NavigationEvent event = new NavigationEvent(
                        (resource.checkPermission(Permission.VIEW)) 
                                        ? NavigationEvent.EVENT_RESOURCE_ACCESS
                                        : NavigationEvent.EVENT_FAILED_ACCESS,
                                        current,request.getUserId(), "");
                    event.save();
                    session.setAttribute(Request.RESOURCE_ATTRIBUTE, current);
                }
            }
        		 
            
            /**
             * Wrapper to allow resource owner to be displayed from a template.
             * Show the owners of the resource. This methods has quite an
             * overhead as it needs to query the database layer twice. It only 
             * ever displays the first 2 members of the owners group.
             * @param req The request which contains the resource.
             * @param out The output to write the HTML to.
             */
            public void resourceOwner ( Request req, PrintWriter out )
            {
               Resource res = req.getResource();       	    
                String id = res.getResourceId().toString();
        	    try
        	    {
                    if (req.isAnonymous())
                    {
                        return;
                    }
                    StringBuffer output = new StringBuffer();
                    boolean moreThanOne = false;
            	    Group group = Group.findGroupByName("localgroup."+ id+".owners");
            	    Enumeration members = group.members();
                    if(members.hasMoreElements())
                	    output.append(((User)members.nextElement()).getName());
                    else
                        output.append("None");
                    if(members.hasMoreElements())
                    {
                        moreThanOne = true;
                        output.append(", ");
                        output.append(((User)members.nextElement()).getName());     
                	}
                    if(members.hasMoreElements())
                    {
                        output.append(" and ");
                        output.append("<a href='bs_template_accessgroup.html?group_id=");
                        output.append(group.getPrimaryKey());
                        output.append("' target='groupwindow' onclick='grpwin()'>");
                        output.append("others");
                        output.append("</a>");
                    }
                    out.print((moreThanOne)?"Owners: ":"Owner: ");
                    out.print(output.toString());
        	    }
        	    catch ( BuildingServerException bsex )
    		    {
        	        log.warn("Problem finding owners of: "+ id, bsex);
    		    }
            }
            
            public void allowedTags( Request request, PrintWriter out )
            {
                String field = request.getInsertName();
                if (("description").equalsIgnoreCase(field))
                {
                    EscapedHtmlWriter htmlOut = new EscapedHtmlWriter(out);
                    Collection elements = HtmlFilterFactory
                        .getValues("htmlfilter.fragment.elements");
                    Iterator elementIt = elements.iterator();
                    if (elementIt.hasNext())
                    {
                        out.print("<br/>");
                        htmlOut.print("Allowed HTML tags: ");
                        htmlOut.print("<"+ elementIt.next()+ ">");
                        while (elementIt.hasNext())
                            htmlOut.print(", <"+ elementIt.next()+ ">");
                    }
                }
            }
            
                        
            public void homeContainer( Request request, PrintWriter out )
            {
                try
                {
            		User user = request.getServerNavigationSession().getAuthenticatedUser();
            		HomeContainerSession homeConatiner = request.getServerNavigationSession().getHomeContainerSession();
                    if (homeConatiner == null)
                        return;
            		BodingtonURL url = new BodingtonURL(request);
            		if (homeConatiner.hasHome(user))
            		{
            			// Already has home.
            			Resource home = homeConatiner.findHome(user);
            			out.print("<a href=\"");
            			out.print(url.getResourceUrl(home));
            			out.print("\" target=\"_top\">MyWebLearn</a> ");
                    String bookmark = request.getParameter("bookmark");
                    if (bookmark != null)
                    {
                        HomeSession homeSession = (HomeSession)BuildingSessionManagerImpl.getSession(home);
                        homeSession.createBookmark(request.getResource());
                        out.println("<script>message = \"Resource Bookmarked\";</script>");
                    }
                    out.print("(<a href=\"bs_template_top.html?bookmark=yes\">");
                    out.print("Bookmark");
                    out.println("</a>)");
            		}
            		else
            		{
            			if (homeConatiner.canCreateHome(user))
            			{
            				out.print("<a href=\"");
                        out.print(url.getResourceUrl(homeConatiner.getResourceId(), false));
                        out.print("\" target=\"_top\">MyWebLearn Signup</a>");
            			}
            		}
                }
                catch (BuildingServerException bse)
                {
                    log.error("Problem with HomeContainer", bse);
                }
            }
            
            private static Group publicGroups[] = null;
            
            /**
             * Check is the current resource is VIEWable by the public.
             * @param request The current request from which we get the resource.
             * @return True if the resource is viewable by the public.
             */
            public boolean isPublic ( Request request )
            {
                return isPublic( request.getResource() );
            }
            
            public boolean isPublic ( Resource resource )
            {
                try
                {
                    return ResourceUtils.anyPrincipal( resource, getPublicGroups(), Permission.VIEW );
                }
                catch (BuildingServerException bse)
                {
                    log.warn( "Failed to discover if resource is public." );
                }
                return false;
                
            }
            
            /**
             * Get groups that are used in displaying access summary. Currently
             * these are hardcoded but maybe from a property in the future?
             * @return An array of groups which permissions can be tested against.
             */
            public static synchronized Group[] getPublicGroups() throws BuildingServerException
            {
                if (publicGroups == null)
                {
                    String accessNames[] = {"public", "anonymous"};
                    Vector publicGroupsVec = new Vector();
                    for (int i = 0; i < accessNames.length; i++)
                    {
                        Group group = Group.findGroupByName(accessNames[i]);
                        if ( group == null )
                            log.error("Failed to find core group: "+ accessNames[i]);
                        else
                            publicGroupsVec.add(group);
                    }
                    publicGroups = new Group[publicGroupsVec.size()];
                    publicGroupsVec.toArray( publicGroups );
                }
                return publicGroups;
            }
            
            public void outputDate(PrintWriter out)
            {
                out.print(DateFormatter.formatDate(new java.util.Date(), DateFormatter.SHORT));
            }
            
            /** 
             * Method to allow templates to send errors. Should be called in the
             * preperation method. This method only exists because the template
             * language doesn't support ints.
             * @param response The servlet response to set the header on.
             * @param error The error string.
             */
            public void setStatus(Response response, String error) throws NumberFormatException, IOException
            {
                response.setStatus(Integer.parseInt(error));
            }
     
            /**
             * Get the name of this facility.
             * @return The name of resources managed by this facility. Eg Suite.
             */
            public String getName()
            {
                return bundle.getString("name");
            }
            
            /**
             * Get the descriptions of this facility.
             * @return The description of resources managed by this facility.
             * Eg. Container for other resources.
             */
            public String getDescription()
            {
                return bundle.getString("description");
            }
            
            /**
             * Is this facility only creatable by sysadmin.
             * @return <code>true</code> if only sysadmin can create this tool.
             */
            public boolean isSysadmin()
            {
                return false;
            }

            /**
             * Is this facililty creatable. A facility might still be supported
             * for existing resource but no new resources should be created.
             * @return <code>true</code> if this facility can no longer be created.
             */
            public boolean isCreate()
            {
                return true;
            }


            /**
             * Look for a date in the request and attempt to convert it into a Timestamp.
             * Only add an error if the field is defined and we couldn't convert to a date. 
             * @param breq The HttpServletRequest to look in.
             * @param errors The List to add the errors to.
             * @param param The request parameter to pull the date from.
             * @param field The field in the HTML to say the error was against.
             * @return A Timestamp or <code>null</code> if no data was found or the 
             * string couldn't be converted into a date.
             */
            protected Timestamp parseDate(HttpServletRequest breq, List errors, String param, String field)
            {
                Timestamp timestamp = null;
                String dateString = breq.getParameter(param);
                if (dateString != null && dateString.length() > 0)
                {
                    Date date = DateParser.parse(dateString);
                    if (date == null) {
                        errors.add("The value: " + '\"' + dateString 
                            + "\" for \'" + field + "\' is not a valid date.");
                    } 
                    else 
                    {
                        timestamp = new Timestamp(date.getTime());
                    }
                }
                return timestamp;
            }
}
