/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *     "Apache Jetspeed" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache" or
 *    "Apache Jetspeed", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.jetspeed.portal.security.portlets;

// Jetspeed
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletState;
import org.apache.jetspeed.portal.PortletConfig;
import org.apache.jetspeed.portal.PortletException;
import org.apache.jetspeed.portal.PortletInstance;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.portletcache.Cacheable;
import org.apache.jetspeed.services.security.PortalResource;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.util.template.JetspeedLink;
import org.apache.jetspeed.util.template.JetspeedLinkFactory;
import org.apache.jetspeed.util.MimeType;
import org.apache.jetspeed.services.PortletStats;

// Turbine imports
import org.apache.turbine.util.RunData;
import org.apache.turbine.services.localization.Localization;

// ECS
import org.apache.ecs.ConcreteElement;
import org.apache.jetspeed.util.JetspeedClearElement;



/**
<p>
This object is used to wrap a Portlet, ensuring that access control rules are enforced.
</p>

@author <A HREF="mailto:sgala@apache.org">Santiago Gala</A>
@version $Id: PortletWrapper.java,v 1.22 2003/07/23 19:50:18 morciuch Exp $
*/
public class PortletWrapper implements Portlet
{
    /**
     * Static initialization of the logger for this class
     */    
    private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(PortletWrapper.class.getName());
    
    /*
     * The portlet we are wrapping
     */
    private Portlet wrappedPortlet = null;
    private PortalResource portalResource = null;
    private ConcreteElement content = null;

    public PortletWrapper(Portlet inner)
    {
        wrappedPortlet = inner;
        portalResource = new PortalResource(wrappedPortlet);
    }

    /**
    */
    public final String getName()
    {
        //This means name is accessible for every Portlet
        return wrappedPortlet.getName();
    }

    /**
    */
    public final void setName(String name)
    {
        //if we want to secure this, we need a context for the check
        wrappedPortlet.setName(name);
    }

    /**
    */
    public final PortletConfig getPortletConfig()
    {
        return wrappedPortlet.getPortletConfig();
    }

    /**
    */
    public final void setPortletConfig(PortletConfig pc)
    {
        //if we want to secure this, we need a context for the check
        wrappedPortlet.setPortletConfig(pc);
    }
    
    /**
    */
    public ConcreteElement getContent(RunData rundata)
    {
 
        if (checkPermission(rundata, JetspeedSecurity.PERMISSION_VIEW))
        {
            if (PortletStats.isEnabled())
            {
            	long start = System.currentTimeMillis();
            	content = wrappedPortlet.getContent(rundata);
            	long time = System.currentTimeMillis() - start; // time it took to get the content for this portlet
                PortletStats.logAccess(rundata, this, PortletStats.ACCESS_OK, time);
            } else {
            	content = wrappedPortlet.getContent(rundata);
            }
            
            return content;
        }
        else 
        {
            if (PortletStats.isEnabled())
            {
                PortletStats.logAccess(rundata, this, PortletStats.ACCESS_DENIED);
            }
            return new JetspeedClearElement(Localization.getString(rundata, "SECURITY_NO_ACCESS_TO_PORTLET"));
        }
    }

    /**
    Provide a description within PML if the user has specified one.

    @return a null entry if the user hasn't defined anything
    */
    public String getDescription()
    {
        return wrappedPortlet.getDescription();
    }

    public String getDescription(String instanceDescription)
    {
        return wrappedPortlet.getDescription(instanceDescription);
    }

    /**
    */
    public void setDescription(String description)
    {
        wrappedPortlet.setDescription(description);
    }

    /**
     * @see Portlet#getImage
     */
    public String getImage(String instanceImage)
    {
        return wrappedPortlet.getImage(instanceImage);
    }
    
