/* ====================================================================
* 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.util.template;
import org.apache.ecs.ConcreteElement;
import org.apache.ecs.StringElement;
import org.apache.turbine.util.Log;
import org.apache.turbine.util.RunData;
import org.apache.turbine.services.pull.ApplicationTool;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletSet;
import org.apache.jetspeed.portal.PortletControl;
import org.apache.jetspeed.services.PortalToolkit;
import org.apache.jetspeed.services.PortletFactory;
import org.apache.jetspeed.services.PsmlManager;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.jetspeed.services.Profiler;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.om.SecurityReference;
import org.apache.jetspeed.om.profile.PSMLDocument;
import org.apache.jetspeed.om.profile.Profile;
import org.apache.jetspeed.om.profile.ProfileException;
import org.apache.jetspeed.om.profile.Entry;
import org.apache.jetspeed.modules.ParameterLoader;
import org.apache.jetspeed.services.security.PortalResource;
import org.apache.jetspeed.om.registry.Parameter;
import org.apache.jetspeed.om.registry.PortletEntry;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.services.Registry;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.portal.security.portlets.PortletWrapper;
import java.util.Enumeration;
import java.util.Stack;
import java.util.Map;
/**
* Utility class for accessing Jetspeed in a "pull" mode
*
* <strong>Since the tool stores a RunData object, it may not be
* shared between threads and/or requests</strong>
*
* @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a>
* @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
* @author <a href="mark_orciuch@ngsltd.com">Mark Orciuch</a>
* @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
*
* @version $Id: JetspeedTool.java,v 1.31 2003/04/11 15:29:57 weaver Exp $
*/
public class JetspeedTool implements ApplicationTool
{
/** RunData object for this request */
protected JetspeedRunData rundata = null;
/** Empty constructor used by introspection
*/
public JetspeedTool()
{
}
/** The Tool constructor
*
* @param data the RunData object for the current request
*/
public JetspeedTool(RunData data)
{
this.rundata = (JetspeedRunData)data;
}
/**
* This will initialise a JetspeedTool object that was
* constructed with the default constructor (ApplicationTool
* method).
*
* @param data assumed to be a RunData object
*/
public void init(Object data)
{
this.rundata = (JetspeedRunData)data;
}
/**
* Refresh method - does nothing
*/
public void refresh()
{
// empty
}
/**
* Returns the portlet content customized for the current user.
* Currently, the layout engine does not handle panes, so the
* panes are mapped to real PSML files.
* If the pane name is null or "default", the profiler will automatically
* chose the PSML content, else the tool will try to load the PSML
* file with the specified name
*
* @param name the name of the pane to render
* @return the rendered content of the pane
*/
public ConcreteElement getPane(String name)
{
ConcreteElement result = null;
String msg = "";
if (null != rundata)
{
Profile profile = rundata.getProfile();
try
{
if (null == profile)
{
profile = Profiler.getProfile(rundata);
if (profile == null)
{
throw new ProfileException("Profile not found.");
}
rundata.setProfile(profile);
}
if ((name == null) || Profiler.DEFAULT_PROFILE.equals(name) || "".equals(name))
;
else
profile.setName(name);
PSMLDocument doc = profile.getDocument();
if (null != doc)
{
result = PortalToolkit.getSet(doc.getPortlets()).getContent(rundata);
}
}
catch (Exception e)
{
Log.warn("JetspeedTool.getPane: problem getting: "
+ name + " from current request's profile: " + e.toString());
msg = e.getMessage();
}
}
if (result == null)
{
result = new StringElement("Error retrieving Portal Page: " + msg);
}
return result;
}
/**
* Return the content of a named portlet. This portlet is sought in
* the current PSML resource.
*
* If a control is attached to the portlet description, returns the defined
* portlet and control, otherwise use the default control.
*
* Note: This will return the FIRST portlet with a name = name. Use getPortletById().
*
* @param name the name of the portlet to render
* @return the rendered content of the portlet
*
* @deprecated Use getPortletById()
*/
public ConcreteElement getPortlet(String name)
{
ConcreteElement result = null;
Portlet found = null;
Stack sets = new Stack();
sets.push(rundata.getProfile().getRootSet());
while ((sets.size() > 0) && (found==null))
{
PortletSet set = (PortletSet)sets.pop();
if (set.getName().equals(name))
{
found = set;
}
else
{
Enumeration en = set.getPortlets();
while((found==null) && en.hasMoreElements())
{
Portlet p = (Portlet)en.nextElement();
// unstack the controls to find the real PortletSets
Portlet real = p;
while (real instanceof PortletControl)
{
real = ((PortletControl)p).getPortlet();
}
if (real instanceof PortletSet)
{
// we'll explore this set afterwards
sets.push(real);
}
else if (p.getName().equals(name))
{
found = p;
}
}
}
}
if (found!=null)
{
result = found.getContent(rundata);
}
if (result==null)
{
//the customizer already streamed its content, return a stub
result = new ConcreteElement();
}
return result;
}
/** This method retrieves the appropriate customizer portlet for the
* current portlet
*
* @param p the portlet to customize
* @param data the RunData for this request
* @return the portlet object of the appropriate customizer
*/
public static Portlet getCustomizer(Portlet p)
{
Portlet customizer = p;
while (p instanceof PortletControl)
{
p = ((PortletControl)p).getPortlet();
}
// if the portlet cannot customize itself...
if ( !p.providesCustomization() )
{
//look for the customizer name in the portlet
//config (from Registry definition)
String name = p.getPortletConfig().getInitParameter("_customizer");
if (name == null)
{
String key = (p instanceof PortletSet)?"PortletSet":"Portlet";
name = JetspeedResources.getString("customizer."+key,key+"Customizer");
}
try
{
customizer = PortletFactory.getPortlet(name, p.getID()+"customize");
customizer.getPortletConfig()
.setPortletSkin(p.getPortletConfig().getPortletSkin());
PortletControl control = PortalToolkit.getControl((String)null);
if (control!=null)
{
control.setPortlet(customizer);
control.init();
return control;
}
}
catch (Exception e)
{
Log.error(e);
}
}
return customizer;
}
/** This method retrieves the appropriate information portlet for the
* current portlet
*
* @param p the portlet to display information about
* @param data the RunData for this request
* @return the portlet object of the appropriate customizer
*/
public static Portlet getPortletInfoPortlet(RunData data)
{
Portlet info = null;
String name = JetspeedResources.getString("PortletInfoPortlet.name", "PortletInfoPortlet");
try {
if (null != data)
{
JetspeedRunData jdata = (JetspeedRunData)data;
Profile profile = jdata.getProfile();
if (null == profile)
{
Log.warn("JetspeedTool: profile is null");
profile = Profiler.getProfile(jdata);
jdata.setProfile(profile);
}
Portlet source = findPortlet(data);
if (source != null) {
jdata.setPortlet(source.getName());
info = PortletFactory.getPortlet(name, "PortletInfoPortlet");
info.getPortletConfig().setPortletSkin(source.getPortletConfig().getPortletSkin());
PortletControl control = PortalToolkit.getControl((String)null);
if (control != null)
{
control.setPortlet(info);
control.init();
return control;
}
}
}
} catch (Exception e) {
Log.error(e);
}
return info;
}
/**
* Finds portlet identified by js_peid in the current user's profile
*
* @param rundata for this request
* @return portlet identified by js_peid
*/
private static Portlet findPortlet(RunData rundata) {
Portlet found = null;
JetspeedRunData jdata = (JetspeedRunData)rundata;
String peid = jdata.getJs_peid();
if (peid != null)
{
Stack sets = new Stack();
sets.push(jdata.getProfile().getRootSet());
while ((found==null) && (sets.size() > 0))
{
PortletSet set = (PortletSet)sets.pop();
if (set.getID().equals(peid))
{
found = set;
}
else
{
Enumeration en = set.getPortlets();
while((found==null) && en.hasMoreElements())
{
Portlet p = (Portlet)en.nextElement();
// unstack the controls to find the real PortletSets
Portlet real = p;
while (real instanceof PortletControl)
{
real = ((PortletControl)p).getPortlet();
}
if (real instanceof PortletSet)
{
if (real.getID().equals(peid))
{
found=real;
}
else
{
// we'll explore this set afterwards
sets.push(real);
}
}
else if (p.getID().equals(peid))
{
found = p;
}
}
}
}
}
return found;
}
/**
* Return the content of a portal element given the id of the element.
*
* @param id The portlet id
* @return the rendered content of the portlet
*/
public ConcreteElement getPortalElement(String id)
{
ConcreteElement result = null;
if (null != rundata)
{
Profile profile = rundata.getProfile();
try
{
if (null == profile)
{
System.out.println("profile is null");
profile = Profiler.getProfile(rundata);
rundata.setProfile(profile);
}
PSMLDocument doc = profile.getDocument();
if (null != doc)
{
Entry entry = doc.getEntryById(id);
if (null == entry)
{
// FIXME: need to write this function
// Portlets ps = doc.getPortletsById(id);
result = new StringElement("not implemented - PortletElement");
}
else
{
Portlet p = PortletFactory.getPortlet( entry );
if (p != null)
{
result = p.getContent(rundata);
}
else
result = new StringElement("Error retrieving PortletElement");
}
}
}
catch (Exception e)
{
Log.error(e);
}
}
if (result == null)
{
result = new StringElement("Error fetching pane");
}
return result;
}
/**
* Return the content of a portlet using the portlet's id (PEID). This portlet is sought in
* the current PSML resource.
*
* If a control is attached to the portlet description, returns the defined
* portlet and control, otherwise use the default control.
*
* @param peid the peid of the portlet to render
* @return the rendered content of the portlet
*/
public ConcreteElement getPortletById(String peid)
{
ConcreteElement result = null;
Portlet found = null;
Stack sets = new Stack();
sets.push(rundata.getProfile().getRootSet());
while ((sets.size() > 0) && (found==null))
{
PortletSet set = (PortletSet)sets.pop();
if (set.getID().equals(peid))
{
found = set;
}
else
{
Enumeration en = set.getPortlets();
while((found==null) && en.hasMoreElements())
{
Portlet p = (Portlet)en.nextElement();
// unstack the controls to find the real PortletSets
Portlet real = p;
while (real instanceof PortletControl)
{
real = ((PortletControl)p).getPortlet();
}
if (real instanceof PortletSet)
{
// we'll explore this set afterwards
sets.push(real);
}
else if (p.getID().equals(peid))
{
found = p;
}
}
}
}
if (found!=null)
{
// Return portlet's content checking the security first
result = PortletWrapper.wrap(found).getContent(rundata);
}
if (result==null)
{
//the customizer already streamed its content, return a stub
result = new ConcreteElement();
}
return result;
}
/**
* Return the content of a portlet using the portlet's name. This portlet is sought in
* the registry. This is useful when you want to get portlet's content without
* actually having the portlet in user's profile (for example, to preview a portlet
* before adding it to the profile).
* <P>
* If a control name is specified to the portlet description, returns the defined
* portlet and control, otherwise use the default control.
* <P>
* Issues to resolve:
* <UL>
* <LI>is new portlet instance created everytime someone previews the same portlet?</LI>
* <LI>should use the same skin as the current pane</LI>
* <LI>if TitlePortletControl is used, the action icons (max, min, etc) are not functional.
* Also, customize icon should not be present.</LI>
* <LI> interactive portlets (such as DatabaseBrowser) lose functionality (such as sorting
* in DatabaseBrowser).</LI>
* </UL>
*
* @param portletName
* Name of the portlet as defined in registry
* @param controlName
* Optional control name to use in displaying the portlet
* @return the rendered content of the portlet
*/
public ConcreteElement getPortletFromRegistry(String portletName, String controlName)
{
ConcreteElement result = null;
Portlet p = null;
try
{
// Always set portlet id to "preview" so each preview request gets it from the cache.
// At least, I think that's how it works.
p = PortletFactory.getPortlet(portletName, "preview");
PortletControl control = controlName == null ? PortalToolkit.getControl((String) null)
: PortalToolkit.getControl(controlName);
if (control != null)
{
JetspeedRunData jdata = (JetspeedRunData) rundata;
// Use the profile's skin
p.getPortletConfig().setPortletSkin(PortalToolkit.getSkin(jdata.getProfile().getDocument().getPortlets().getSkin()));
control.setPortlet(p);
control.init();
result = control.getContent(rundata);
}
else if (p != null)
{
result = p.getContent(rundata);
}
}
catch (Exception e)
{
Log.error(e);
result = new ConcreteElement();
}
if (result == null)
{
//the customizer already streamed its content, return a stub
result = new ConcreteElement();
}
return result;
}
/**
* Returns a parameter in its defined parameter style
*
* @param data for this request
* @param portlet portlet instance
* @param parmName parameter name
* @return current parameter value using specified presentation style
*/
public static String getPortletParameter(RunData data, Portlet portlet, String parmName)
{
if (portlet != null && parmName != null)
{
String parmValue = portlet.getPortletConfig().getInitParameter(parmName, "");
return getPortletParameter(data, portlet, parmName, parmValue);
}
return "";
}
/**
* Returns a parameter in its defined parameter style
*
* @param data for this request
* @param portlet portlet instance
* @param parmName parameter name
* @param parmValue current parameter value
* @return current parameter value using specified presentation style
*/
public static String getPortletParameter(RunData data, Portlet portlet, String parmName, String parmValue)
{
String result = null;
try
{
if (portlet != null && parmName != null)
{
// Retrieve registry entry and its parameter
PortletEntry entry = (PortletEntry) Registry.getEntry(Registry.PORTLET, portlet.getName());
Parameter param = entry.getParameter(parmName);
// Verify security for the parameter
boolean canAccess = JetspeedSecurity.checkPermission((JetspeedUser) data.getUser(),
new PortalResource(entry, param),
JetspeedSecurity.PERMISSION_CUSTOMIZE);
Map portletParms = portlet.getPortletConfig().getInitParameters();
String parmStyle = portlet.getPortletConfig().getInitParameter(parmName + ".style");
if (canAccess)
{
if (parmStyle != null)
{
result = ParameterLoader.getInstance().eval(data,
parmStyle,
parmName,
parmValue,
portletParms);
}
else
{
result = "<input type=\"text\" name=\"" + parmName + "\" value=\"" + parmValue + "\"";
}
}
else
{
// If security does not allow access to specific parameter, allow to provide a fallback parameter
String parmNameNoAccess = portlet.getPortletConfig().getInitParameter(parmName + ".style.no-access");
if (parmNameNoAccess != null)
{
if (Log.getLogger().isDebugEnabled())
{
Log.debug("JetspeedTool: access to parm [" + parmName + "] disallowed, redirecting to parm [" +
parmNameNoAccess + "]");
}
String parmStyleNoAccess = portlet.getPortletConfig().getInitParameter(parmNameNoAccess + ".style");
result = ParameterLoader.getInstance().eval(data,
parmStyleNoAccess,
parmNameNoAccess,
parmValue,
portletParms);
}
}
}
}
catch (Exception e)
{
Log.error(e);
}
return result;
}
/**
* Retreives the correct SecurityReference for the portlet based on the current
* profile and the request.
*/
public SecurityReference getSecurityReference(Entry entry)
{
return JetspeedSecurity.getSecurityReference(entry, rundata);
}
public int getSecuritySource(Entry entry)
{
return JetspeedSecurity.getSecuritySource(entry, rundata);
}
/**
* Retreives the Entry object for current portlet based on the
* "js_peid" parameter
*/
public Entry getEntryFromRequest() throws Exception
{
String jsPeid = rundata.getParameters().getString("js_peid");
Profile profile = Profiler.getProfile(rundata);
PSMLDocument doc = profile.getDocument();
return doc.getEntryById(jsPeid);
}
}