/*
ESUP-portail is a french academic project developed under the GPL (General Public License) augmented according to the following :
A binary or source file developped by ESUP-portail can be used or compiled with products under Apache license.
The official english text of the GPL can be found here : http://www.gnu.org/licenses/gpl.html .
A non official french translation can be found here : http://www.linux-France.org/article/these/gpl.html .
The different kinds of licenses governing the products developed by the Apache foundation can be found here : http://www.apache.org/licenses .
It follows that you can as well as use download contents as well modify and redistribute them provided you respect the GPL terms.
Downloading and using such contents do not provide any guaranty.
Be sure that you have well understood the terms of the license before using the contents it covers.
The ESUP-portail distribution includes the following distributions :
    * UPortal : 
      software developed by JA-SIG (Java Architecture - Special Interest Group)
      You can find the license page here : http://mis105.udel.edu/ja-sig/uportal/license.html
    * CAS :
      SSO solution developed by Yale University
      You can find the project page here : http://www.yale.edu/tp/auth
    * Cocoon :
      XML framework distributed by the Apache foundation under Apache license;
      Please find the full text here : http://cocoon.apache.org/2.1/license.html
    * Mod_dav:
      A DAV module for Apache web server
      You can find the project page here : http://www.webdav.org/mod_dav
    * IMP :
      webmail from Horde application framework
      You can find the project page here : http://www.horde.org
    * …. To be completed
*/
 
package org.esupportail.portal.channels.CStockage;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.esupportail.portal.channels.CStockage.channelAction.AbstractDefaultRenderXml;
import org.esupportail.portal.channels.CStockage.channelAction.BufferAction;
import org.esupportail.portal.channels.CStockage.channelAction.Constants;
import org.esupportail.portal.channels.CStockage.config.AuthorizationSpaceParameter;
import org.esupportail.portal.channels.CStockage.config.ChannelConfiguration;
import org.esupportail.portal.channels.CStockage.config.Space;
import org.esupportail.portal.channels.CStockage.exception.AnyVisibleSpaceException;
import org.esupportail.portal.channels.CStockage.exception.ApplicationException;
import org.esupportail.portal.channels.CStockage.exception.BadConnexionParameters;
import org.esupportail.portal.channels.CStockage.exception.CancelException;
import org.esupportail.portal.channels.CStockage.exception.ChannelException;
import org.esupportail.portal.channels.CStockage.exception.ConnexionParametersRequired;
import org.esupportail.portal.channels.CStockage.exception.DataBaseException;
import org.esupportail.portal.channels.CStockage.exception.DownloadException;
import org.esupportail.portal.channels.CStockage.exception.PropertiesException;
import org.esupportail.portal.channels.CStockage.exception.ServerException;
import org.esupportail.portal.channels.CStockage.provider.ChannelResource;
import org.esupportail.portal.channels.CStockage.spacesPersonalization.PersonalizationTool;
import org.esupportail.portal.channels.CStockage.spacesPersonalization.target.UserForPersonalization;
import org.esupportail.portal.webservices.groups.PortalGroups;
import org.jasig.portal.ChannelCacheKey;
import org.jasig.portal.ChannelRuntimeData;
import org.jasig.portal.ChannelRuntimeProperties;
import org.jasig.portal.ChannelStaticData;
import org.jasig.portal.ICacheable;
import org.jasig.portal.IChannel;
import org.jasig.portal.IMimeResponse;
import org.jasig.portal.PortalEvent;
import org.jasig.portal.PortalException;
import org.jasig.portal.UPFileSpec;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.utils.XSLT;
import org.xml.sax.ContentHandler;

/**
 * Id: CStockage.java,v 1.0 7 avr. 2004<br/>
 * Copyright (c) 2004 Esup Portail (www.esup-portail.org)<br/>
 * Classes: CStockage<br/>
 * Original Author: Yohan Colmant<br/>
 * Channel used to manage personal files on a webdav server through the portal<br/>
 */



public class CStockage implements IChannel, IMimeResponse ,ICacheable  {


	/**
	 * Logger object
	 */
	protected static final Log log = LogFactory.getLog(CStockage.class);
	
	
	
	
	private ChannelStaticData staticData;
	private ChannelRuntimeData runtimeData;
	
	private String fileId;
	//private PortalControlStructures pcs;


  
	
	/**
	 * The spaces availables
	 */
	private ArrayList spaces;
	
	/**
	 * The current used space
	 */
	private Space currentSpace;
	

	/**
	 * if we support the spaces personalization
	 */
	private boolean isSpacesPersonalization;
	
	
	
	
	/**
	 * Error at the beginning of the channel
	 */
	private ChannelException setStaticDataException;		
	
	
	/**
	 * The stylesheet used for the transformation
	 */
	private StringBuffer stylesheet;
	
	
	
	/**
	 * User login
	 */
	private String portalLogin;
	
	/**
	 * The user groups
	 */
	private Vector userGroups;
	
	
	/**
	 * The mode to use
	 */
	private String currentMode;
	
	
	/**
	 * Object used to keep the objects used between 2 actions, for example, the clipboard
	 */
	private BufferAction buffer;
	
	
	/**
	 * The current user IPerson object
	 */
	private IPerson person;
	
	/**
	 * If we want help
	 */
	//private boolean helpMode= false;
	
	
	
	/**
	 * Constructor
	 */
	public CStockage() {				
		
		// init the buffer
		this.buffer = new BufferAction();
	}	

	/** 
	 *  Process layout-level events coming from the portal.
	 *  Satisfies implementation of IChannel Interface.
	 *
	 *  @param <b>PortalEvent</b> ev a portal layout event
	 */
	public void receiveEvent(PortalEvent ev) {
		if (ev.getEventNumber() == PortalEvent.HELP_BUTTON_EVENT) {
			//helpMode = true;
		}
		else {
			//helpMode = false;
		}
	}