    /**
     * @see Portlet#setImge
     */
    public void setImage(String image)
    {
        wrappedPortlet.setImage(image);
    }
    /**
     * @see Portlet#getTitle
     */
    public String getTitle()
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                         JetspeedSecurity.PERMISSION_VIEW ) )
                                              { */
        return wrappedPortlet.getTitle();
        /* } */

    }

    /**
     * @see Portlet#getImage
     */
    public String getTitle(String instanceTitle)
    {
        return wrappedPortlet.getTitle(instanceTitle);
    }
    
    /**
    Set the title for this Portlet
    */
    public void setTitle(String title)
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                              JetspeedSecurity.PERMISSION_CUSTOMIZE ) )
                                              { */
        wrappedPortlet.setTitle(title);
        /* } */
    }


    /**
    */
    public boolean getAllowEdit(RunData rundata)
    {
        return checkPermission(rundata, JetspeedSecurity.PERMISSION_CUSTOMIZE);
    }

    /**
    */
    public boolean getAllowMaximize(RunData rundata)
    {
        return checkPermission(rundata, JetspeedSecurity.PERMISSION_MAXIMIZE);
    }

    /**
    By default don't provide any initialization
    */
    public void init() throws PortletException 
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                              JetspeedSecurity.PERMISSION_CUSTOMIZE) )
                                              { */
        wrappedPortlet.init();
        /* } */
    }

    /**
    @see Portlet#getCreationTime
    */
    public long getCreationTime()
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                              JetspeedSecurity.PERMISSION_VIEW) )
                                              { */
        return wrappedPortlet.getCreationTime();
        /* } */
    }
    
    /**
    @see Portlet#setCreationTime
    */
    public void setCreationTime(long creationTime)
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                              JetspeedSecurity.PERMISSION_CUSTOMIZE) )
                                              { */
        wrappedPortlet.setCreationTime(creationTime);
    }
    
    /**
    @see Portlet#supportsType
    */
    public boolean supportsType(MimeType mimeType)
    {
        /* FIXME, no rundata here if( !checkPermission(rundata, 
                                              JetspeedSecurity.PERMISSION_VIEW) )
                                              { */
            return wrappedPortlet.supportsType(mimeType);
            /* } */
    }

    /**
     * Utility method for checking Permissions on myself.
     * @param rundata A rundata Object
     * @param permissionName String the name of the Permission requested
     * @return boolean is it granted?
     */
    protected boolean checkPermission(RunData rundata, String permissionName)
    {
        try
        {
            JetspeedLink jsLink = JetspeedLinkFactory.getInstance(rundata);
            portalResource.setOwner(jsLink.getUserName());
            JetspeedLinkFactory.putInstance(jsLink);
        }
        catch (Exception e)
        {
            logger.warn(e.getMessage(), e);
            portalResource.setOwner(null);
        }
        
        if (logger.isDebugEnabled())
        {
            logger.debug("checking for Portlet permission: "
                      + permissionName 
                      + " for portlet: "
                      + wrappedPortlet.getName() 
                      + " Owner = " 
                    + portalResource.getOwner());
        }
        
        return JetspeedSecurity.checkPermission((JetspeedUser) rundata.getUser(),
                                                 portalResource,
                                                 permissionName);
    }

    // utility methods

    /**
    Returns TRUE if the title bar in should be displayed. The title bar includes
    the portlet title and action buttons. 
     
    NOTE(FIXME) Not in Portlet interface. Called a la Bean from Velocity.
    @param rundata A RunData object
    */
    public boolean isShowTitleBar(RunData rundata)
    {
        if (wrappedPortlet.getPortletConfig() != null) 
        {
            // Parameter can exist in PSML or <portlet-entry>
            return Boolean.valueOf(wrappedPortlet.getPortletConfig().getInitParameter("_showtitlebar", "true")).booleanValue();
        }
        return getAttribute("_showtitlebar", "true", rundata).equals("true");
    }

    
    /**
    Retrieve a portlet attribute from persistent storage

    @param attrName The attribute to retrieve
    @parm attrDefValue The value if the attr doesn't exists
    @param rundata A RunData object
    @return The attribute value
    */
    public String getAttribute(String attrName, String attrDefValue, RunData rundata)
    {
        if (checkPermission(rundata, JetspeedSecurity.PERMISSION_VIEW))
        {
            return wrappedPortlet.getAttribute(attrName, attrDefValue, rundata);
        }
        else
        {
            //FIXME: for the moment we will allow this call to succeed...
            //throw new TurbineRuntimeException( "Security check failed" );
            return wrappedPortlet.getAttribute(attrName, attrDefValue, rundata);
        }

    }

    /**
     * Sets a portlet attribute to persistent storage
     *
     * @param attrName The attribute to retrieve
     * @param attrValue The value 
     * @param rundata A RunData object
     */
    public void setAttribute(String attrName, String attrValue, RunData rundata)
    {
        if (checkPermission(rundata, JetspeedSecurity.PERMISSION_VIEW))
        {
            wrappedPortlet.setAttribute(attrName, attrValue, rundata);
        }
        else
        {
            //FIXME: for the moment we will allow this call to succeed...
            //throw new TurbineRuntimeException( "Security check failed" );
            wrappedPortlet.setAttribute(attrName, attrValue, rundata);
        }

    }

    /**
     * Gets the portlet instance associated with this portlet.
     *
     * @param rundata A RunData object
     * @return PortletInstance
     */
    public PortletInstance getInstance(RunData rundata)
    {
        return wrappedPortlet.getInstance(rundata);
    }

    /**
     * <p>Return an instance of one of the classes in this package
     * making tests before calling the wrapped portlet</p>
     * <p>Different wrapper classes must be used with the current API
     * depending on the interfaces implemented by the portlet. :-(</p>
     *
     */
    public static Portlet wrap(Portlet aPortlet)
    {
        //SGP Security test
        if (aPortlet instanceof PortletState)
        {
            if (aPortlet instanceof Cacheable)
            {
                return new CacheableStatefulPortletWrapper(aPortlet);
            }
            return new StatefulPortletWrapper(aPortlet);
        }
        if (aPortlet instanceof Cacheable)
        {
            return new CacheablePortletWrapper(aPortlet);
        }
        return new PortletWrapper(aPortlet);
        
    }
 

    public String getID()
    {
        return wrappedPortlet.getID();
    }

    public void setID(String id)
    {
        wrappedPortlet.setID(id);
    }

    /**
    * @return true if the portlet does its own customization
    */
    public boolean providesCustomization()
    {
        return wrappedPortlet.providesCustomization();
    } 

    public Portlet getPortlet()
    {
        return wrappedPortlet;
    }      
}
