toHtml to generate the javascript needed for the validation. * * Current validation functions are: *
* <?php
* require_once ( dirname ( dirname ( dirname ( __FILE__ ) ) ) . "/config.php" );
*
* $val = new Validator( 'testFormName' );
* $val->setMissingAlert( true );
*
* $val->addExists( 'Exists', 'Exists Description' );
* $val->addExists( 'Length1', 'Length1 Description' );
* $val->addLength( 'Length1', 'Length1 Description', 5, 5 );
* $val->addExists( 'Length2', 'Length2 Description' );
* $val->addLength( 'Length2', 'Length2 Description', 2, 6 );
* $val->addExists( 'Length3', 'Length3 Description' );
* $val->addLength( 'Length3', 'Length3 Description', 2 );
* $val->addExists( 'Length4', 'Length4 Description' );
* $val->addLength( 'Length4', 'Length4 Description', null, 5 );
* $val->addExists( 'Range1', 'Range1 Description' );
* $val->addRange( 'Range1', 'Range1 Description', 5, 5 );
* $val->addExists( 'Range2', 'Range2 Description' );
* $val->addRange( 'Range2', 'Range2 Description', 2, 6 );
* $val->addExists( 'Range3', 'Range3 Description' );
* $val->addRange( 'Range3', 'Range3 Description', 2 );
* $val->addExists( 'Range4', 'Range4 Description' );
* $val->addRange( 'Range4', 'Range4 Description', null, 5 );
* $val->addExists( 'Integer', 'Integer Description' );
* $val->addInteger( 'Integer', 'Integer Description' );
* $val->addExists( 'Email', 'Email Description' );
* $val->addEmail( 'Email', 'Email Description' );
* $val->addExists( 'Equal1', 'Equal Description' );
* $val->addEqual( 'Equal1', 'Equal2', 'Equal Description' );
* $val->addCopy( 'Copy1', 'Copy2' );
* $val->addExists( 'Copy1', 'Copy 1 descr' );
* $val->add24HrTime( '24HR', 'Time needs to be HH:MM' );
* $html = $val->toHtml();
* echo '<a href="javascript:void(0)" onclick="$(\'#code\').toggle()">Hide/Show Javascript Output</a><br/>';
* echo '<div id="code" style="display:none"><pre>' . htmlspecialchars( $html ) . '</pre></div>';
* echo '<a href="javascript:void(0)" onclick="$(\'#vars\').toggle()">Hide/Show GET Variables</a><br/>';
* if ( count ( $_GET ) > 0 ) {
* echo '<div id="vars" style="display:none"><pre>';
* print_r( $_GET );
* echo '</pre></div>';
* }
*
* echo $html;
* echo '<form name="testFormName" onSubmit="' . $val->onSubmit() . '">';
* echo 'Exists' . Tag::text( 'Exists', array ( 'value' => 'xyz' ) ) . '<br>';
* echo 'Length1' . Tag::text( 'Length1', array ( 'value' => '12345' ) ) . '<br>';
* echo 'Length2' . Tag::text( 'Length2', array ( 'value' => '123' ) ) . '<br>';
* echo 'Length3' . Tag::text( 'Length3', array ( 'value' => '12345' ) ) . '<br>';
* echo 'Length4' . Tag::text( 'Length4', array ( 'value' => '1234' ) ) . '<br>';
* echo 'Range1' . Tag::text( 'Range1', array ( 'value' => '5' ) ) . '<br>';
* echo 'Range2' . Tag::text( 'Range2', array ( 'value' => '5' ) ) . '<br>';
* echo 'Range3' . Tag::text( 'Range3', array ( 'value' => '4' ) ) . '<br>';
* echo 'Range4' . Tag::text( 'Range4', array ( 'value' => '4' ) ) . '<br>';
* echo 'Integer' . Tag::text( 'Integer', array ( 'value' => '10' ) ) . '<br>';
* echo 'Email' . Tag::text( 'Email', array ( 'value' => 'a@b.c' ) ) . '<br>';
* echo 'Equal1' . Tag::text( 'Equal1', array ( 'value' => 'A1' ) ) . '<br>';
* echo 'Equal2' . Tag::text( 'Equal2', array ( 'value' => 'A1' ) ) . '<br>';
* echo 'Copy1' . Tag::text( 'Copy1', array ( 'value' => 'BB' ) ) . '<br>';
* echo 'Copy2' . Tag::text( 'Copy2' ) . '<br>';
* echo '24Hr' . Tag::text( '24HR', array ( 'value' => '01:02' ) ) . '<br>';
* echo Tag::submit( 'Submit', 'submit' ) . '<br>';
* echo Response::factory()->toHidden(false);
* echo '</form>';
*
*/
class Validator extends \Jackbooted\Util\JB {
/**
* Enumerated list of functions that are available.
*/
const FN_EXISTS = 'EXISTS';
/**
* Function to check a range.
*/
const FN_RANGE = 'RANGE';
/**
* Function to check if the field is a valid integer.
*/
const FN_INTEGER = 'INTEGER';
/**
* Check to see if the field is a valid email.
*/
const FN_EMAIL = 'EMAIL';
/**
* Check to see if 2 fields are the same.
*/
const FN_EQUAL = 'EQUAL';
/**
* Copies one field to another if the second one is empty.
*/
const FN_COPY = 'COPY';
/**
* Validates if the field is a particular length or range.
*/
const FN_LENGTH = 'LENGTH';
/**
* Validates if the field is 24hr time.
*/
const FN_24HRTIME = '24HRTIME';
public static function factory ( $formName, $suffix='' ) {
return new Validator ( $formName, $suffix );
}
/**
* Name of the form in this document.
* @var String
*/
private $formName;
/**
* An array of all the form variables that will be tested.
* @see $add
* @var array
*/
private $testCases = [];
/**
* List of used functions.
*
* This is used so we
* do not generate javascript that we don't need.
* @var array
*/
private $usedFunct = [];
/**
* This variable will alert the user to any form variables that are missing.
*
* This is not used in anything other than testing.
* @var array
*/
private $alertOnMissingFormVar = FALSE;
/**
* @var int is a unique name for the javascript. Automatically generated.
*/
private $id;
private $headerJS;
private $existsJS;
private $emailJS;
private $integerJS;
private $validateHeaderJS;
private $testCaseHeaderJS;
private $caseExistsJS;
private $caseLenEqJS;
private $caseLenBetweenJS;
private $caseLenGTJS;
private $caseLenLTJS;
private $caseIntegerJS;
private $caseRangeBetweenJS;
private $caseRangeGTJS;
private $caseRangeLTJS;
private $caseEmailJS;
private $caseEqualJS;
private $caseCopyJS;
private $caseAlertMissingJS;
private $validateFooterJS;
private $validateFunctionJS;
private $case24HrTimeJS;
private $t24HrTimeJS;
/**
* Creates the Validation object.
*
* Requires the form name.
*
* @param string $formName The name of the form that will be validated.
* @param string $suffix The suffix will give the unique identifier if there are a number of
* validators on a page. The uniquie suffix is automatically generated based on number of
* invokations of the form. This does not work on ajax late generated forms
* so for ajax, supply a unique suffix
*
* @since 1.0
*/
public function __construct ( $formName, $suffix='' ) {
parent::__construct();
if ( $suffix == '' ) {
$suffix = Invocation::next();
}
$this->formName = $formName;
$this->id = '_' . $suffix;
$this->setUpJavaScriptFunctions ();
}
/**
* Sets the variable alertOnMissingFormVar.
*
* This variable controlles
* is there is a message displayed if the javascript is given an
* invalid form variable name to test.
*
* @param boolean $state The state of this variable.
*
* @since 1.0
* @return void
*/
public function setMissingAlert ( $state ) {
$this->alertOnMissingFormVar = $state;
return $this;
}
/**
* Add a test for this form variable.
*
* Tests for Existance.
*
* @param string $fv Form Variable name.
* @param string $desc A message if the test fails.
*
* @since 1.0
* @return void
*/
public function addExists ( $fv, $desc ) {
return $this->add ( $fv, $desc, self::FN_EXISTS );
}
/**
* Add a test for this form variable.
*
* Tests for a length.
* if $minLength is set and $maxLength then the test will ensure that
* the field is at least $minLength long. The same goes for $maxLength set and
* $minLength not set. The field can be a maximum of maxLength long
* If they are both set then the field must ve between the 2 lengths
* If they are the same then the field must be exactly that length.
* If both $minLength and $maxLength are null then no tests are performed.
*
* @param string $fv Form Variable name.
* @param string $desc A message if the test fails.
* @param integer $minLength Minimum length of string.
* @param integer $maxLength Maximum length of string.
*
* @since 1.0
* @return void
*/
public function addLength ( $fv, $desc, $minLength=null, $maxLength=null, $zeroOk='false' ) {
return $this->add ( $fv, $desc, self::FN_LENGTH, [ $minLength, $maxLength, $zeroOk ] );
}
/**
* Add a test that a field is within a particular range.
*
* The range works in
* the same sort of way as the length checking. If both variables are set then the
* field value must be between the max and min. If the min is set and max not set then
* the value must be greater than min, and visa-versa
* If both $mn and $mx are null then no tests are performed.
*
* @param string $fv Form Variable name.
* @param string $desc A message if the test fails.
* @param float $mn Minimum value of the field.
* @param float $mx Maximum value of the field.
*
* @since 1.0
* @return void
*/
public function addRange ( $fv, $desc, $mn=null, $mx=null ) {
return $this->add ( $fv, $desc, self::FN_RANGE, [ $mn, $mx ] );
}
/**
* Add a test for this form variable.
*
* Tests for value being an integer
* ensures that the field contains a valid integer. Note that empty is valid as well
* Usually you check for existance before you test for integer.
*
* @param string $fv Form Variable name.
* @param string $desc A message if the test fails.
*
* @since 1.0
* @return void
*/
public function addInteger ( $fv, $desc ) {
return $this->add ( $fv, $desc, self::FN_INTEGER );
}
/**
* Add a test for this form variable.
*
* Tests for valid email
* Note that empty is valid. That means that you must check for
* Existance as well as email.
*
* @param string $fv The form variable name to test.
* @param string $desc The description of the error.
*
* @since 1.0
* @return void
*/
public function addEmail ( $fv, $desc ) {
return $this->add ( $fv, $desc, self::FN_EMAIL );
}
/**
* Add a test for this form variable.
*
* Tests for valid 24 hour time
*
* @param string $fv The form variable name to test.
* @param string $desc The description of the error.
*
* @since 2.0
* @return void
*/
public function add24HrTime ( $fv, $desc, $defaultTime='' ) {
$this->usedFunct[self::FN_INTEGER] = 'YES';
return $this->add ( $fv, $desc, self::FN_24HRTIME, $defaultTime );
}
/**
* Add a test for this form variable.
*
* Tests for 2 form variables
* are equal. This is particually useful for passwords
*
* @param string $fv1 First form variable to check.
* @param string $fv2 Other form variable to test againse.
* @param string $desc Message if the variables are not the same.
*
* @since 1.0
* @return void
*/
public function addEqual ( $fv1, $fv2, $desc ) {
return $this->add ( $fv1, $desc, self::FN_EQUAL, $fv2 );
}
/**
* Add a test for this form variable.
*
* Tests if a form variable
* is empty. If it is then it copies the value from $fv1 into $fv2
* This would be useful for say prefered name.
*
* @param string $fv1 Source Form Variable.
* @param string $fv2 Destination Form Variable.
*
* @since 1.0
* @return void
*/
public function addCopy ( $fv1, $fv2 ) {
return $this->add ( $fv1, '', self::FN_COPY, $fv2 );
}
/**
* Generic function for adding tests.
*
* This is only called internally.
*
* @param string $fv Form Variable.
* @param string $desc Description for this test.
* @param enum $t Test type.
* @param string $xtra Extra information.
*
* @since 1.0
* @return void
*/
private function add ( $fv, $desc, $t, $xtra=NULL ) {
$this->testCases[] = [ 'NAME' => $fv,
'DESC' => $desc,
'TEST' => $t,
'XTRA' => $xtra ];
$this->usedFunct[$t] = 'YES';
return $this;
}
/**
* Generates HTML and Javascript to do the tests on this form.
*
* @since 1.0
* @return string
*/
public function toHtml () {
$msg = $this->headerJS;
foreach ( $this->usedFunct as $key => $val ) {
$msg .= $this->addJSFunctions ( $key );
}
// Then create the validation function that will test all the
// different form variables
$msg .= sprintf ( $this->validateHeaderJS, $this->formName );
foreach ( $this->testCases as $val ) {
$nam = $val['NAME'];
$desc = $val['DESC'];
$xtra = $val['XTRA'];
// Test is in java and check if the form variable exists
// Ensures the Javascript does not crash on
// missing Form Variables
$msg .= sprintf ( $this->testCaseHeaderJS, $nam );
$msg .= $this->createCaseJSTests ( $val, $xtra, $desc );
// End of the if test that check if the form var exists
$msg .= sprintf ( $this->validateFooterJS, $this->formName );
// Check if we are notifying the user on missing form vars
if ( $this->alertOnMissingFormVar ) {
$msg .= sprintf ( $this->caseAlertMissingJS, $nam );
}
}
$msg .= sprintf ( $this->validateFunctionJS, $this->formName );
return JS::library ( JS::JQUERY ) .
JS::javaScript ( $msg );
}
private function createCaseJSTests ( $val, $xtra, $desc ) {
// Output the javascript to do the different checks
switch ( $val['TEST'] ) {
case self::FN_EXISTS:
return sprintf ( $this->caseExistsJS, $desc );
case self::FN_LENGTH:
return $this->lengthJSCases ( $xtra, $desc );
case self::FN_INTEGER:
return sprintf ( $this->caseIntegerJS, $desc );
case self::FN_RANGE:
return $this->rangeJSCases ( $xtra, $desc );
case self::FN_EMAIL:
return sprintf ( $this->caseEmailJS, $desc );
case self::FN_24HRTIME:
return sprintf ( $this->case24HrTimeJS, $desc, $val['XTRA'], $val['XTRA'] );
case self::FN_EQUAL:
return sprintf ( $this->caseEqualJS, $val['XTRA'], $desc );
case self::FN_COPY:
return sprintf ( $this->caseCopyJS, $val['XTRA'], $desc );
default:
return '';
}
}
private function rangeJSCases ( $xtra, $desc ) {
if ( $xtra[0] != NULL && $xtra[1] != NULL ) {
return sprintf ( $this->caseRangeBetweenJS, $xtra[0], $xtra[1], $desc );
}
else if ( $xtra[0] != NULL ) {
return sprintf ( $this->caseRangeGTJS, $xtra[0], $desc );
}
else if ( $xtra[1] != NULL ) {
return sprintf ( $this->caseRangeLTJS, $xtra[1], $desc );
}
else {
return '';
}
}
private function lengthJSCases ( $xtra, $desc ) {
if ( $xtra[0] != NULL && $xtra[1] != NULL ) {
if ( $xtra[0] == $xtra[1] ) {
return sprintf ( $this->caseLenEqJS, $xtra[2], $xtra[0], $desc );
}
else {
return sprintf ( $this->caseLenBetweenJS, $xtra[2], $xtra[0], $xtra[1], $desc );
}
}
else if ( $xtra[0] != NULL ) {
return sprintf ( $this->caseLenGTJS, $xtra[2], $xtra[0], $desc );
}
else if ( $xtra[1] != NULL ) {
return sprintf ( $this->caseLenLTJS, $xtra[2], $xtra[1], $desc );
}
else {
return '';
}
}
private function addJSFunctions ( $key ) {
switch ( $key ) {
case self::FN_EXISTS: // Output the javascript functions for Existance
return $this->existsJS;
case self::FN_EMAIL: // Output the javascript functions for email
return $this->emailJS;
case self::FN_24HRTIME: // Output the javascript functions for time validation
return $this->t24HrTimeJS;
case self::FN_INTEGER:
$ret = $this->integerJS;
$this->integerJS = '';
return $ret;
default:
return '';
}
}
/**
* Creates the javascript that can be included in attribute that will validate the form.
*
* Typically this would be used in the creation of the form so that it does the validation.
*
* echo '<form onSubmit="' . $val->onSubmit () . '">';
*
*
* @param string $js Additional javascript.
*
* @since 1.0
* @return string
*/
public function onSubmit ( $js='' ) {
return 'if(!' . $this->jsValidate() . ')return false;' . $js .'return true;';
}
/**
* Returns the javascript that is just used for doing the validation.
*
* Typically this would be used in the javascript when you have to integrate with other validation.
*
* <script language="JavaScript">
* function saveClicked() {
* if ( ! <?php echo $valid->jsValidate(); ?> ) {
* return;
* }
* var f = document.forms[0];
* if (f['issues[]'].selectedIndex < 0) {
* if (!confirm('There is no issue selected.')) {
* return;
* }
* }
* top.restoreSession();
* f.submit();
* }
* </script>
*
*
* @since 2.0
* @return string
*/
public function jsValidate ( ) {
return "validateForm{$this->id}()";
}
private function setUpJavaScriptFunctions () {
$this->headerJS = <<