/*
* Copyright (C) 2000 - 2008 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfm.cfform;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.engine.dataNotSupportedException;
import com.naryx.tagfusion.cfm.tag.cfTag;
abstract public class cfAbstractFormTag extends cfTag implements Serializable
{
static final long serialVersionUID = 1;
private static Set<String> supportedValidation;
static{
supportedValidation = new HashSet<String>();
supportedValidation.add( "date" );
supportedValidation.add( "usdate" );
supportedValidation.add( "eurodate" );
supportedValidation.add( "time" );
supportedValidation.add( "float" );
supportedValidation.add( "numeric" );
supportedValidation.add( "integer" );
supportedValidation.add( "range" );
supportedValidation.add( "boolean" );
supportedValidation.add( "telephone" );
supportedValidation.add( "zipcode" );
supportedValidation.add( "creditcard" );
supportedValidation.add( "ssn" );
supportedValidation.add( "social_security_number" );
supportedValidation.add( "email" );
supportedValidation.add( "url" );
supportedValidation.add( "guid" );
supportedValidation.add( "uuid" );
supportedValidation.add( "maxlength" );
supportedValidation.add( "noblanks" );
supportedValidation.add( "regex" );
supportedValidation.add( "regular_expression" );
supportedValidation.add( "submitonce" );
}
protected void appendAttributes( cfSession _Session, StringBuilder _buffer, Set<String> _ignoreKeys ) throws cfmRunTimeException{
Iterator<String> keys = this.properties.keySet().iterator();
while ( keys.hasNext() ){
String nextKey = keys.next();
if ( !_ignoreKeys.contains( nextKey ) ){
appendAttribute( _Session, nextKey, _buffer );
}
}
}
protected void appendAttribute( cfSession _Session, String _name, StringBuilder _buffer ) throws dataNotSupportedException, cfmRunTimeException {
if ( containsAttribute( _name ) ){
_buffer.append( ' ' );
_buffer.append( _name.toLowerCase() );
_buffer.append( "=\"" );
_buffer.append( com.nary.util.string.escapeHtml( getDynamic(_Session, _name ).getString() ) );
_buffer.append( "\"" );
}
}
protected void applyValidation( cfSession _Session, cfFormInputData _formData, String _name, String _type, StringBuilder _onBlurValue ) throws dataNotSupportedException, cfmRunTimeException{
String message = getDynamic( _Session, "MESSAGE" ).getString();
String onError = getValue( _Session, "ONERROR" );
List<String> onBlurList = null;
//--[ where does the validation need to occur
boolean onBlur = false;
boolean onSubmit = false;
boolean onServer = false;
String validateAt = "onsubmit";
if ( containsAttribute( "VALIDATEAT" ) ){ // note we could default it in the defaultParameters but this was added in BD 7
validateAt = getDynamic( _Session, "VALIDATEAT" ).getString().toLowerCase();
}
// the VALIDATEAT attribute specifies where the validation is performed - on form submission, onblur or at the server
// again multiple options can be specified as a comma separated list
List<String> validateAtVals = com.nary.util.string.split( validateAt, ',' );
for ( int i = 0; i < validateAtVals.size(); i++ ){
String nextValidateAt = validateAtVals.get( i ).toString();
if ( nextValidateAt.equals( "onsubmit" ) ){
onSubmit = true;
}else if ( nextValidateAt.equals( "onblur" ) ){
onBlur = true;
onBlurList = new ArrayList<String>();
}else if ( nextValidateAt.equals( "onserver" ) ){
onServer = true;
}else{
throw newRunTimeException( "Unsupported VALIDATEAT value: " + nextValidateAt + ". Supported values are onblur, onserver and onsubmit" );
}
}
boolean isRequired = getRequired(_Session );
//--[ if form element is required, append check
if ( isRequired ){
if ( onSubmit ){
_formData.formTagRequired( _name, "_" + _type, message, onError );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORMREQUIRED", message );
}
}
if ( containsAttribute("ONVALIDATE") ){
// call custom validation function
_formData.addValidateCheck( _name, getDynamic( _Session, "ONVALIDATE" ).getString(), null, message, onError );
}else if ( containsAttribute("VALIDATE") ){
// multiple validate options can be specified as a comma separated liste
String validate = getDynamic(_Session, "VALIDATE").getString().toLowerCase();
List<String> validateVals = com.nary.util.string.split( validate, ',' );
String onValidate = getValue(_Session, "ONVALIDATE");
for ( int i = 0; i < validateVals.size(); i++ ){
//--[ check that there exists a function for the given validate choice
String nextValidate = (String) validateVals.get( i );
if ( nextValidate.equals( "submitonce" ) ){
// only relevent when TYPE is SUBMIT
if ( onSubmit ){
_formData.setSubmitOnce();
}
}else if ( nextValidate.equals( "maxlength" ) ){
int maxLength = 0;
if ( containsAttribute( "MAXLENGTH" ) ){
maxLength = getDynamic( _Session, "MAXLENGTH" ).getInt();
}
if ( onSubmit ){
_formData.addMaxLengthCheck( _name, onValidate, nextValidate, message, onError, maxLength );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORMMAXLENGTH", String.valueOf( maxLength ) );
}
if ( onBlur ){
onBlurList.add( "!tf_validate_maxlength( this," + maxLength + " )" );
}
}else if ( nextValidate.equals("range") ){ // range checking
// first check the value is a numeric
if ( onSubmit ){
_formData.addValidateCheck( _name, onValidate, "numeric", message, onError );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORMNUMERIC", message );
}
// get the min/max limits of the RANGE
if ( containsAttribute( "RANGE" ) ){
String minRange = null;
String maxRange = null;
String range = getDynamic(_Session, "RANGE").getString();
if ( range.length() > 0 ){
int c1 = range.indexOf(",");
if ( c1 != -1 ){
// check if there are more than 2 in the list
if ( range.indexOf( ',', c1+1 ) != -1 ){
throw newRunTimeException( "Invalid RANGE attribute value. Only a minimum and maximum may be listed." );
}
if ( c1 == 0 ){ // only max specified e.g. ",10"
maxRange = range.substring( 1 );
}else if ( c1 == range.length()-1 ){ // only min specified e.g. "1,"
minRange = range.substring( 0, range.length()-1 );
}else{// both min and max specified e.g. "1,10"
minRange = range.substring(0,c1);
maxRange = range.substring(c1+1);
}
}else{
minRange = range;
}
// check that the range values are integers
rangeCheck( minRange );
rangeCheck( maxRange );
}
if ( onSubmit ){
_formData.addRangeCheck( _name, onValidate, nextValidate, message, onError, minRange, maxRange );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORMRANGE", ( minRange != null ? "MIN=" + minRange : "" ) + ( maxRange != null ? " MAX=" + maxRange : "" ) );
}
if ( onBlur ){
onBlurList.add( "!tf_validate_range( this," + minRange + "," + maxRange + " )" );
}
}else{
throw newRunTimeException( "The PATTERN attribute must be specified when VALIDATE='regular_expression'." );
}
}else if ( nextValidate.equals( "regular_expression" ) || nextValidate.equals( "regex" ) ){ // regular expression based validation
if ( containsAttribute( "PATTERN" ) ){
String pattern = getDynamic( _Session, "PATTERN" ).getString();
if ( onSubmit ){
_formData.addRegExpCheck( _name, onValidate, "regular_expression", message, onError, pattern );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORMREGEX", pattern );
}
if ( onBlur ){
onBlurList.add( "!tf_validate_regular_expression( this, /" + pattern + "/ )" );
}
}else{
throw newRunTimeException( "The PATTERN attribute must be specified when VALIDATE='regular_expression'." );
}
}else if ( supportedValidation.contains( nextValidate ) ){ // the rest of the validate options can be handled in the same manner
if ( nextValidate.equals( "ssn" ) ){
nextValidate = "social_security_number";
}
if ( onSubmit ){
_formData.addValidateCheck( _name, onValidate, nextValidate, message, onError );
}
if ( onServer ){
addHiddenField( _formData, _name + "_CFFORM" + nextValidate.toUpperCase(), message );
}
if ( onBlur ){
onBlurList.add( "!tf_validate_" + nextValidate + "( this )" );
}
}else{
throw newRunTimeException( "invalid VALIDATE parameter, " + nextValidate );
}
}
}
//--[ if onBlur we need to append the required checks
if ( onBlur && ( isRequired || onBlurList.size() > 0 ) ){
_onBlurValue.append( "if ( " );
if ( isRequired ){ // call the 'is required' function
_onBlurValue.append( "!tf_element_has_value( this )" );
if ( onBlurList.size() > 0 ){
_onBlurValue.append( " && ( " );
}
}
for ( int i = 0; i < onBlurList.size(); i++ ){
_onBlurValue.append( onBlurList.get(i).toString() );
if ( i < onBlurList.size()-1 ){
_onBlurValue.append( " || " );
}
}
if ( isRequired && onBlurList.size() > 0 ){
_onBlurValue.append( " ) " );
}
// add consequenc of 'if' evaluating to true - display error message
_onBlurValue.append( "){ alert(\'" );
_onBlurValue.append( message );
_onBlurValue.append( "\'); }" );
}
}
private void rangeCheck( String _rangeVal ) throws cfmRunTimeException{
if ( _rangeVal != null ){
try{
Integer.parseInt( _rangeVal );
}catch( NumberFormatException e ){
throw newRunTimeException( "Invalid RANGE attribute value. Failed to convert \"" + _rangeVal + "\" to an integer value" );
}
}
}
private void addHiddenField( cfFormInputData _formData, String _name, String _value ){
_formData.appendToFooter( "<input type=\"hidden\" name=\"" + _name + "\" value=\"" + _value + "\"/>\r\n");
}
protected boolean getRequired( cfSession _Session ) throws cfmRunTimeException {
boolean required = false;
if ( containsAttribute("REQUIRED") ){
cfData reqd = getDynamic(_Session, "REQUIRED");
if ( reqd.getString().length() == 0 || reqd.getBoolean() ){
required = true;
}
}
return required;
}
protected String getValue( cfSession _Session, String _attr ) throws cfmRunTimeException{
if ( containsAttribute( _attr ) ){
return getDynamic( _Session, _attr ).getString();
}else{
return null;
}
}
protected Set<String> getIgnoreKeys(){
Set<String> ignoreKeys = new HashSet<String>();
ignoreKeys.add( "BIND" ); // flash only
ignoreKeys.add( "TYPE" ); // already read
ignoreKeys.add( "NAME" ); // already read
ignoreKeys.add( "MASK" );
ignoreKeys.add( "MESSAGE" );
ignoreKeys.add( "RANGE" );
ignoreKeys.add( "REQUIRED" );
ignoreKeys.add( "LABEL" );
ignoreKeys.add( "VALIDATE" );
ignoreKeys.add( "VALIDATEAT" );
ignoreKeys.add( "MESSAGE" );
ignoreKeys.add( "PATTERN" );
ignoreKeys.add( "ONERROR" );
ignoreKeys.add( "ONVALIDATE" );
ignoreKeys.add( "PASSTHROUGH" );
return ignoreKeys;
}
}