Solved

limit form entry to once per day

Posted on 2011-09-22
40
814 Views
Last Modified: 2012-05-12
I am trying to limit form entries to once per day by searching table for records with same email and submit date of today:

table name: customer
field name: email
field name: submitdate

The existing application uses a series of includes so I was hoping to add a "function" or include that does this validation, terminates the form submission and takes user to an error page if there is already a record for that day.

I am having no success with the following code (among other things, there might be a date formatting issue because table contains date like: 2011-09-11 20:38:31 so I guess it would have to trim that to find a match)

// Get a specific result from the database_table
$result = mysql_query("SELECT * FROM contact_requests
WHERE email='$email'") or die(mysql_error());

// get the first and loop until all entries are found with the above result
while($row = mysql_fetch_array($result))
// compare the entry from the form to the result of the sql_table & the date of entry into table
if($email == $row['email'] && $submitdate == $row['DATETIME'])
{
header('Location: error-duplicate-entry.php');
exit;
}

I am trying to add the code to an existing class include but I'm an amateur... please help...
0
Comment
Question by:web5dev7
  • 19
  • 16
  • 5
40 Comments
 
LVL 3

Accepted Solution

by:
dkellner earned 350 total points
ID: 36582540
Try this:

$q = mysql_query("
   select count(1)from contact_requests
   where email='$email' and substr(datetime,1,10)=substr(now(),1,10)
");
$result = mysql_fetch_array($q);
$foundSomething = $result[0];
if($foundSomething) {
...

}

With this query you'll go faster - SQL will only return if there is a row like that or not.
Also, the date formatting problem is handled with those substr functions.
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 36583301
This is an ISO-8601 DATETIME string: 2011-09-11 20:38:31.  You can learn more about how to use DATETIME information here:
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_201-Handling-date-and-time-in-PHP-and-MySQL.html

You retrieve the date portion of the DATETIME string like this:

$date_only = date('Y-m-d', strtotime($iso_datetime));

You find matching dates in the data base with a query that says, "SELECT my_thing FROM my_table WHERE my_date LIKE '$date_only%'"
0
 

Author Comment

by:web5dev7
ID: 36583728
dkellner,

the date field in my table is named "submitdate"

so regarding: substr(datetime,1,10)

does it need to be (submitdate,1,10)

??
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 36584052
It might look something like this:

if($email == $row['email'] && $submitdate == substr($row['submitdate'], 0, 10)

But really, you would be so much better off if you just follow the tried-and-true methods of handling DATETIME strings.  They work, they are peer-reviewed, and they are designed with a lot of deep background knowledge that helps you avoid errors like this one:

(submitdate,1,10)

That structure will cut off the first digit of the year.

0
 

Author Comment

by:web5dev7
ID: 36584278
Ray,

re: methods of handling DATETIME strings

in practical terms, how do I apply that to my case - can you be specific?

p.s. I tried to digest the info you linked to - but the overload confused me. What I have most trouble with is putting it to practice with proper syntax so it works.
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 36584501
Please show us your test data.  If you don't have that, please create some.  Associate each test input with a good output.  If you can give us that kind of information we can help you bridge the gap from input to output with code!
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36584520
web5dev7,

YES, correct.  But only if you do it from SQL.  substr works differently, or, more precisely, strings work differently in PHP and xxSQL.  PHP starts numbering the characters from zero (that's why you got a comment like "1,10 will cut off the first digit" which would be true for PHP), SQL starts with 1.  So, yes, substr(submitdate,1,10) will do.
0
 

Author Comment

by:web5dev7
ID: 36584523
Here's two versions I've been trying with no luck. What am I doing wrong  ?

//limit one entry per day - version 1

function oneaday()
{
// Get a specific result from the database_table
$result = mysql_query("SELECT * FROM contact_requests
WHERE email='$email'") or die(mysql_error());

// get the first and loop until all entries are found with the above result
while($row = mysql_fetch_array($result))
// compare the entry from the form to the result of the sql_table & the date of entry into table
if($email == $row['email'] && $submitdate == substr($row['submitdate'], 0, 10)
{
header('Location: error-duplicate-entry.php');
exit;
}
}

//limit one entry per day - version 2

function oneaday()
{
   $q = mysql_query("
   select count(1)from contact_requests
   where email='$email' and substr(datetime,1,10)=substr(now(),1,10)
");
$result = mysql_fetch_array($q);
$foundSomething = $result[0];
if($foundSomething) {
header('Location: error-duplicate-entry.php');
exit;
}
}
0
 

Author Comment

by:web5dev7
ID: 36584582
ok, I replaced substr(datetime,1,10) with substr(submitdate,1,10) but...

version 1 gives me a system submission error (not the duplicate entry error I am hoping for) so implies syntax error

version 2 submits successfully (so apparently not finding the other 20 records with my email dated today) so implies the code format is ok but it's not doing what it's supposed to.

here's the table structure which contains mulltiple records for today with my email address
:
id      first        last               email              terms       submitdate
23       Joe        Smith       Joe@joe.com     y                 2011-09-22 15:04:31

0
 
LVL 3

Expert Comment

by:dkellner
ID: 36584604
?...  (Surprised wtf icon here)
Pls try the query itself and post the error message.
0
 

Author Comment

by:web5dev7
ID: 36585072
I'm just a slow learner but I'm trying...

Using phpMyadmin here is what I get:

SELECT count( 1 )
FROM contact_requests
WHERE email = Joe@jo.com'
AND substr( added_on, 1, 10 ) = substr( now( ) , 1, 10 )
LIMIT 0 , 30

I get a result of 12 - which seems correct because I have 12 records with my email dated today.

If that means that the form validation should be working - then maybe the problem is where I am placing the code?
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36585176
Do a print_r($result) for me in version 2 please. Then, if it shows Array( [0]=>12 ), try to do an alert before your redirecting header. Let's divide the process to smaller steps so that we see which part is not working!
0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 150 total points
ID: 36586704
This kind of problem is only overwhelming if you try to learn it too fast.  Norvig is a senior scientist at Google.  Read what he says about it.
http://norvig.com/21-days.html

The script posted below is available for you to see in action on my server here:
http://www.laprbass.com/RAY_temp_web5dev7.php

The creation of test data is an absolutely vital part of programming, which explains why I created some test data in this script.  Here is the annotation.

Line 7-9 Example of the test data from this post at EE
Line 13-25 Required reading - you must understand this material
Line 30 Look up the function so you understand it and substitute your values
Line 34-61 Connect and Select, with appropriate error handling
Line 65-85 Create the test data table.
Line 89-92 Create different DATETIME strings for use in the test data
Line 96-127 Load the test data into the table
Line 138-155 Visualize the test data so we know if we are using a good test data set
Line 159-184 Make a test and visualize the result
Line 188-213 Make another test and visualize the result

Now, having said all that, there is a thing in MySQL called UNIQUE.  You can make a UNIQUE index on any column or combination of columns.  So you might consider using a process like this...

1. Normalize the email address by trim() and strtoupper()
2. Normalize the date with date('Y-m-d', strtotime( -- external client input -- ))
3. ALTER TABLE to make a UNIQUE index on the email and submitdate columns

Once you have that UNIQUE index in place, MySQL will throw error number 1062 if you try to insert duplicate data into the columns.  Your script can trap this error and thereby know that this is a duplicate entry.  Of course this will not prevent people from using multiple email addresses to repeat the registration process, but that is not a part of this technical question, it's more of a business requirements question.

You might enjoy this book.  It will help you get some foundation in how PHP and MySQL work together.   Very readable and great examples.  It has been a part of my professional library since its first printing.
http://www.sitepoint.com/books/phpmysql4/

Best of luck with your project, ~Ray

<?php // RAY_temp_web5dev7.php
error_reporting(E_ALL);
echo "<pre>";



// TEST DATA FROM THE QUESTION AT EE
// id      first      last        email           terms       submitdate
// 23      Joe        Smith       Joe@joe.com     y           2011-09-22 15:04:31



// THE ABSOLUTE MINIMUM YOU MUST UNDERSTAND TO USE PHP AND MYSQL
// MAN PAGE: http://php.net/manual/en/ref.mysql.php
// MAN PAGE: http://php.net/manual/en/mysql.installation.php
// MAN PAGE: http://php.net/manual/en/function.mysql-connect.php
// MAN PAGE: http://php.net/manual/en/function.mysql-select-db.php
// MAN PAGE: http://php.net/manual/en/function.mysql-real-escape-string.php
// MAN PAGE: http://php.net/manual/en/function.mysql-query.php
// MAN PAGE: http://php.net/manual/en/function.mysql-errno.php
// MAN PAGE: http://php.net/manual/en/function.mysql-error.php
// MAN PAGE: http://php.net/manual/en/function.mysql-num-rows.php
// MAN PAGE: http://php.net/manual/en/function.mysql-fetch-assoc.php
// MAN PAGE: http://php.net/manual/en/function.mysql-fetch-array.php
// MAN PAGE: http://php.net/manual/en/function.mysql-insert-id.php



// REQUIRED FOR PHP 5.1+
date_default_timezone_set('America/Chicago');



// CONNECTION AND SELECTION VARIABLES FOR THE DATABASE
$db_host = "localhost"; // PROBABLY THIS IS OK
$db_name = "??";        // GET THESE FROM YOUR HOSTING COMPANY
$db_user = "??";
$db_word = "??";

// LIVE DATABASE CREDENTIALS
require_once('RAY_live_data.php');

// OPEN A CONNECTION TO THE DATA BASE SERVER
// MAN PAGE: http://us2.php.net/manual/en/function.mysql-connect.php
if (!$db_connection = mysql_connect("$db_host", "$db_user", "$db_word"))
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>NO DB CONNECTION: ";
    echo "<br/> $errmsg <br/>";
}

// SELECT THE MYSQL DATA BASE
// MAN PAGE: http://us2.php.net/manual/en/function.mysql-select-db.php
if (!$db_sel = mysql_select_db($db_name, $db_connection))
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>NO DB SELECTION: ";
    echo "<br/> $errmsg <br/>";
    die('NO DATA BASE');
}
// IF WE GOT THIS FAR WE CAN DO QUERIES



// QUERY FOR CREATING A TABLE
$sql = "CREATE TEMPORARY TABLE my_table (
        id         INT           NOT NULL AUTO_INCREMENT,
        first      VARCHAR(24)   NOT NULL DEFAULT '',
        last       VARCHAR(24)   NOT NULL DEFAULT '',
        email      VARCHAR(96)   NOT NULL DEFAULT '',
        terms      VARCHAR(1)    NOT NULL DEFAULT 'n',
        submitdate DATETIME      NOT NULL DEFAULT 0,
        PRIMARY KEY(id)  )";

// RUN THE QUERY
$res = mysql_query($sql);

// IF mysql_query() RETURNS FALSE, GET THE ERROR REASONS
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}



// MAKING ISO-8601 DATETIME STRINGS
$yesterday = date('c', strtotime('yesterday'));
$today     = date('c', strtotime('now'));
$tomorrow  = date('c', strtotime($today . ' + 24 hours'));



// CREATE TEST DATA BY MAKING INSERT QUERIES AND TESTING THE RESULTS
$sql = "INSERT INTO my_table ( first, last, email, terms, submitdate ) VALUES ( 'Bob', 'Yesterday', 'Joe@joe.com', 'y', '$yesterday' )";
$res = mysql_query($sql);
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}

$sql = "INSERT INTO my_table ( first, last, email, terms, submitdate ) VALUES ( 'Joe', 'Today', 'Joe@joe.com', 'y', '$today' )";
$res = mysql_query($sql);
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}

// MAKING INSERT QUERIES AND TESTING THE RESULTS
$sql = "INSERT INTO my_table ( first, last, email, terms, submitdate ) VALUES ( 'Ted', 'Tomorrow', 'Joe@joe.com', 'y', '$tomorrow' )";
$res = mysql_query($sql);
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}
// IF WE GET THIS FAR, THE INSERT QUERIES ALL SUCCEEDED



