/*
* Copyright(C) NetStruxr, Inc. All rights reserved.
*
* This software is published under the terms of the NetStruxr
* Public Software License version 0.5, a copy of which has been
* included with this distribution in the LICENSE.NPL file. */
package er.extensions.components;
import java.util.Enumeration;
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOContext;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import er.extensions.crypting.ERXCrypto;
import er.extensions.eof.ERXEOEncodingUtilities;
import er.extensions.foundation.ERXStringUtilities;
/**
* This component can be used for two things:
* <ol>
* <li>Generating direct action urls for use in
* components that are being e-mailed to people.</li>
* <li>Support for encoding enterprise objects in
* the form values of generated urls.
* At the moment this component still contains some
* custy code that needs to be cleaned up before it
* can really be used, like adding the .wo and .api files ;0.</li>
* </ol>
* <h3>Synopsis:</h3>
* [actionClass=<i>anActionClass</i>];directActionName=<i>aDirectActionName</i>;[entityNameSeparator=<i>aSeparator</i>;]
* [relative=<i>aBoolean</i>;][shouldEncryptObjectFormValues=<i>aBoolean</i>;][objectsForFormValues=<i>anArray</i>;]
* [bindingDictionary=<i>aDictionary</i>;][unencryptedBindingDictionary=<i>aDictionary</i>;]
*
* @binding actionClass direct action class to be used
* @binding directActionName direct action name
* @binding entityNameSeparator separator used when constructing urls with encoded enterprise objects
* @binding relative generates relative or absolute url
* @binding shouldEncryptObjectFormValues boolean flag that tells if the primary keys
* of the enterprise objects should be encrypted using blowfish
* @binding objectForFormValue an enterprise object to be encoded in the url
* @binding objectsForFormValues array of enterprise objects to be encoded in the url
* @binding bindingDictionary adds the key-value pairs to generated url as
* form values, encrypting the values with blowfish.
* @binding unencryptedBindingDictionary adds the key-value pairs to generated url as
* form values
*/
public class ERXDirectActionHyperlink extends ERXStatelessComponent {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
// Class instances -------------------------------------------------
/** Key used to denote an adaptor prefix for a generated url string */
// MOVEME: ERXWOUtilities
public final static String ADAPTOR_PREFIX_MARKER="**ADAPTOR_PREFIX**";
/** Key used to denote a suffix for a generated url string */
// MOVEME: ERXWOUtilities
public final static String SUFFIX_MARKER="**SUFFIX**";
// Constructor -------------------------------------------------
/**
* Public constructor
* @param aContext a context
*/
public ERXDirectActionHyperlink(WOContext aContext) {
super(aContext);
}
// Component methods -------------------------------------------------
/**
* Cover method to return the binding: <b>entityNameSeparator</b>
* The entity name separator is used when constructing URLs with enterprise objects encoded in the url.
* This value default to the value defined in the system property <i>er.extensions.ERXDirectActionHyperlink.EntityNameSeparator</i> which defaults as well to the character '<pre>_</pre>'.
* @return returns the value for binding: <b>entityNameSeparator</b>
*/
public String entityNameSeparator() {
String separator = (String)valueForBinding("entityNameSeparator");
if (separator == null)
separator = ERXEOEncodingUtilities.entityNameSeparator();
return separator;
}
/**
* Cover method to return the boolean value
* of the binding: <b>relative</b>
* Defaults to <code>true</code>.
* @return returns if the generated url should be relative or not(absolute).
*/
public boolean relative() {
return booleanValueForBinding("relative", true);
}
/**
* Cover method to return the boolean value
* of the binding: <b>shouldEncryptObjectFormValues</b>
* Defaults to <code>false</code>.
* @return returns if the encoded objects' primary keys
* should be encrypted or not.
*/
public boolean shouldEncryptObjectFormValues() {
return booleanValueForBinding("shouldEncryptObjectFormValues");
}
/**
* Cover method to return the binding: <b>objectsForFormValues</b>
* This is an array of objects to be encoded as form values.
* @return returns bound array of objects to be encoded
*/
public NSArray objectsForFormValues() {
return(NSArray)valueForBinding("objectsForFormValues");
}
/**
* Cover method to return the binding: <b>objectsForFormValue</b>
* This is an enterprise object to be encoded as form values.
* @return returns bound enterprise object to be encoded
*/
public EOEnterpriseObject objectForFormValue() {
return(EOEnterpriseObject)valueForBinding("objectForFormValue");
}
/**
* Returns all of the objects to be encoded
* in the form values. Collects those bound
* to both 'objectsForFormValues' and
* 'objectForFormValue' into a single array.
* @return complete collection of objects to
* be encoded in form values.
*/
public NSArray allObjectsForFormValues() {
NSMutableArray objects = null;
if(hasBinding("objectsForFormValues") || hasBinding("objectForFormValue")) {
objects = new NSMutableArray();
if(objectsForFormValues() != null)
objects.addObjectsFromArray(objectsForFormValues());
if(objectForFormValue() != null)
objects.addObject(objectForFormValue());
}
return objects != null ? objects : NSArray.EmptyArray;
}
/**
* Generates an href for the given direct action based
* on all of the bindings. Currently it generates an
* absolute url starting with the key: ADAPTOR_PREFIX_MARKER.
* Before this href can be really useful it needs to
* be cleaned up.
* @return href containing all of the specification from
* the bindings.
*/
// FIXME: Lots of stuff to be fixed here.
public String href() {
String directActionName = null;
NSDictionary encryptedBindingDict = null;
NSDictionary unencryptedBindingDict = null;
NSArray formValuesObjects = null;
// Compose the direct action name from the bindings
// Typically, something like "DirectActionClass/actionMethod".
// Keep consistency with directActionName semantics as it is defined in directActionHref static method
if(hasBinding("actionClass")) {
StringBuilder daBuffer = new StringBuilder();
daBuffer.append(valueForBinding("actionClass"));
daBuffer.append('/');
daBuffer.append(valueForBinding("directActionName"));
directActionName = daBuffer.toString();
} else {
directActionName = (String)valueForBinding("directActionName");
}
if((directActionName == null) || (directActionName.length() == 0))
throw new IllegalArgumentException("ERXDirectActionHyperlink: directActionName must be specified.");
// Get the binding dictionaries
// FIXME: Rename binding to encryptedBindingDictionary
if(hasBinding("bindingDictionary"))
encryptedBindingDict = (NSDictionary)valueForBinding("bindingDictionary");
if(hasBinding("unencryptedBindingDictionary"))
unencryptedBindingDict = (NSDictionary)valueForBinding("unencryptedBindingDictionary");
// Get the objects to encode
if(allObjectsForFormValues().count() > 0)
formValuesObjects = allObjectsForFormValues();
// Compose and return the final url
return directActionHyperlink(context(),
shouldEncryptObjectFormValues(), formValuesObjects, entityNameSeparator(),
encryptedBindingDict, unencryptedBindingDict,
application().name(), directActionName,
relative(), null);
}
// Class methods -------------------------------------------------
/** Holds the application host url */
// MOVEME: This stuff might be better served if it was off of ERXApplication
private static String _applicationHostUrl;
/**
* This returns the value stored in the system properties:
* <b>ERApplicationHostURL</b> if this isn't set then a
* runtime exception is thrown. This property should be of
* the form: http://mymachine.com
* @return the application host url that should be used when
* complete urls are generated.
*/
// MOVEME: This stuff might be better served if it was off of ERXApplication
public static String applicationHostUrl() {
if(_applicationHostUrl ==null) {
// FIXME: Should be: er.extensions.ERXApplicationHostURL
_applicationHostUrl = System.getProperty("ERApplicationHostURL");
if(_applicationHostUrl==null || _applicationHostUrl.length()==0)
throw new RuntimeException("The ERApplicationHostURL default was empty -- please set it for the machine running the target application: it should look like http://mymachine.com");
}
return _applicationHostUrl;
}
public static String directActionHyperlink(WOContext context,
boolean encryptEos, NSArray eos, String entityNameSeparator,
NSDictionary encryptedDict, NSDictionary unencryptedDict,
String appName, String daName,
boolean relative, String suffix) {
StringBuffer result = new StringBuffer(ADAPTOR_PREFIX_MARKER);
result.append(WOApplication.application().applicationExtension());
result.append('/');
result.append(WOApplication.application().directActionRequestHandlerKey());
result.append('/');
result.append(daName);
result.append('?');
if(encryptedDict != null) {
NSArray allKeys = encryptedDict.allKeys();
for(Enumeration e = allKeys.objectEnumerator(); e.hasMoreElements();) {
String key =(String)e.nextElement();
String value = encryptedDict.objectForKey(key).toString();
ERXStringUtilities.appendSeparatorIfLastNot('&', '?', result);
result.append(key);
result.append('=');
result.append(ERXCrypto.crypterForAlgorithm(ERXCrypto.BLOWFISH).encrypt(value));
}
}
if(unencryptedDict != null) {
NSArray allKeys = unencryptedDict.allKeys();
for(Enumeration e = allKeys.objectEnumerator(); e.hasMoreElements();) {
String key =(String)e.nextElement();
String value = unencryptedDict.objectForKey(key).toString();
ERXStringUtilities.appendSeparatorIfLastNot('&', '?', result);
result.append(key);
result.append('=');
result.append(value);
}
}
if((eos != null) &&(eos.count() > 0)) {
ERXStringUtilities.appendSeparatorIfLastNot('&', '?', result);
result.append(ERXEOEncodingUtilities.encodeEnterpriseObjectsPrimaryKeyForUrl(eos, entityNameSeparator, encryptEos));
}
if(suffix != null)
result.append(SUFFIX_MARKER);
return completeURLFromString(result.toString(), context, appName, relative, suffix);
}
/**
* This method is useful for completing urls that are being generated
* in components that are going to be e-mailed to users. This method
* has the ability to substitute different application names which
* can be helpful if one application is generating the component, but
* the action of the url points to a different application on the
* same host.
* @param s href string to be completed
* @param c current context
* @param applicationName to be substituted if ADAPTOR_PREFIX_MARKER
* is present
* @param relative flag to indicate if the generated url should be
* relative or absolute in which case the applicationHostUrl
* will be used
* @param suffix string to be substitued if the SUFFIX_MARKER string
* is present
* @return complete url after substitutions have been made
*/
// MOVEME: ERXWOUtilities
public static String completeURLFromString(String s,
WOContext c,
String applicationName,
boolean relative,
String suffix) {
if(s!=null && s.indexOf(ADAPTOR_PREFIX_MARKER)!=-1) {
if(applicationName==null || applicationName.length()==0)
throw new RuntimeException("completeURLFromString: found ADAPTOR_PREFIX_MARKER and no application name to replace it - original text:"+s);
NSArray a=NSArray.componentsSeparatedByString(s, ADAPTOR_PREFIX_MARKER);
// BIG ASSUMPTION : the target application must have the same suffix as this application
String postFix=c.request().adaptorPrefix()+"/"+applicationName;
s= a.componentsJoinedByString(relative ? postFix : applicationHostUrl()+postFix);
}
if(s!=null && s.indexOf(SUFFIX_MARKER)!=-1) {
NSArray a=NSArray.componentsSeparatedByString(s, SUFFIX_MARKER);
// BIG ASSUMPTION : the target application must have the same suffix as this application
String postFix=suffix!=null ? suffix : "";
s= a.componentsJoinedByString(postFix);
}
return s;
}
}