	  /** 
	   *  Receive channel runtime data from the portal.
	   *  Satisfies implementation of IChannel Interface.
	   *
	   *  @param <b>ChannelRuntimeData</b> rd handle to channel runtime data
	   */
	  public void setRuntimeData(ChannelRuntimeData rd) {
	  	this.runtimeData = rd;
	  }
	  
	  
	  /** 
	   *  Returns channel runtime properties.
	   *  Satisfies implementation of Channel Interface.
	   *
	   *  @return handle to runtime properties 
	   */ 
	  public ChannelRuntimeProperties getRuntimeProperties() { 
	  	return new ChannelRuntimeProperties();
	  }
	
	/**
	 * Receive static channel data from the portal. Satisfies implementation of IChannel Interface.
	 * 
	 * @param sd <b>ChannelStaticData</b> static channel data
	 */
	public void setStaticData(ChannelStaticData sd) {
		
		this.staticData = sd;			
		 
		try {
			ChannelConfiguration config = ChannelConfiguration.getInstance();
			
			// init the value of directories for servant
			staticData.setParameter("serverView", config.getDirectoriesList());
			
			// the uportal person object
			person = sd.getPerson();			
			
			// the usernameAttribute
			String userNameAttribute = config.getUsernameAttribute();
			
			// Set the user login			
			portalLogin = (String)person.getAttribute(userNameAttribute);	
			
			// the user groups
			try {
				Vector groups = (Vector)new PortalGroups().getGroupsOfUser(portalLogin);
				userGroups = new Vector(groups.size());
				for (int i=0; i<groups.size(); i++) {
					Vector group = (Vector)groups.elementAt(i);
					userGroups.addElement((String)group.elementAt(group.size()-1));
				}
				//System.out.println(userGroups);
			}
			catch(Exception e) {
				throw new ServerException();
			}
			
			// if we support the spaces personalization
			isSpacesPersonalization = ChannelConfiguration.getInstance().isSpacesPersonalization();
			
			
			// the spaces availables in the configuration file
			spaces = config.getDefaultSpaces();
			
			// add the obliged spaces from the database
			if (isSpacesPersonalization)
				addObligedSpacesToList(portalLogin, userGroups, spaces);
			
			
			
			// for each Space, we check if the path is in the {...} form. Is yes, we replace {...} by the real value									
			for (int i=0; i<spaces.size(); i++) {
				
				Space space = (Space)spaces.get(i);
				boolean valid = initSpace(space, person, portalLogin, spaces, buffer, userGroups);
				
				if (!valid) {
					spaces.remove(i);
					i--;
				}
				
			}				

			
			
			// init the personal spaces into the current menu
			if (isSpacesPersonalization)
				initPersonalSpacesIntoMenu();
			
			
			
			
			// if we can not see any space
			if (spaces.isEmpty()) {
				if (log.isDebugEnabled()){
					log.debug("setStaticData"+" :: AnyVisibleSpaceException");
				}
				
				throw new AnyVisibleSpaceException();
			}
			
			else {
				// the current space
				this.currentSpace = (Space)spaces.get(0);									
			}

		}		
		catch(AnyVisibleSpaceException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}			
		}
		catch(ServerException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(ApplicationException e) {			
			//this.currentSpace.setWebdavAccessObject(null);			
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(PropertiesException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(ChannelException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		
	}
	
	
	
	/**
	 * Init the actions and server of a space
	 * @param space the space to init
	 * @return true if the access is valid, false else
	 * @throws PropertiesException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws ApplicationException
	 * @throws MalformedURLException
	 * @throws ChannelException
	 */
	private static boolean initSpace(Space space, IPerson person, String portalLogin, ArrayList spaces, BufferAction buffer, Vector userGroups) throws PropertiesException, ApplicationException {
		//System.out.println("\nj'initSpace "+space.getLabel());

		// log
		if (log.isDebugEnabled()) {
			log.debug("initSpace"+" :: init = "+space.getLabel());
		}
		
		try {
			
			//System.out.println("\n\n"+space.getLabel());
			
			// if the server access is valid
			boolean validAccess = false;
			
			// check the authorization
			ArrayList authorizations = space.getAuthorization();
			if (authorizations!=null && !authorizations.isEmpty()) {
				
				for (int j=0; !validAccess && j<authorizations.size(); j++) {
					AuthorizationSpaceParameter auth = (AuthorizationSpaceParameter)authorizations.get(j);
					String attribute = auth.getAttribute();
					String value = auth.getValue();
					
					if (attribute!=null && !attribute.trim().equals("")) {
						Object[] userValues = (Object[])person.getAttributeValues(attribute);						
						if (userValues!=null) {
						
							for (int k=0; !validAccess && k<userValues.length; k++) {
							
								String userValue = (String)userValues[k];
								if (userValue.toLowerCase().equals(value.toLowerCase())) {
									validAccess = true;
									log.debug("initSpace"+" :: "+attribute+" == "+value+" ??? yes");
								}
								else log.debug("initSpace"+" :: "+attribute+" == "+value+" ??? no, "+attribute+" = "+userValue);
							}							
						
						}
						else log.debug("initSpace"+" :: "+attribute+" == "+value+" ??? no, "+attribute+" is null ");
					}
				}					
			}
			else {
				validAccess = true;
			}
			
			//System.out.println("validAccess 1 "+validAccess);

			// log
			if (log.isDebugEnabled()) {
				log.debug("initSpace"+" :: validAccess 1 "+validAccess);
			}
			
			// if authorized
			if (validAccess) {
				
				validAccess = false;
			
				// cas authentication mode
				if (space.isCasAuthentication()) {
				
					//TODO validAccess = space.setWebdavAccessObject(davAccessClass, uri, trustedLogin, password);
				}
			
				// trusted or asked authentication mode
				else if (space.isTrustedAuthentication() || space.isAskedAuthentication()) {
				
					// if there is a trusted login
					String login = space.getLogin();
					String accessLogin = null;
					if (login==null || login.equals("")) {
						accessLogin = portalLogin;
						space.setLogin(accessLogin);
					}
					else accessLogin = login;
				
					// check the {...}
					space.checkPath(person);
					if (!space.isAskedAuthentication()) {
						space.clearRegexp();
					}
	
					// check the {...} in the checked attributes
					ArrayList checkedAttributes = ChannelConfiguration.getInstance().getCheckedAttributes();
					for (int j=0; j<checkedAttributes.size(); j++) {							
						space.checkAttribute((String)checkedAttributes.get(j), person);							
					}
					
					//System.out.println("avant setServerAccessObject");

					// log
					if (log.isDebugEnabled()) {
						log.debug("initSpace"+" :: before setServerAccessObject");
					}
					
					// we configure the object used by the space to acces the server
					validAccess = space.setServerAccessObject();

					// log
					if (log.isDebugEnabled()) {
						log.debug("initSpace"+" :: after setServerAccessObject");
					}
					//System.out.println("apres setServerAccessObject");
				}
				
			}		
			
			//System.out.println("validAccess 2 "+validAccess);

			// log
			if (log.isDebugEnabled()) {
				log.debug("initSpace"+" :: validAccess 2 "+validAccess);
			}
			
			// we check if this space if valid or not
			if (!validAccess) {
				if (log.isDebugEnabled()){
					log.debug("initSpace"+" :: "+portalLogin+" can not see "+space.getKey());
				}
				
				if (!space.isPersonnalSpace()) 
					return false;
			}
			
			else {
				// set for the space, the channel action object used
				//System.out.println("avant setChannelActionObject");

				// log
				if (log.isDebugEnabled()) {
					log.debug("initSpace"+" :: before setChannelActionObject");
				}
				
				boolean wellDone = space.setChannelActionObject(person, spaces, buffer, portalLogin, userGroups);
				//System.out.println("apres setChannelActionObject");

				// log
				if (log.isDebugEnabled()) {
					log.debug("initSpace"+" :: after setChannelActionObject");
				}
				
				//System.out.println("wellDone "+wellDone);

				// log
				if (log.isDebugEnabled()) {
					log.debug("initSpace"+" :: wellDone "+wellDone);
				}
				
				if (!wellDone && !space.isPersonnalSpace()) {
					return false;
				}
			}
			
			return true;
		}
		catch(ClassNotFoundException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			PropertiesException ex = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("initSpace"+" :: "+e);
			}
			throw ex;
		}
		catch(IllegalAccessException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			PropertiesException ex = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("initSpace"+" :: "+e);
			}
			throw ex;
		}
		catch(InstantiationException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			PropertiesException ex = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("initSpace"+" :: "+e);
			}
			throw ex;
		}
		catch(MalformedURLException e) {			
			//this.currentSpace.setWebdavAccessObject(null);	
			PropertiesException ex = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("initSpace"+" :: "+e);
			}
			throw ex;
		}
	}
	
		
	
	/**
	 * Output channel content to the portal
	 * 
	 * @param out a sax document handler
	 */
	public void renderXML(ContentHandler out) throws PortalException {
		
		// Create a new XSLT styling engine		
		XSLT xslt = new XSLT(this);
		
		// if we click on help
		/*if (helpMode) {
			String xmlFilePath = "/docbook/CStockage/xml/utilisation.xml";
			
			String mediaOriginal = "media";
			String mediaFinal = "media/org/esupportail/portal/channels/CStockage/docbook";
			
			URL url = ChannelConfiguration.class.getResource(xmlFilePath);
			File xmlFile = new File(url.getFile());
			
		    // pass the result XML to the styling engine.
		    xslt.setXML(xmlFile);
		    
		    // specify the stylesheet selector
		    xslt.setXSL("/docbook/CStockage/xsl/docbookEsup.xsl");
		    xslt.setStylesheetParameter("media.original", mediaOriginal);
		    xslt.setStylesheetParameter("media.final", mediaFinal);
		    
		    // set the output Handler for the output.
		    xslt.setTarget(out);
		    
		    // do the deed
		    xslt.transform();
			
		    helpMode = false;
		    
			return ;
		}*/
		
		
		
		// if we change the space
		String[] spaceChanges = runtimeData.getParameterValues("space");
		if (spaceChanges != null){
			String oldSpaceKey = this.currentSpace.getKey();
			String newSpaceKey = null;
			boolean findChangement = false;
			for (int i = 0; i<spaceChanges.length && !findChangement;i++){
				newSpaceKey = spaceChanges[i];
				if (!newSpaceKey.equals(oldSpaceKey))
					findChangement = true;
			}
			
			// if found, we look for the good space into the ArrayList
			if (findChangement) {
				
				boolean findGoodSpace = false;
				Iterator iter = spaces.iterator();
				while (iter.hasNext() && !findGoodSpace) {
					Space s = (Space)iter.next();
					if (s.getKey().equals(newSpaceKey)) {
						findGoodSpace = true;
						this.currentSpace = s;
					}
						
				}
				
			}
		}
		

		
		
		//////////////////////////////////////////////////////
		// S P A C E S   P E R S O N A L I Z A T I O N
		// Manage the shared spaces 
		currentMode = null;
		StringBuffer xml = null;
		String[] mod=runtimeData.getParameterValues("modeDav");
		if (mod != null){
			for (int i = 0; i<mod.length;i++){
				if (!mod[i].equals("")) {
					currentMode = mod[i];
				}
			}
		}
		
		try {
			
			if (currentMode!=null && currentMode.equals(Constants.MANAGE_SHARED_SPACES)) {
				try {
					stylesheet = new StringBuffer("manageSharedSpaces");
					xml = renderXmlManageSharedSpaces(runtimeData);
				}
				catch(ChannelException e) {
					stylesheet = new StringBuffer("CStockage");
					
					if (e instanceof ConnexionParametersRequired) {
						throw (ConnexionParametersRequired)e;
					}
					if (e instanceof BadConnexionParameters) {
						throw (BadConnexionParameters)e;
					}
					
					if (this.currentSpace!=null) {
						xml = this.currentSpace.getChannelActionObject().renderXmlChannelException(e);
					}
					else {
						xml = AbstractDefaultRenderXml.getXmlError(e, -1);
					}
						
					// log
					if (log.isDebugEnabled()){
						log.debug("renderXML"+" :: "+e);
					}
				}
				
				xsltTransformation(xslt, xml, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
				return;
			}
			
			
			// if no space authorized
			else {
				if (this.currentSpace == null) {
					stylesheet = new StringBuffer("CStockage");
					if (setStaticDataException==null) {
						xml = AbstractDefaultRenderXml.getXmlError(new AnyVisibleSpaceException(), -1);
					}
					else {
						xml = AbstractDefaultRenderXml.getXmlError(setStaticDataException, -1);
					}
					xsltTransformation(xslt, xml, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
					return;
				}
				else {
					// generate the xml for in function of the current space type 
					try {
						this.currentSpace.getChannelActionObject().checkCalledAction(currentMode, staticData, runtimeData, out, xslt, setStaticDataException);
					}
					catch(NullPointerException ex) {
						//ex.printStackTrace();
						StackTraceElement[] stack = ex.getStackTrace();
						String s = "";
						for (int i=0; i<stack.length; i++) {
							s+=stack[i]+"\n";
						}
						log.error("renderXML"+" :: "+ex+" :: "+s);
						
						stylesheet = new StringBuffer("CStockage");
						
						ServerException e = new ServerException();
					
						xml = AbstractDefaultRenderXml.getXmlError(e, -1);
						try {
							xml.append(AbstractDefaultRenderXml.getXmlNavigation(spaces, this.currentSpace));
						}
						catch(PropertiesException pe) {
							if (log.isDebugEnabled()) {
								log.debug("renderXML"+" :: "+pe);
							}
						}
						
						// log
						if (log.isDebugEnabled()){
							log.debug("renderXML"+" :: "+e);
						}
						
						xsltTransformation(xslt, xml, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
						return;
					}
					
				}
			}
		}
		catch(BadConnexionParameters e) {
			stylesheet = new StringBuffer("CStockage");
			try {
				StringBuffer xmlTemp = this.currentSpace.getChannelActionObject().renderXmlBadConnexionParametersException(e);
				// log
				if (log.isDebugEnabled()){
					log.debug("checkCalledAction"+" :: "+e);
				}
				xsltTransformation(xslt, xmlTemp, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
				return;
			}
			catch(PropertiesException ex) {
				StringBuffer xmlTemp = this.currentSpace.getChannelActionObject().renderXmlChannelException(ex);
				xsltTransformation(xslt, xmlTemp, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
			}
		}
		catch(ConnexionParametersRequired e) {
			stylesheet = new StringBuffer("CStockage");
			try {
				StringBuffer xmlTemp = this.currentSpace.getChannelActionObject().renderXmlBadConnexionParametersException(e);
				// log
				if (log.isDebugEnabled()){
					log.debug("checkCalledAction"+" :: "+e);
				}
				xsltTransformation(xslt, xmlTemp, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
				return;
			}
			catch(PropertiesException ex) {
				StringBuffer xmlTemp = this.currentSpace.getChannelActionObject().renderXmlChannelException(ex);
				xsltTransformation(xslt, xmlTemp, out, runtimeData, stylesheet, buffer, isSpacesPersonalization);
			}
		}
	}
	
	
	
	
	
	/**
	 * Do the XSLT transformation in the channel
	 * @param xslt the XSLT object
	 * @param xmlTemp the XML generated
	 * @param out the ContentHandler object
	 * @param runtimeData the ChannelRuntimeData object
	 * @param stylesheet the stylesheet used
	 * @param buffer the buffer used in the channel
	 * @throws PortalException
	 */
	public static void xsltTransformation(XSLT xslt, StringBuffer xmlTemp, ContentHandler out, ChannelRuntimeData runtimeData, StringBuffer stylesheet, BufferAction buffer, boolean isSpacesPersonalization) throws PortalException {
		
		StringBuffer xml = new StringBuffer();
		xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");			
		xml.append("<ROOT spaces_personalization=\""+isSpacesPersonalization+"\">");
		xml.append(xmlTemp);
		
		// the baseActionUrl
		String baseActionUrl = runtimeData.getBaseActionURL();
		
		// set the stylesheet and the parameters
		
		//specify the stylesheet selector		
		xslt.setXSL("CStockage.ssl", stylesheet.toString(), runtimeData.getBrowserInfo());		
		
		//url to the channel
		xslt.setStylesheetParameter("baseActionURL",baseActionUrl);				
		
		//url to the download worker
		String downloadUrl = runtimeData.getBaseWorkerURL(UPFileSpec.FILE_DOWNLOAD_WORKER,true).replaceAll("%2F","/");
		xslt.setStylesheetParameter( "downloadURL", downloadUrl);	
		
		// the parameter to know if there is a resource in the clipboard
		if (buffer.getClipboard() != null)
			xslt.setStylesheetParameter("isClipboardEmpty","false");			      
		
		// the parameter used in the copy/move mode. tell if the mode is copy or move
		String clipboardParameter = buffer.getClipboardParameter();
		if (clipboardParameter != null) {
			xslt.setStylesheetParameter("clipboardParameter",clipboardParameter);			

		}

		// if we redirect in https
		try {
			xslt.setStylesheetParameter("redirection", ChannelConfiguration.getInstance().isHttpsRedirection()+"");
		}
		catch(PropertiesException e) {
			xslt.setStylesheetParameter("redirection", "false");
			
			// log
			if (log.isDebugEnabled()){
				log.debug("xsltTransformation"+" :: "+e);
			}
		}
		
		// the final balise
		xml.append("</ROOT>");

		//System.out.println(xml+"\n\n");
		// log
		/*if (log.isDebugEnabled()){
			log.debug("xsltTransformation"+" :: rendered XML "+xml);
		}*/
		
		//pass the result XML to the styling engine.
		String s = xml.toString();		
		xslt.setXML(s);		

		// set the output Handler for the output.
		xslt.setTarget(out);
	
		// do the deed
		xslt.transform();
	}
	

	/**
	 * Init the configuration spaces
	 * @throws DataBaseException
	 */
	/*private void initConfigurationSpaces() throws DataBaseException, ChannelException {

		
		
	}*/
	
	
	/**
	 * Init the personal spaces into the menu
	 * @throws DataBaseException
	 */
	private void initPersonalSpacesIntoMenu() throws DataBaseException, ChannelException {

		// init the personal spaces
		PersonalizationTool tool = new PersonalizationTool();
		TreeMap[] availableAndUsedSpaces = getAvailableAndUsedSpaces(tool, this.portalLogin, this.userGroups);
		this.currentSpace = setSpacesIntoMenu(this.spaces, this.currentSpace, tool, this.person, this.portalLogin, this.userGroups, availableAndUsedSpaces[0], this.buffer);
			
	}

		
	
	
	
	
	
		
	
	/**************************
	 *  Obliged spaces from DB
	 **************************/
	

	/**
	 * Set the obliged spaces for this user
	 */
	public static void addObligedSpacesToList(String userPortalLogin, Vector userGroups, ArrayList initialSpaces) throws DataBaseException, PropertiesException, ServerException {

		PersonalizationTool tool = new PersonalizationTool();
		
		// get each obliged space for the current user
		Vector spaces = tool.getAvailableSpacesForUser(userPortalLogin, userPortalLogin, true);
		for (int i=0; i<spaces.size(); i++) {
			Space space = (Space)spaces.elementAt(i);
			initialSpaces.add(space);
		}
			

		// get each obliged space for the groups of the current user
		for (int g=userGroups.size()-1; g>=0; g--) {
			spaces = tool.getAvailableSpacesForGroup((String)userGroups.elementAt(g), userPortalLogin, true);
			for (int i=0; i<spaces.size(); i++) {
				Space space = (Space)spaces.elementAt(i);
				initialSpaces.add(space);
			}
		}
		

		// get each obliged space for all
		spaces = tool.getAvailableSpacesForAll(userPortalLogin, true);
		for (int i=0; i<spaces.size(); i++) {
			Space space = (Space)spaces.elementAt(i);
			initialSpaces.add(space);
		}
		
	}
	
		

		
	/**************************
	 *   Manage shared spaces
	 **************************/
			
			
	/**
	 * The spaces still in use
	 */
	private TreeMap usedSpaces;
			
	/**
	 * The avalaible spaces
	 */
	private TreeMap availableSpaces;

	
	
	/**
	 * Set the used spaces and the avalaible spaces for this user
	 */
	public static TreeMap[] getAvailableAndUsedSpaces(PersonalizationTool tool, String userPortalLogin, Vector userGroups) throws DataBaseException, PropertiesException, ServerException {

		TreeMap usedSpaces = new TreeMap();
		TreeMap availableSpaces = new TreeMap();

		// we get each space from the preferences user
		Vector idUsedUsers = tool.getKeyOfSpacesFromPreferences(userPortalLogin);
		//System.out.println(idUsedUsers);
			
		// get each shared space for the current user
		Vector spaces = tool.getAvailableSpacesForUser(userPortalLogin, userPortalLogin, false);
		for (int i=0; i<spaces.size(); i++) {
			Space space = (Space)spaces.elementAt(i);
				
			String id = space.getKey();
			if (!idUsedUsers.contains(id)) {
				if (!availableSpaces.containsKey(id))
					availableSpaces.put(id, space);
			}
			else {
				if (!usedSpaces.containsKey(id))
					usedSpaces.put(id, space);
			}
		}
			

		// get each shared space for the groups of the current user
		for (int g=userGroups.size()-1; g>=0; g--) {
			spaces = tool.getAvailableSpacesForGroup((String)userGroups.elementAt(g), userPortalLogin, false);
			for (int i=0; i<spaces.size(); i++) {
				Space space = (Space)spaces.elementAt(i);
				
				String id = space.getKey();
				if (!idUsedUsers.contains(id)) {
					if (!availableSpaces.containsKey(id))
						availableSpaces.put(id, space);
				}
				else {
					if (!usedSpaces.containsKey(id))
						usedSpaces.put(id, space);
				}
					
			}
		}

		// get each shared space for the groups of the current user
		spaces = tool.getAvailableSpacesForAll(userPortalLogin, false);
		for (int i=0; i<spaces.size(); i++) {
			Space space = (Space)spaces.elementAt(i);
			
			String id = space.getKey();
			if (!idUsedUsers.contains(id)) {
				if (!availableSpaces.containsKey(id))
					availableSpaces.put(id, space);
			}
			else {
				if (!usedSpaces.containsKey(id))
					usedSpaces.put(id, space);
			}
				
		}
		
		TreeMap[] result = new TreeMap[2];
		result[0] = usedSpaces;
		result[1] = availableSpaces;
		return result;
	}
		

	/**
	 * Set the personal spaces into the spaces menu
	 * @throws DataBaseException
	 */
	public static Space setSpacesIntoMenu(ArrayList spaces, Space currentSpace, PersonalizationTool tool, IPerson person, String userPortalLogin, Vector userGroups, TreeMap usedSpaces, BufferAction buffer) throws DataBaseException, PropertiesException, ApplicationException {			
		
		Space currSpace = currentSpace;
		
		// delete each space wich has been deleted from the database and from the spaces list
		for (int i=0; i<spaces.size(); i++) {
			Space s = (Space)spaces.get(i);
			//System.out.println("1:"+s.getLabel());
			if (s.isPersonnalSpace() && !usedSpaces.containsKey(s.getKey())) {
				
				// delete from the database
				tool.delStorageUsersPreferencesForUser(s.getKey(), userPortalLogin);								
				
				// delete from the list
				spaces.remove(i);
				i--;
				
				// if the space deleted is the current space
				if (currentSpace!=null && s.getKey().equals(currentSpace.getKey())) {
					if (spaces.size()>0)
						currSpace = (Space)spaces.get(0);
					else currSpace = null;
				}
			}
		}
		
		// add each used space into the list and in the user preferences
		Iterator used = usedSpaces.values().iterator();
		while(used.hasNext()) {
			Space s = (Space)used.next();
			
			if (!spaces.contains(s)) {
				// add into the user preferences
				String spaceId = s.getKey();
				tool.addStorageUsersPreferences(spaceId, userPortalLogin);
				
				// add into the used list
				spaces.add(s);			
			}
			
		}
		
		// init each personal space connexion
		for (int i=0; i<spaces.size(); i++) {//TODO
			
			Space space = (Space)spaces.get(i);
			
			if (space.isPersonnalSpace() && space.getServerAccessObject()==null) {				
				//System.out.println("\n\n\n\n\n"+space.getLabel());
				
				initSpace(space, person, userPortalLogin, spaces, buffer, userGroups);
				
				/*
				// set the server access
				boolean serverOk = false;
				try {
					serverOk = space.setServerAccessObject();
				}
				catch(Exception e) {
					log.error("setSpacesIntoMenu"+" :: "+e);
					//serverOk = false;
				}
				//System.out.println("   server ok ? "+serverOk);
				// set for the space, the channel action object used
				
				boolean wellDone = false;
				//if (serverOk) {
				wellDone = space.setChannelActionObject(person, spaces, buffer, userPortalLogin, userGroups);
				//System.out.println("   action ok ? "+wellDone);
				//}				
				 
				 //if (!wellDone) {
				 //spaces.remove(i);
				 //i--;
				 //}
				*/
				
			}
			
		}
		if (currSpace == null && spaces.size()>0) {
			currSpace = (Space)spaces.get(0);
		}
		
		return currSpace;
	}
		
	
	
	/**
	 * Generate the XML when we want to manage the shared spaces
	 * @param runtimeData the runtime data channel object
	 * @return the XML for this mode
	 * @throws CancelException
	 * @throws DataBaseException
	 */
	private StringBuffer renderXmlManageSharedSpaces(ChannelRuntimeData runtimeData) throws ChannelException, ConnexionParametersRequired, ApplicationException, PropertiesException, CancelException, DataBaseException, ServerException {
		
		// if it is the first call
		if (usedSpaces == null && availableSpaces == null) {
			TreeMap[] availableAndUsedSpaces = getAvailableAndUsedSpaces(new PersonalizationTool(), portalLogin, userGroups);
			usedSpaces = availableAndUsedSpaces[0];
			availableSpaces = availableAndUsedSpaces[1];
		}
		
		// if cancel
		String action = runtimeData.getParameter("action");
		if (action!=null) {
			if (action.equals("cancel")) {
				renderXmlManageSharedSpacesCancel();
			}
			
			// if valid		
			if (action.equals("valid")) {
				StringBuffer xml = renderXmlManageSharedSpacesValid(runtimeData);
				return xml;
			}
		}
		
		// if add
		String add = runtimeData.getParameter("add");
		if (add != null) {
			renderXmlManageSharedSpacesAdd(runtimeData);
		}
		
		// if delete
		String delete = runtimeData.getParameter("delete");
		if (delete != null) {
			renderXmlManageSharedSpacesDelete(runtimeData);
		}
		
		// generate the xml
		return renderXmlManageSharedSpacesGetXmlForRender(runtimeData);
	}
	
	
	
	
	
	/**
	 * Generate the XML when we want to manage the shared spaces
	 * @param runtimeData the runtime data channel object
	 * @return the XML for this mode
	 * @throws CancelException
	 */
	private StringBuffer renderXmlManageSharedSpacesGetXmlForRender(ChannelRuntimeData runtimeData) throws ServerException, PropertiesException  {
		
		StringBuffer xml = new StringBuffer();
		
		xml.append("<used>");
		xml.append(renderXmlManageSharedSpacesGetXmlForRenderOnlySpaces(usedSpaces));		
		xml.append("</used>");
		
		xml.append("<available>");		
		xml.append(renderXmlManageSharedSpacesGetXmlForRenderOnlySpaces(availableSpaces));
		xml.append("</available>");
		
		return xml;
	}
	
	
	
	
	/**
	 * Generate the XML when we want to manage the shared spaces
	 * @param spaces spaces list
	 * @return the XML for this mode
	 * @throws ServerException
	 * @throws PropertiesException
	 */
	/*private StringBuffer renderXmlManageSharedSpacesGetXmlForRenderOnlySpaces(TreeMap spaces) throws ServerException, PropertiesException  {
		
		StringBuffer xmlReadOnly = new StringBuffer();
		StringBuffer xmlWrite = new StringBuffer();
		StringBuffer xmlManage = new StringBuffer();
		
		Iterator used = spaces.values().iterator();
		while (used.hasNext()) {			
			Space space = (Space)used.next();			
			String target = space.getCurrentViewer().getDisplayName();
			
			//if (!space.getCurrentViewer().isWriting()) {
			if (!space.getCurrentViewer().isWriting()) {
				xmlReadOnly.append("<space key=\""+space.getKey()+"\" writing=\""+space.getCurrentViewer().isWriting()+"\" from=\""+(new UserForPersonalization(space.getCurrentViewer().getFromKey()).getDisplayName())+"\" target=\""+target+"\">A"+space.getLabel()+"</space>");
			}
			else {
				if (!space.isSharing()) {
					xmlWrite.append("<space key=\""+space.getKey()+"\" writing=\""+space.getCurrentViewer().isWriting()+"\" from=\""+(new UserForPersonalization(space.getCurrentViewer().getFromKey()).getDisplayName())+"\" target=\""+target+"\">B"+space.getLabel()+"</space>");
				}
				else {
					xmlManage.append("<space key=\""+space.getKey()+"\" writing=\""+space.getCurrentViewer().isWriting()+"\" from=\""+(new UserForPersonalization(space.getCurrentViewer().getFromKey()).getDisplayName())+"\" target=\""+target+"\">C"+space.getLabel()+"</space>");
				}
			}
		}				
		
		StringBuffer xml = new StringBuffer();
		xml.append("<read_only>");
		xml.append(xmlReadOnly);
		xml.append("</read_only>");
		xml.append("<read_and_write>");
		xml.append(xmlWrite);
		xml.append("</read_and_write>");
		xml.append("<read_and_write_and_manage>");
		xml.append(xmlManage);
		xml.append("</read_and_write_and_manage>");
		return xml;
	}*/
	private StringBuffer renderXmlManageSharedSpacesGetXmlForRenderOnlySpaces(TreeMap spaces) throws ServerException, PropertiesException  {
		
		StringBuffer xml = new StringBuffer();
		
		Iterator used = spaces.values().iterator();
		while (used.hasNext()) {
			Space space = (Space)used.next();			
			String target = space.getCurrentViewer().getTargetDisplayName();
			
			xml.append("<space key=\""+space.getKey()+"\" from=\""+(new UserForPersonalization(space.getCurrentViewer().getFromKey()).getDisplayName())+"\" target=\""+target+"\">"+space.getLabel()+"</space>");
		}				

		return xml;
	}
	
	
	
	/**
	 * Generate the XML when we click on "cancel" when we manage the shared spaces
	 * @throws CancelException
	 */
	private void renderXmlManageSharedSpacesCancel() throws CancelException, PropertiesException, ConnexionParametersRequired {
		usedSpaces = null;
		availableSpaces = null;

		// if it the asked authentication, and none password still set
		if (this.currentSpace.isAskedAuthentication() && (this.currentSpace.getPassword()==null || this.currentSpace.getPassword().trim().equals(""))) {
			throw new ConnexionParametersRequired();
		}
		
		throw new CancelException();
	}
	
	
	
	
	
	/**
	 * Generate the XML when we click on "valid" when we manage the shared spaces
	 * @param runtimeData the runtime data channel object
	 * @return the XML for this mode
	 * @throws DataBaseException
	 */
	private StringBuffer renderXmlManageSharedSpacesValid(ChannelRuntimeData runtimeData) throws ChannelException, DataBaseException, PropertiesException, ApplicationException, ConnexionParametersRequired {
		this.currentSpace = setSpacesIntoMenu(this.spaces, this.currentSpace, new PersonalizationTool(), this.person, this.portalLogin, this.userGroups, usedSpaces, this.buffer);
		
		usedSpaces = null;
		availableSpaces = null;
		
		stylesheet = new StringBuffer("CStockage");
		if (setStaticDataException instanceof AnyVisibleSpaceException) {
			setStaticDataException = null;
		}
		
		
		
		if (this.currentSpace!=null) {
			// if it the asked authentication, and none password still set
			if (this.currentSpace.isAskedAuthentication() && (this.currentSpace.getPassword()==null || this.currentSpace.getPassword().trim().equals(""))) {
				throw new ConnexionParametersRequired();
			}
			
			if (this.currentSpace.getChannelActionObject()==null) {
				this.currentSpace = (Space)this.spaces.get(0);
			}
			return this.currentSpace.getChannelActionObject().renderXmlWithInformation("2015");
		}
		else {
			return AbstractDefaultRenderXml.getXmlInformation("2015");
		}
	}
	
	
	
	
	
	/**
	 * Generate the XML when we click on "add" when we manage the shared spaces
	 * @param runtimeData the runtime data channel object
	 * @return the XML for this mode
	 */
	private StringBuffer renderXmlManageSharedSpacesAdd(ChannelRuntimeData runtimeData) throws ServerException, PropertiesException  {
		
		/*Iterator iter = usedSpaces.values().iterator();
		 while(iter.hasNext()) {
		 System.out.println(iter.next());
		 }*/
		
		
		// we check the selected spaces, remove them from "availableSpaces" and add to "usedSpaces"
		String[] available = runtimeData.getParameterValues("available");
		for (int i=0; available!=null && i<available.length; i++) {
			String spaceId = available[i];
			Space space = (Space)availableSpaces.remove(spaceId);
			usedSpaces.put(spaceId, space);
		}		
		
		return renderXmlManageSharedSpacesGetXmlForRender(runtimeData);
	}
	
	
	
	/**
	 * Generate the XML when we click on "delete" when we manage the shared spaces
	 * @param runtimeData the runtime data channel object
	 * @return the XML for this mode
	 */
	private StringBuffer renderXmlManageSharedSpacesDelete(ChannelRuntimeData runtimeData) throws ServerException, PropertiesException  {
		
		// we check the selected spaces, remove them from "usedSpaces" and add to "availableSpaces"
		String[] used = runtimeData.getParameterValues("used");
		for (int i=0; used!=null && i<used.length; i++) {
			String spaceId = used[i];
			Space space = (Space)usedSpaces.remove(spaceId);
			availableSpaces.put(spaceId, space);
		}	
		
		return renderXmlManageSharedSpacesGetXmlForRender(runtimeData);
	}
		
		

/***************************************************************************/
/*********************START OF DOWNLOAD FUNCTIONS***************************/
/***************************************************************************/

	/**
	 * The resource to download
	 */
	private ChannelResource downloadResource = null;
	
	
  	/**
	 * Return the content type of the seleted file
	 * @see org.jasig.portal.IMimeResponse#getContentType()
	 */
	public String getContentType() {				
		return downloadResource.getContentType();
	}


	
	/**
	 * Send HTTP header
	 * @see org.jasig.portal.IMimeResponse#getHeaders()
	 */
	public Map getHeaders() {
		Map map = new HashMap();
      
		fileId = runtimeData.getParameter("downloadFile");
		//System.out.println("fileId: "+fileId); 
		try {
			fileId = URLDecoder.decode(fileId,"UTF-8");
			//System.out.println("fileId: "+fileId);
		}
		catch (UnsupportedEncodingException e) {
			log.error("getHeaders"+" :: "+e);			
		}
    
		// we check if it is a directory
		String path = currentSpace.getCurrentPath();
		
		try {
			//System.out.println("path: "+path);
			downloadResource = this.currentSpace.getServerAccessObject().download(fileId,path);
				
			fileId = fileId.replaceAll(" ", "_");
			fileId = fileId.replaceAll("[àäâã]", "a");
			fileId = fileId.replaceAll("[éêèë]", "e");
			fileId = fileId.replaceAll("[îïì]", "i");
			fileId = fileId.replaceAll("[ôöòõ]", "o");
			fileId = fileId.replaceAll("[ûüù]", "u");
			fileId = fileId.replaceAll("ç", "c");
			fileId = fileId.replaceAll("ñ", "n");
			fileId = fileId.replaceAll("[ÀÄÂÃ]", "A");
			fileId = fileId.replaceAll("[ÉÈËÊ]", "E");
			fileId = fileId.replaceAll("[ÌÏÎ]", "I");
			fileId = fileId.replaceAll("[ÒÖÔÕ]", "O");
			fileId = fileId.replaceAll("[ÙÜÛ]", "U");
			fileId = fileId.replaceAll("Ç", "C");
			fileId = fileId.replaceAll("Ñ", "N");		
			
			// open in new window : inline  -- ||
			// download : attachment  -- -- -- ||
			//                                 \/
			map.put("Content-disposition", "attachment; filename="+fileId  );

			return map;
		}
		catch(DownloadException e) {
			log.error("getHeaders"+" :: "+e);
			map.put("Location", runtimeData.getBaseActionURL());
			return map;
		}
	}
	
	

	/**
	 * Send HTTP header
	 * @see org.jasig.portal.IMimeResponse#getHeaders()
	 */
	public Map getHeaders2() {
		Map map = new HashMap();
      
		fileId = runtimeData.getParameter("downloadFile");		
    
		// we check if it is a directory
		String path = currentSpace.getCurrentPath();
		
		try {
			//System.out.println("path: "+path);
			downloadResource = this.currentSpace.getServerAccessObject().download(fileId,path);				
			
			// open in new window : inline  -- ||
			// download : attachment  -- -- -- ||
			//                                 \/
			//map.put("Content-disposition", "attachment; filename=\""+fileId+"\";charset=\"utf-8\" ");
			map.put("Content-disposition", "attachment; filename=\""+fileId+"\"");
			//map.put("Content-disposition", "attachment");			
			//map.put("Content-Type", "text/html; charset=\"iso-8859-1\"");
			return map;
		}
		catch(DownloadException e) {
			log.error("getHeaders"+" :: "+e);
			map.put("Location", runtimeData.getBaseActionURL());
			return map;
		}
	}
	
	
	/**
	 *	Get the strem for the file to dowload
	 * @see org.jasig.portal.IMimeResponse#getInputStream()
	 */
	public InputStream getInputStream() throws IOException {	
		return downloadResource.getInputStream();
	}


	/**
	 * Function unused here
	 * @see org.jasig.portal.IMimeResponse#downloadData(java.io.OutputStream)
	 */
	public void downloadData(OutputStream arg0) throws IOException {
	}


	/**
	 * Function unused here
	 * @see org.jasig.portal.IMimeResponse#getName()
	 */
	public String getName() {
		System.out.println("passe par le getName");
		return "yahoo.txt";
	}


	/**
	 * Print the error generated by the download
	 * @see org.jasig.portal.IMimeResponse#reportDownloadError(java.lang.Exception)
	 */
	public void reportDownloadError(Exception arg0) {
		log.error("reportDownloadError"+" :: "+arg0);
	}

	

	/***************************************************************************/
	/*********************END OF DOWNLOAD FUNCTIONS*****************************/
	/***************************************************************************/



	/***************************************************************************/
	/**********************START OF CACHE FUNCTIONS*****************************/
	/***************************************************************************/

	/* (non-Javadoc)
	 * @see org.jasig.portal.ICacheable#generateKey()
	 */
	public ChannelCacheKey generateKey() {

	    ChannelCacheKey k = new ChannelCacheKey();
	    StringBuffer sbKey = new StringBuffer(1024);
	    sbKey.append(this.getClass().getName() + " : ");
	    sbKey.append("login : "+portalLogin+";");
	    sbKey.append("SubscribeId:"+this.staticData.getChannelSubscribeId());
	    k.setKeyScope(ChannelCacheKey.INSTANCE_KEY_SCOPE);

	    k.setKey(sbKey.toString());
	    k.setKeyValidity(new Long(System.currentTimeMillis()));
	    /*LogService.log(LogService.DEBUG,
	                              "CStockage::generateKey() (id : " +
	                              this.staticData.getChannelSubscribeId() +
	                              ") : generateKey :  -> Cle de cache:" + k.getKey());
	    */
		return k;
	}

	/* (non-Javadoc)
	 * @see org.jasig.portal.ICacheable#isCacheValid(java.lang.Object)
	 */
	public boolean isCacheValid(Object validity) {

	    boolean cacheValid = false;

	    /*if (helpMode) {
	    	//helpMode = false;
	    	return false;
	    }*/
	    
	  if(runtimeData.getParameters().size()==0)
	  		cacheValid=true;

	  /*if (cacheValid) {
        LogService.log(LogService.DEBUG,
                                  "CStockage::isCacheValid (" +
                                  this.staticData.getChannelSubscribeId() +
                                  ") : isCacheValid -> YES");
      }
      else {
        LogService.log(LogService.DEBUG,
                                  "CStockage::isCacheValid (" +
                                  this.staticData.getChannelSubscribeId() +
                                  ") : isCacheValid -> NO");
      }*/
	    return cacheValid;
	}
	/***************************************************************************/
	/************************END OF CACHE FUNCTIONS*****************************/
	/***************************************************************************/
}