// JUST FOR FUN - GET THE AUTO_INCREMENT ID OF THE LAST RECORD WE INSERTED
$id  = mysql_insert_id($db_connection);
echo PHP_EOL . "LAST KEY: $id ";
echo PHP_EOL;



// MAKING AN OMNIBUS SELECT QUERY TO VISUALIZE ALL OF THE TEST DATA
$sql = "SELECT * FROM my_table ORDER BY first ASC";
$res = mysql_query($sql);
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
} // IF WE GET THIS FAR, THE QUERY SUCCEEDED AND WE HAVE A RESOURCE-ID IN $res SO WE CAN NOW USE $res IN OTHER MYSQL FUNCTIONS



// ITERATE OVER THE RESULTS SET TO SHOW WHAT WE SELECTED
while ($row = mysql_fetch_assoc($res))
{
    var_dump($row);
}



// CHOOSE THE NAME(S) WITH A DATE EQUAL TO TODAY'S DATE
$date_alpha = date('c', strtotime('today'));
$date_omega = date('c', strtotime('today + 24 hours - 1 second'));
$sql = "SELECT first, last FROM my_table WHERE submitdate BETWEEN '$date_alpha' AND '$date_omega' LIMIT 5";

// SHOW THE QUERY
echo PHP_EOL . $sql;
echo PHP_EOL;

