/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 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.modules.actions.portlets;
// Jetspeed Stuff
import org.apache.jetspeed.modules.actions.portlets.CustomizeSetAction;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletConfig;
import org.apache.jetspeed.portal.PortletException;
import org.apache.jetspeed.portal.PortletSkin;
import org.apache.jetspeed.portal.portlets.VelocityPortlet;
import org.apache.jetspeed.services.persistence.PersistenceManager;
import org.apache.jetspeed.services.Registry;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.om.BaseSecurityReference;
import org.apache.jetspeed.om.profile.Entry;
import org.apache.jetspeed.om.profile.Profile;
import org.apache.jetspeed.om.profile.Skin;
import org.apache.jetspeed.om.profile.psml.PsmlSkin;
import org.apache.jetspeed.om.SecurityReference;
import org.apache.jetspeed.om.registry.PortletEntry;
import org.apache.jetspeed.om.registry.Parameter;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.services.PortalToolkit;
import org.apache.jetspeed.services.security.PortalResource;
import org.apache.jetspeed.portal.PortletInstance;
import org.apache.jetspeed.util.MetaData;
import org.apache.jetspeed.services.statemanager.SessionState;
// Turbine stuff
import org.apache.turbine.util.Log;
import org.apache.turbine.util.RunData;
import org.apache.turbine.modules.ActionLoader;
// Velocity Stuff
import org.apache.velocity.context.Context;
import java.util.Vector;
import java.util.Iterator;
/**
* This action implements the default portlet behavior customizer
*
* <p>Don't call it from the URL, the Portlet and the Action are automatically
* associated through the registry PortletName
*
* @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a>
*/
public class CustomizeAction extends VelocityPortletAction
{
/**
* Subclasses must override this method to provide default behavior
* for the portlet action
*
* <table>
* <tr><th>Context </th><th> Description</th></tr>
* <!-- --------- ------------------------- -->
* <tr><td>action </td><td> Action to use</td></tr>
* <tr><td>current_skin </td><td> Current skin for this portlet INSTANCE</td></tr>
* <tr><td>params </td><td> List of configurable parameters from the REGISTRY entry.</td></tr>
* <tr><td>portlet </td><td> Portlet, not the Portlet Instance!</td></tr>
* <tr><td>skins </td><td> List of skins</td></tr>
* <tr><td>security </td><td> List of security ref</td></tr>
* <tr><td>security_ref </td><td> Current securityRef for this portlet INSTANCE</td></tr>
* </table>
*/
protected void buildNormalContext( VelocityPortlet portlet,
Context context,
RunData rundata )
{
// generic context stuff
context.put("skins", CustomizeSetAction.buildList(rundata, Registry.SKIN));
context.put("securitys", CustomizeSetAction.buildList(rundata, Registry.SECURITY));
// we should first retrieve the portlet to customize
Portlet p = ((JetspeedRunData)rundata).getCustomized();
context.put("action", "portlets.CustomizeAction");
if (p==null) return;
// retrieve the portlet parameters
PortletEntry entry = (PortletEntry)Registry.getEntry(Registry.PORTLET,p.getName());
// save the entry in the session
Vector params = new Vector();
Iterator i = entry.getParameterNames();
while(i.hasNext())
{
String name = (String)i.next();
Parameter param = entry.getParameter(name);
// filter some "system" and hidden parameters
if ( (!param.isHidden()) && (name.charAt(0)!='_') )
{
// check the user role
if (JetspeedSecurity.checkPermission((JetspeedUser)rundata.getUser(), new PortalResource( entry, param), JetspeedSecurity.PERMISSION_CUSTOMIZE))
{
params.add(entry.getParameter(name));
}
}
}
// get the customization state for this page
SessionState customizationState = ((JetspeedRunData)rundata).getPageSessionState();
customizationState.setAttribute("customize-parameters", params);
// populate the customizer context
context.put("parameters", params);
context.put("portlet", p);
context.put("customizer", portlet);
if (p.getPortletConfig().getSecurityRef() != null)
context.put("security_ref", p.getPortletConfig().getSecurityRef().getParent());
if (p.getPortletConfig().getSkin() != null)
context.put("current_skin", p.getPortletConfig().getPortletSkin().getName());
Profile profile = ((JetspeedRunData)rundata).getCustomizedProfile();
String currentTitle = profile.getDocument().getEntryById(p.getID()).getTitle();
if (currentTitle == null && p.getPortletConfig().getMetainfo() != null)
{
currentTitle = p.getPortletConfig().getMetainfo().getTitle();
}
context.put("current_title", currentTitle);
}
/** Clean up the customization state */
public void doCancel(RunData rundata, Context context)
{
((JetspeedRunData)rundata).setCustomized(null);
if (((JetspeedRunData)rundata).getCustomized()==null)
{
try
{
ActionLoader.getInstance().exec( rundata, "controls.EndCustomize" );
}
catch (Exception e)
{
Log.error("Unable to load action controls.EndCustomize ",e);
}
}
}
/** Updates the customized portlet entry */
public void doUpdate(RunData rundata, Context context)
{
// get the customization state for this page
SessionState customizationState = ((JetspeedRunData)rundata).getPageSessionState();
// we should first retrieve the portlet to customize and its parameters
// definition
Portlet p = ((JetspeedRunData)rundata).getCustomized();
Vector params = (Vector) customizationState.getAttribute("customize-parameters");
String newSecurityParent = rundata.getParameters().getString("_security_ref");
String newSkinName = (String) rundata.getParameters().getString("_skin");
String newTitle = (String) rundata.getParameters().getString("current_title");
boolean changeRequested = ( (params == null) || (newSkinName == null) || (newSecurityParent == null) || (newTitle == null));
boolean madePsChange = false;
boolean madePcChange = false;
if ((p==null) || (changeRequested = false ))
{
doCancel(rundata, context);
return;
}
PortletConfig pc = p.getPortletConfig();
Profile profile = ((JetspeedRunData)rundata).getCustomizedProfile();
Entry entry = profile.getDocument().getEntryById(p.getID());
// Only update the security ref if the parent changed
if ((newSecurityParent != null))
{
boolean securityChanged = false;
SecurityReference currentSecurityRef = pc.getSecurityRef();
if (currentSecurityRef != null)
{
securityChanged = (newSecurityParent.equals(currentSecurityRef.getParent()) == false);
}
else
{
securityChanged = (newSecurityParent.trim().length() > 0);
}
if (securityChanged == true)
{
SecurityReference securityRef = null;
if ((newSecurityParent.trim().length() > 0))
{
securityRef = new BaseSecurityReference();
securityRef.setParent( newSecurityParent);
}
// Note: setting the portlet's config may not be a good idea -
// it might be used as the Portlet for other PSMLDocument Entries that
// have a different idea of security - and the caching of Portlets does
// NOT include security -ggolden.
pc.setSecurityRef(securityRef);
entry.setSecurityRef(securityRef);
madePcChange = true;
}
}
// Only update the skin if the name changed
if (newSkinName != null)
{
boolean skinChanged = false;
String currentSkinName = null;
if (pc.getSkin() != null)
currentSkinName = pc.getPortletSkin().getName();
if (currentSkinName != null)
{
skinChanged = (newSkinName.equals(currentSkinName) == false);
}
else
{
skinChanged = (newSkinName.trim().length() > 0);
}
if (skinChanged == true)
{
PortletSkin skin = null;
if ((newSkinName.trim().length() > 0))
{
skin = PortalToolkit.getSkin(newSkinName);
if (skin != null)
{
// Note: setting the portlet's config may not be a good idea -
// it might be used as the Portlet for other PSMLDocument Entries that
// have a different idea of skin - and the caching of Portlets does
// NOT include skin -ggolden.
pc.setPortletSkin(skin);
Skin psmlSkin = entry.getSkin();
if (psmlSkin == null)
{
entry.setSkin(new PsmlSkin());
}
entry.getSkin().setName(newSkinName);
}
else
{
Log.warn( "Unable to update skin for portlet entry " + entry.getId() + " because skin " + skin + " does not exist.");
}
}
else
{
// Note: setting the portlet's config may not be a good idea -
// it might be used as the Portlet for other PSMLDocument Entries that
// have a different idea of skin - and the caching of Portlets does
// NOT include skin -ggolden.
pc.setPortletSkin( null);
entry.setSkin(null);
}
madePcChange = true;
}
}
// Only update the title if the title changed
if (newTitle != null)
{
boolean titleChanged = false;
String currentTitle = entry.getTitle();
MetaData md = pc.getMetainfo();
if (currentTitle == null && md != null && md.getTitle() != null)
currentTitle = md.getTitle();
if (currentTitle != null)
{
titleChanged = (newTitle.equals(currentTitle) == false);
}
else
{
titleChanged = (newTitle.trim().length() > 0);
}
if (titleChanged == true)
{
if ((newTitle.trim().length() > 0))
{
// Note: setting the portlet's config may not be a good idea -
// it might be used as the Portlet for other PSMLDocument Entries that
// have a different idea of title - and the caching of Portlets does
// NOT include title -ggolden.
if (md == null) {
md = new MetaData();
pc.setMetainfo(md);
}
md.setTitle(newTitle);
entry.setTitle(newTitle);
madePcChange = true;
}
}
}
// Update paramaters
try
{
PortletInstance instance = PersistenceManager.getInstance(p, rundata);
Iterator i = params.iterator();
while(i.hasNext())
{
Parameter param = (Parameter)i.next();
String name = param.getName();
String newValue = rundata.getParameters().getString(name);
// we treat null and empty string in the same way, we should
// probably find a better system as they may have different semantic
if ((newValue==null)||(newValue.equals("")))
{
// this will fail if the parameter is currently set in the registry
// but not in the PSML entry. This is a good thing !
instance.removeAttribute(name);
}
else
{
// only update if the value changed
if (!newValue.equals(p.getPortletConfig().getInitParameter(name)))
{
instance.setAttribute(name,newValue);
// Note: setting the portlet's config *IS NOT* a good idea -
// it might be used as the Portlet for other PSMLDocument Entries that
// used to have the same parameters as this one (Portlets *ARE* cached on
// their parameters), so changing the parameters like this changes them for
// all and make the Portlet *NOT MATCH* the cache handle.
// It's ok not to do this here, since when this PSML is next rendered,
// it will look for a portlet with the new set of parameters, and will
// find / create a new one if needed - ggolden
//pc.setInitParameter(name,newValue);
madePsChange = true;
}
}
}
// save all the changes
if ((madePsChange == true) || (madePcChange == true))
{
try
{
((JetspeedRunData) rundata).getCustomizedProfile().store();
}
catch (Exception e)
{
Log.error("Unable to save profile ",e);
}
}
//FIXME: this hack is due to the corrupted lifecycle of the portlet in the
//current API when caching is activated
try
{
org.apache.jetspeed.util.PortletSessionState.setPortletConfigChanged(p, rundata);
p.init();
}
catch (PortletException e)
{
Log.error("Customizer failed to reinitialize the portlet "+p.getName(), e);
}
// we're done, make sure clean up the
// session
doCancel(rundata, context);
}
catch (Exception e)
{
Log.error(e);
}
}
}