// RUN THE QUERY
$res = mysql_query($sql);

// IF mysql_query() RETURNS FALSE, GET THE ERROR REASONS
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}

// ITERATE OVER THE RESULTS SET TO SHOW WHAT WE SELECTED
while ($row = mysql_fetch_assoc($res))
{
    var_dump($row);
}



// CHOOSE THE NAME(S) WITH A DATE EQUAL TO TOMORROW'S DATE
$date_alpha = date('c', strtotime('tomorrow'));
$date_omega = date('c', strtotime('tomorrow + 24 hours - 1 second'));
$sql = "SELECT first, last FROM my_table WHERE submitdate BETWEEN '$date_alpha' AND '$date_omega' LIMIT 5";

// SHOW THE QUERY
echo PHP_EOL . $sql;
echo PHP_EOL;

// RUN THE QUERY
$res = mysql_query($sql);

// IF mysql_query() RETURNS FALSE, GET THE ERROR REASONS
if (!$res)
{
    $errmsg = mysql_errno() . ' ' . mysql_error();
    echo "<br/>QUERY FAIL: ";
    echo "<br/>$sql <br/>";
    die($errmsg);
}

// ITERATE OVER THE RESULTS SET TO SHOW WHAT WE SELECTED
while ($row = mysql_fetch_assoc($res))
{
    var_dump($row);
}

Open in new window

0
 
LVL 3

Expert Comment

by:dkellner
ID: 36587100
With all my respect, Ray - this question clearly shows that the asker is on the right way to learn, giving us a standard "help at this point" request and he only needs a small chunk of information as a return value. It's not a protocol of downloading a whole knowledge, and it should not be.

UNIQUE, on the other hand, is a way too specific solution to the "limit to 1 per day" problem; if someone decides to make it "3 per day" suddenly UNIQUE will be just holding back the business logic. And it's an everyday thing to make a change like that. So maybe it's better not to build it into the database structure itself.

And when I say "all my respect", I mean it. I see your scores and your experience, Ray, and I know you're good. But everytime I see a question with a will behind that is not satisfied with the (technically proper) answer, I feel like correcting it. Even if considered a bad habit, this will stay with me.
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 36587435
I agree with your comment about UNIQUE and if you want to limit the number of instances to three, you would use different logic.  I was reading this in the original question: an error page if there is already a record for that day.  I read "already a record" to mean that the business logic says "no more than one."

It may be beside the point, but in my experience as a programmer I have found that there are three numbers that really matter.  Zero, One and Infinity.   If you want zero of a thing you sanitize the data with code that unconditionally eliminates the thing.  If you want one of a thing, you assign it to a scalar variable.  If you want more than one of a thing, you assign the collection of things to an array.  When I think of numbers in these terms it becomes easier to organize my thoughts and my test data.
0
 

Author Comment

by:web5dev7
ID: 36588233
Wow, lots of good info here - you both make good points. Ray, I will study your suggested  material nd example when I have time.  Just as some background I inherited this application and because I have an interest and keen desire to learn php/mySql - so my boss let me try my hand.  I am determined to succeed or I get put back at my old desk. It is what should be a simple entry form (which I have hacked together in the past) but this one uses fancy Ajax and multiple includes/classes and jQuery to accomplish validation, sanitize, connect, errors, etc (all preexisting and working fine).  For example, the form's processing page uses 19 includes which are loaded with classes. Everything works perfectly so I thought adding the "one-a-day" validation would be simple - but I guess not.  However, now I am under the gun to deliver by Monday and I am determined.

Ok dkellner, for the sake of urgency I will go try your suggestion now and let you know what happens... thanks all....
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36588424
If you have the number 12 you're one step from the glory.  It shows that the counting works as expected.  Now all you have to fix is the "don't let the user submit" part.

Tip: maybe the headers are already sent when you try to do the "Location:" redirect.
Try this:

if($foundSomething) {
    print "<script> alert(\"Don't do it twice, man!\"); </script>";
    exit;
}


Then, if it works, try:

if($foundSomething) {
    print "<script> location='error-duplicate-entry.php'; </script>";
    exit;
}
0
 

Author Comment

by:web5dev7
ID: 36588606
ok tried the print statement on the processing page in the function (nothing printed) and then the form page and still nothing. ?  Does the $result have to be declared global and/or the print command told where (what page) to print on ?
0
 

Author Comment

by:web5dev7
ID: 36590294
ok, tried the "don't do it twice, man" version and still nothing.  but here's the thing - I am not actually placing the code on the form page, but instead on a processing page which is "included" on the form page. As you will see below it is actually a "class" and I am placing the code right before the "insert into" function, so the issue might be where to place the code?   Here is the complete class file where I placed the code:

<?PHP        

class ContactRequest
{
    private $_db;      
    function __construct($db)
    {
        $this->_db = $db;
    }
 
// one-a-day functon
$oad = mysql_query("
   select count(1)from contact_requests
   where email='$email' and substr(submitdate,1,10)=substr(now(),1,10)
");
$result = mysql_fetch_array($oad);
$foundSomething = $result[0];
//if($foundSomething) {
//header('Location: http://www.url_ledited_by_alias99.com"');
//exit;
//}
if($foundSomething) {
    print "<script> alert(\"Don't do it twice, man!\"); </script>";
    exit;
}

&#9;
    function Create($data)
    {&#9;
&#9;$sql = "INSERT INTO contact_requests SET
                first_name = '".$data['first_name']."',
&#9;last_name = '".$data['last_name']."',
                email = '".$data['email']."',
                age_rules = '".$data['age_rules']."',
                optin = '".$data['optin']."',
                ip_address = '".$_SERVER['REMOTE_ADDR']."',
                submitdate = now()
                ";
               
        $response = $this->_db->query($sql);
        if($response)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
   

   
    function DataSet()
    {
        return $this->_db->dataset($this->getSQL());
    }
   
    function PagedDataSet()
    {
        $clsPaging = new clsPaging();
        $clsPaging->_do($this->getSQL());
       
        return $clsPaging;
    }
   
    function getSQL()
    {
        $sql = "SELECT r.* FROM contact_requests r ";              
       
        $sql .= " WHERE r.contact_id > 0";
       
        if(isset($_GET['contact_id']) && $_GET['contact_id'] != "")
        {
           $sql .= " AND r.contact_id like '%".$_GET['contact_id']."%'";
        }
       
        if(isset($_GET['first_name']) && $_GET['first_name'] != "")
        {
           $sql .= " AND r.first_name like '%".$_GET['first_name']."%'";
        }
&#9;&#9; if(isset($_GET['last_name']) && $_GET['last_name'] != "")
        {
           $sql .= " AND r.last_name like '%".$_GET['last_name']."%'";
        }
       
        if(isset($_GET['email']) && $_GET['email'] != "")
        {
           $sql .= " AND r.email like '%".$_GET['email']."%'";
        }
       
        if(isset($_GET['age_rules']) && $_GET['age_rules'] != "")
        {
           $sql .= " AND r.age_rules like '%".$_GET['age_rules']."%'";
        }
       
        if(isset($_GET['optin']) && $_GET['optin'] != "")
        {
           $sql .= " AND r.optin like '%".$_GET['optin']."%'";
        }
       
        if(isset($_GET['remember_me']) && $_GET['remember_me'] != "")
        {
           $sql .= " AND r.remember_me like '%".$_GET['remember_me']."%'";
        }
       
         if(isset($_GET['submitdate_from']) && $_GET['submitdate_from'] != "")
        {
            $sql .= " AND  STR_TO_DATE(r.submitdate,'%Y-%m-%d') >= '".$_GET['submitdate_from']."'";
        }
         
        if(isset($_GET['submitdate_to']) && $_GET['submitdate_to'] != "")
        {

            $sql .= " AND  STR_TO_DATE(r.submitdate,'%Y-%m-%d') <= '".$_GET['submitdate_to']."'";
        }
                 
        if(isset($_GET['has_response']) && $_GET['has_response'] == "y")
        {
            $sql .= " AND  r.contact_id IN (SELECT contact_id FROM contact_responses) ";
        }
        else if(isset($_GET['has_response']) && $_GET['has_response'] == "n")
        {
            $sql .= " AND  r.contact_id NOT IN (SELECT contact_id FROM contact_responses)";
        }
       
       

        if(isset($_GET['order_by']) && $this->isValidOption($_GET['order_by']))
        {
             $sql .= " ORDER BY r.".$_GET['order_by']." ";    
        }
        else
        {
             $sql .= " ORDER BY r.contact_id ";    
        }
       
        if(isset($_GET['order_direction']) && ($_GET['order_direction'] == 'asc' || $_GET['order_direction'] == 'desc'))
        {
             $sql .= " ".$_GET['order_direction']." ";    
        }
        else
        {
             $sql .= " DESC ";  
        }
                             
       
        return $sql;
    }
   
    function CSVDataset()
    {
        $sql = "SELECT * FROM contact_requests  ORDER by submitdate DESC ";
         
        $q = $this->_db->query($sql);

        $dataset = array();
        while($r = $this->_db->myArray($q)){
           
            $row = array();
           
            foreach($r as $key=>$value)
            {
                $row[strtoupper($key)] = $value;
            }
            $dataset[] = $row;
        }

        return $dataset;
    }
   
    function Delete($contact_id)
    {
        $sql = "DELETE FROM contact_requests WHERE contact_id = '".(int)$contact_id."'";
        return $this->_db->query($sql);
    }
   
    function OrderFields()
    {
        $options = array();
       
        $options['contact_id'] = "Request#";
        $options['first_name'] = "First Name";
        $options['last_name'] = "Last Name";
        $options['email'] = "Email";
        $options['optin'] = "Opt-In";
        $options['age_rules'] = "Over 18";
        $options['submitdate'] = "Date";    
       
        return $options;    
    }
   
    function isValidOption($option)
    {
         $fields = $this->OrderFields();
         
         if($fields[$option] == '')
            return false;
         return true;
    }
   
       
    function Data($contact_id)
    {
        $sql = "SELECT * FROM contact_requests WHERE contact_id = '".(int)$contact_id."'";
        return $this->_db->QuickArray($sql);
    }

}//class ends
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36590382
Oh yes.
The problem is that you've copied this stuff right into a class.  Not the right place.  A class is for declaring methods to access data.  Where do you USE this class, where do you CALL these methods?  You must have another file where you create an instance of ContactRequest and use it...

(Maybe we'll implement this 1/day protection as a new method of this class. Let's see that file first.)
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 

Author Comment

by:web5dev7
ID: 36590576
ok so there are 19 total other includes listed in the head of the form processing file and about 3 of them have an instance of ContactRequest. Not sure if it's relevent, but this one precedes the above mentioned one in the list. Here it is (it's named "handler.php"):

abstract class FormHandler
{  
    protected $_data = array();
    protected $_config = array();
    protected $_validator;
   
    protected $_response;
   
    protected $_request_data = null;
    protected $_email_settings = null;  
   
   public function __construct($config,$form_data)
    {
        $this->_config = $config;
        $this->_data = $form_data;    
    }
   
                           
    protected function getRequestData($id='')
    {
        if($id == '')
        {
            $id = $this->_data['contact_id'];
        }
       
        if($this->_request_data == null)
        {
             $contactRequests = new ContactRequest(MySql::Instance());
             $this->_request_data = $contactRequests->Data((int)$id);
        }
       
        return $this->_request_data;
    }
   
    protected function EmailSettings($key='')
    {
        if($this->_email_settings == null)
        {
            $this->_email_settings = $this->_config['email'];
        }
       
        if($key == '' || $key == null)
        {
            return $this->_email_settings;
        }
        else
        {
             return $this->_email_settings[$key];
        }
    }
   
   
 
    public function setValidator(Validator $validator)
    {
        $this->_validator = $validator;
    }
   
    public function getValidator()
    {
        if($this->_validator == null)
        {
            $this->_validator = new FormValidator();
        }
       
        return $this->_validator;
    }  
   
    public function getConfig($section_key,$key)
    {
        $Data = $this->_config[$section_key];
        return $Data[$key];
    }
       
    public function Validate()
    {
       if($this->getValidator()->Validate($this->_data)->isValidated())
        {
           
           $this->_response = array(
                "response"=>array(
                    "message"=>"Thank you ".$this->_data['first_name']."! Entry submitted successfully.",
                    "redirect_to" => $this->getConfig('form_settings','redirect_to')
                    )
             );
             
             return true;
        }
        else
        {
            $this->_response = array(
                "response"=>array(
                    "message"=> $this->getValidator()->getMessage(),
                    "security_question"=> CommonFunc::security_question()
                    )
             );          
             
             return false;                                                                                                                        
        }
    }
   
      abstract function Handle();
}//class ends
0
 

Author Comment

by:web5dev7
ID: 36590594
further down the list of includes there's also this one named: "class.ajaxform.handler.php":

class clsAjaxFormHandler  extends FormHandler
{                         
    function Handle()
    {
        $ajax = Ajax::Instance();
                                               
        if($this->Validate())
        {
            try
            {
                $this->_process();
               
                $ajax->setSuccessResponse($this->_response);
            }
            catch(Exception $ex)
            {
                $this->_response = array(
                    "response"=>array(
                        "message"=> array("Request couldn't be submitted. Please try again."),
                        "security_question"=> CommonFunc::security_question()
                        )
                 );    
                 
                 $ajax->setErrorResponse($this->_response);                                                                                                                                          
            }
        }
        else
        {
            $ajax->setErrorResponse($this->_response);                                                                                                                                    
        }
       
        $ajax->renderJSONResponse();
    }
   
    private function _process()
    {
          $oContactRequest = new ContactRequest(MySql::Instance());
          $oContactRequest->Create($this->_data);
         
          $form_settings = $this->_config['form_settings'];
         
          if($form_settings['enable_email'] == true)
          {
                $this->_mail();
          }
    }
   
   
    private function _mail(){
   
        $Mail = new clsMail();  
       
        $email_settings = $this->_config['email'];
        $fromName = $email_settings['from_name'];
        $no_reply = $email_settings['no_reply_email'];
        $contact_to_email = (array)$email_settings['to_email'];
        $reply_to = $email_settings['reply_to'];
       
        $Mail->IsHTML(true);
        $Mail->prep_headers($fromName,$no_reply);

        $Mail->setSubject("New Entry from ".$this->_data['first_name']."");

        $message="<html><body><table cellpadding='0' cellspacing='0' width='100%'>";

        unset($this->_data["security_code"]);
       
        foreach($this->_data as $label=>$value)
        {
            if($value != "")
            {
                $label = ucWords(str_replace("_"," ",$label));
                $message .="<tr><td width='30%'>".$label."</td><td width='70%' colspan='2'>".nl2br($value)."</td></tr>";
            }
        }
       
        $message .="<tr><td>IP Address</td><td colspan='2'>".$_SERVER['REMOTE_ADDR']."</td></tr></table></body></html>";
       
        $Mail->setMessage($message);

        // Email form to multiple email addresses - Added on: 14/April/2011
        foreach($contact_to_email as $k=>$to_email)
        {
            @$Mail->send($to_email);
        }
       
       
        #######
        // auto response email!!
        $template = $this->_config['email']['auto_response_template'];
        if($template['enable_auto_response'])
        {
            $to_subject = str_replace("[NAME]",$this->_data['first_name'],$template['subject']);

            $Mail->prep_headers($fromName,$reply_to);
            $Mail->setSubject($to_subject);

            $to_message = str_replace("[NAME]",$this->_data['first_name'],$template['message']);

            $Mail->setMessage(html_entity_decode($to_message));
            @$Mail->send($this->_data['email']);
        }
    }
}
?>
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36590603
That's good but still a file that does nothing by itself.
It's like a dashboard with no one in the driver's seat.

What is the main PHP?
Where's the FORM itself, and what's the "action" part of it?
Where does the posted request arrive?
0
 

Author Comment

by:web5dev7
ID: 36590731
=============
form page:
=============

<form method="post" id="contact_form" action="process.php">

<p><label>First Name <span class="req">*</span><em id="err_first_name" class="error">required</em></label><span><input type="text" id="first_name" name="first_name" class="text req_class" /></span></p>

<p><label>Last Name <span class="req">*</span><em id="err_last_name" class="error">required</em></label><span><input type="text" id="last_name" name="last_name" class="text req_class" /></span></p>

<p><label>Email Address <span class="req">*</span><em id="err_email" class="error">required</em></label><span><input type="text" id="email" name="email" class="text req_class" /></span></p>
                                                                                 
<p>
<span class="lef"><input type="checkbox" name="rules" id="rules" class="check req_class" value="y" ></span>
<label class="label2">
I am over 18 years of age. I agree to be bound by the official rules.
<span class="req">*</span>
<em id="err_rules" class="error">required</em>
</label>
</p>
   
<p><span class="lef"><input type="checkbox" name="optin" id="optin" class="check" value="y" checked="yes"> </span>
<label class="label2">Sign me up for future offers and promotions.</label></p>
   
<p class="button">
<input type="submit" name="button" id="contact_btn" class="btn ajax_submit_btn" value="Submit" />
<span class="ajax_loading"><img src="images/ajax-loader.gif" /></span>
</p>  
                                                                                                                               
 </form>

=========================
processing page (process.php):
=========================
<?PHP
/**
 * Form Handler
 */
require_once"include/init.php";  
$handler = new clsAjaxFormHandler($config,$_POST);
$handler->handle();
?>

=========================
init.php:
=========================

<?PHP
/**
 * Script Init
 */
ob_start();
session_start();  
require_once"include/config/config.php";
require_once"include/common.php";
require_once"include/abstract/validator.php";
require_once"include/class.db.mysql.php";    
require_once"include/class.ajax.php";    
require_once"include/class.validation.php";
require_once"include/class.validator.php";
require_once"include/abstract/handler.php";
require_once"include/models/contact.request.php";
require_once"include/models/contact.responses.php";
require_once"include/models/login.php";
require_once"include/class.mail.php";
require_once"include/class.csv.php";
require_once"include/class.ajaxform.handler.php";
require_once"include/class.ajaxReplyform.handler.php";
require_once"include/class.ajaxEmailReplyform.handler.php";
require_once"include/class.paging.php";
require_once"include/class.array.helper.php";
require_once"include/class.reply.validator.php";
?>

===========================
I'm not sure where the posted request arrives - I thought it was one of the pages I sent you earlier with the database "insert into" query. Keep in mind that the 2nd half of the includes listed above have to do with the "members" control panel where it displays the form entries for viewing, contacting (contact.responses.php), deleting, etc.... but there may be some sharing of those functions & classes I guess between the submission part and the user management part.

Does any of that help?
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36591163
Yes, thank you.
if class ContactRequest, look for these lines:

function Create($data)
    {      


Our code goes after the bracket.  This method is the one that gets the posted data - note that there is a variable $email in our code which won't exist there (it was symbolic in my example) so you'll need to do a

$email = $data["email"]

before the checking.
The whole thing should look like this:

function Create($data)
	{      
	
		$email = $data["email"];
		$oad = mysql_query("
			select count(1)from contact_requests
			where email='$email' and substr(submitdate,1,10)=substr(now(),1,10)
		");
		$result = mysql_fetch_array($oad);
		$foundSomething = $result[0];
		if($foundSomething) {
			// react the way you like
			// (for now it's the alert but it's debugtime only!)
			print "<script> alert(\"Don't do it twice, man!\"); </script>";
			exit;
		}
		
		$sql = "INSERT INTO contact_requests SET
			first_name = '".$data['first_name']."',
			last_name = '".$data['last_name']."',

			... yada yada ...

Open in new window


Hope I didn't forget about anything - I have no way to test it but it should do the job.
0
 

Author Comment

by:web5dev7
ID: 36591204
hmmm  the form just stopped responding when I dropped that code in (no errors, submit button does nothing) ?
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36592054
Okay then - we must talk to mysql the way they do.
I mean this:

$email = $data["email"];
    $result = $this->_db->QuickArray(
    	"select count(1)as ct from contact_requests ".
    	"where email='$email' ".
    	"and substr(submitdate,1,10)=substr(now(),1,10)"
    );
	$foundSomething = $result[0];

Open in new window


Now remember that I don't know this sql abstraction class, I'm just trying to use it as I see it's being used.  So maybe even this version won't work but the problem itself is solved, your question has been answered.  I'm here to help because I understand you have some strict deadlines and I know how it feels - but you're the one who should find out how to do queries in your project environment.
0
 

Author Comment

by:web5dev7
ID: 36592679
yeehaaa!  it works!  It is actually blocking the double entry but it's not executing the print or the script alert or the header location redirect - so that is why it appeared to be doing nothing.  It does however show the default ajax error that was already built in to the application "can not complete your entry".  However, if I comment out the script alert then the form appears to stop working, so I left it in and just changed the default message to include "if you have already submitted an entry today please try again tomorrow".

I would prefer to redirect them away from that form page (to discourage them from trying other devious things like using different email address, etc.) but I can live with this unless you have a suggestion about the redirect?

thank you sooo much for your patience!
0
 

Author Comment

by:web5dev7
ID: 36592842
dkellner,

Regarding security, there seems to be plenty of sanitization already happening in the many includes in this application, but now that I have plastered file names, code, etc all over this thread - what do you recommend I do about securing the db?  Change file names, etc?
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36592921
About security: what you revealed here is only important if you don't care about user input.  But anyone who checks the source of your webpage will already know what variables are accepted in your script.  They'll use this information and you must be ready for that.  The source code itself - as a collection of probably well known classes - doesn't say anything new about your project.  That is as far as I can tell, of course. It's always a good idea to ask a security expert.

About alert or not: NO, alert was just an example.  The proper way is to return false if something's wrong.  We're inside a function (a method) and it's not his job to print things anywhere.  So instead of alerting (now that we know what it does), start bubbling some error code thru the calls and catch it on a level that is proper for writing out error messages.  Like, for example, whoever made the AJAX call, should get back some kind of status value which is 0 for success and a nonzero code for errors.  If you don't know how to do such things, maybe it's time to learn more about AJAX; but basically, whatever you print out will be received by the caller so it won't be that much of a challenge.

I tell you this because no one (including the future yourself) likes patchworks, and what we did here is a very typical example of that.  In a hurry, people often do this, only to regret it later.

So just understand what we did any try to implement it PROPERLY, with a return value - one that can be distinguished from other errors (like "there is no such db") and successful completion.
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36592930
(In the first paragraph, when I say "the source of your webpage", it's what the browser can show about any webpage; not to be confused with the php source code which is invisible to users)
0
 

Author Comment

by:web5dev7
ID: 36592972
do you mean somethinglike this to replace the print statement:

 $response = $this->_db->query($sql);
        if($response)
        {
            return true;
        }
        else
        {
            return false;
        }
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36593000
Yes, something like this.  Like if($thereIsSomeOtherRecord) return false.
0
 

Author Comment

by:web5dev7
ID: 36593067
so instead of:

$foundSomething = $result[0];
            if($foundSomething) {
            print "<script> alert(\"Don't do it twice, man!\"); </script>";
            exit;
            }


something like:

$foundSomething = $result[0];
            if($foundSomething) {
$thereIsSomeOtherRecord= $this->_db->query($sql);
       
if($thereIsSomeOtherRecord)
        {
            return false;
        }
      exit;
}
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36593082
No.

if($foundSomething) return false;

This will do.
0
 

Author Comment

by:web5dev7
ID: 36593096
regarding optimizing the sql query.  When I do an "Explain" it says it had to look at 50 rows (which is all rows with my emails address). I guess it looks at all records with that email so if someone entered every day for 6 months that could eventually be alot - could I tweak the query so that it only has to look at today's entries instead of all ?
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36593099
Yes, create an index on the email AND date field.  Two fields, combined index.  Then check EXPLAIN again.
0
 

Author Comment

by:web5dev7
ID: 36593133
curiosly the if($foundSomething) return false;
makes the validation stop working so duplicates get submitted successfully

but when I put back the print "<script> alert(\"Don't do it twice, man!\"); </script>";
even though it does not print the validation works again
0
 
LVL 3

Expert Comment

by:dkellner
ID: 36593240
:) Well, strange enough.  But I think this thread starts to get very far from the original issue.  To completely solve your problem I'd have to check the whole project and understand what inside messaging it has - and I have my own projects to work on.  You must finish this thing alone.

As a final comment: look for anything that seems to communicate along the calling path, like A calls B, B calls C, C returns something to B, B makes some further checks and returns another something to A.  You have the option to test anything you figure out - I don't.  Try to use the logic the original scripts use; try to get back to the HTML somehow to be able to display a message.  And when you do, you can use javascript redirection, alert message or whatever you please.

Play with it.  It's yours.  Change the print "..." text, see what happens if you just print "0" or "1", or nothing; learn what you're working with.

And good luck with the deadline!  I hope you make it.
0
 

Author Comment

by:web5dev7
ID: 36593302
thank you very very much for your time - I'll keep at it.  have a great weekend.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Author Note: Since this E-E article was originally written, years ago, formal testing has come into common use in the world of PHP.  PHPUnit (http://en.wikipedia.org/wiki/PHPUnit) and similar technologies have enjoyed wide adoption, making it possib…
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now