Scenario: I have a HTML form that sends variables to a Perl CGI which then takes them and inserts them on to a SQL DB I created earlier, but the problem is that it sends only NULL values to the DB - it does send the "correct number" of nulls though so I don't know what is going wrong. I have a feeling it is something to do with the variable passing to the Perl not Perl to DB. The Perl file:
#! \xampp\perl\bin\perl.exe -w
require "dbfunc.pl";
use warnings;
use CGI qw/:standard/;
use CGI::Carp qw(fatalsToBrowser);
$table = "routes";
#$spotted = "spotted";
$booked = "bookings";
$logged = "log";
$dbh = getConnection();
print header;
print start_html("Journey Details");
$name = param($name);
$email = param($email);
$price = param($price);
$date = param($date);
$departure = param($departure);
$arrival = param($arrival);
$adults = param($adults);
$children = param($children);
$totalCost = param($totalCost);
$departureTime = param($departureTime);
$arrivalTime = param($arrivalTime);
$jid = param($jid);
$dbh->do("INSERT INTO $logged VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", undef,
$date, $date, $name, $email, $departure, $arrival, $departureTime, $adults, $children, $totalCost);
#my $sth = $dbh->prepare(qq{INSERT INTO $logged SET DateBooked=?, Journeydate=?, Name=?, Email=?, RouteFrom=?, RouteTo=? , DepartTime=?, Adults=?, Children=?, AmountPaid=?});
#$sth->execute($date, $date, $name, $email, $departure, $arrival, $departureTime, $adults, $children, $totalCost) or die $dbh->errstr;
print end_html;
The first perl file that initially takes the vars:
#! \xampp\perl\bin\perl.exe -w
use CGI qw(:standard);
$query = new CGI;
#parameters = $query -> param;
print header, start_html("Receipt");
print p("Your Journey Receipt");
my $name = $query->param('name');
print ("Name: $name");
print br;
my $email = $query->param('email');
print ("Email: $email");
print br;
my $price = $query->param('price');
print ("Price: £$price");
print br;
my $date = $query->param('date');
print ("Journey date: $date");
print br;
my $departure = $query->param('departure');
print ("From: $departure");
print br;
my $arrival = $query->param('arrival');
print ("To: $arrival");
print br;
my $adults = $query->param('adults');
print ("Adults: $adults");
print br;
my $children = $query->param('children');
print ("Children: $children");
print br;
my $totalCost = $query->param('totalCost');
print ("Total Cost: £$totalCost");
print br;
my $departureTime = $query->param('departureTime');
print ("Departure: $departureTime");
print br;
my $arrivalTime = $query->param('arrivalTime');
print ("Arrival: $arrivalTime");
print br;
my $jid = $query->param('jid');
print ("Journey ID: $jid");
print br;
print qq!<br><form><input type="Button" value="Back" onclick="history.back()"></form>!;
print qq!<br><form method="get" action="serverside.pl">!;
print qq!<input type="submit" value="Confirm Booking" />\n</form><br />!;
print end_html;
You are misunderstanding the way CGI programs work. They don't send data to one-another: they are executed as a result of an action on a web browser, and if that action was a click on a form submit button then they will receive a set of parameters according to the names and contents of that form's <input> elements.
Your scripts don't use strict as they should, and the -w on the shebang line pretty much duplicates the action of the use warnings statement. You should use just the latter.
As Quentin says, the NULL values in the database are because you are calling $name = param($name) which, because $name is undefined, is the same as $name = param(''). You need to use a fixed string, like you did in your other script $name = param('name').
But that assumes that somewhere there is a CGI script or just an HTML file that has a <form> element with all those <input> fields. Clicking submit on such a form will execute the script specified in the action attribute and pass to it the contents of all the fields.
The first of your two scripts is expecting form input, and writes the contents of that form to the database, while the second of the two (that you say is the first perl file!) is also expecting form input but builds a web page with the information. The problem is that you don't seem to have written that form anywhere.
What I think you need is to combine the two CGI scripts, so that when submit is clicked the script both writes the information to the database and displays it on the screen. And you also need to write that form which, as I said could be just a plain HTML file.
It is also common to combine the form input and the database update in one script, which checks to see if it has been passed any parameters. If there are none then it displays the input form and waits for a response. Otherwise they are used to update the database and put up a confirmation page.
I hope this helps you.
You make the same mistake several times. I'll use the first instance an example:
$name = param($name);
You get the value of $name (which you haven't yet defined) and use it to get a param from the HTTP request. Since it isn't defined, you don't get the result you are looking for, so you don't get the submitted data in $name.
Presumably you intended:
$name = param('name');
Update now you have the form you are using:
print qq!<br><form method="get" action="serverside.pl">!;
print qq!<input type="submit" value="Confirm Booking" />\n</form><br />!;
You don't have any <input> elements except for the submit button (which doesn't have a name attribute), so there is no data to submit.
Related
I've tried searching forums for a solution, however most of the answers were either too difficult to understand. SO I'm in the process of making a website for a small community, and currently we have our database and html design layout, but I'm getting snagged on how to push my Perl CGI form into another Perl program to then alter my database.
Here is the Perl controller that alters the database tables (and it works):
#!/usr/bin/perl -w
#!/usr/bin/perl -wT
# DBI is the standard database interface for Perl
# DBD is the Perl module that we use to connect to the <a href=http://mysql.com/>MySQL</a> database
use DBI;
use DBD::mysql;
use warnings;
#----------------------------------------------------------------------
# open the accessDB file to retrieve the database name, host name, user name and password
open(ACCESS_INFO, "accessDB.txt") || die "Can't access login credentials";
# assign the values in the accessDB file to the variables
my $database = <ACCESS_INFO>;
my $host = <ACCESS_INFO>;
my $userid = <ACCESS_INFO>;
my $passwd = <ACCESS_INFO>;
my $tablename = "Article";
# the chomp() function will remove any newline character from the end of a string
chomp ($database, $host, $userid, $passwd);
# close the accessDB file
close(ACCESS_INFO);
#----------------------------------------------------------------------
# invoke the ConnectToMySQL sub-routine to make the database connection
$connection = ConnectToMySql($database);
if ($tablename == "Article"){
$connection = InsertArticle($database);
}
elsif ($tablename == "Category"){
$connection = InsertCategory($database);
}
elsif ($tablename == "Comment"){
$connection = InsertComment($database);
}
elsif ($tablename == "User"){
$connection = InsertUser($database);
}
else {
print "No such table found. Contact website administrator.\n"
}
sub InsertArticle{
$query = "insert into $tablename (Id, CategoryId, UserId, Title, Content) values(?, ?, ?, ?, ?)";
$statement = $connection->prepare($query);
$statement->execute('undef', '1', '1029', 'Dota2>League', 'textfromarticle');
}
sub InsertCategory{
$query = "insert into $tablename (Id, CategoryId, UserId, Title, Content) values(?, ?, ?, ?, ?)";
$statement = $connection->prepare($query);
$statement->execute('undef', '1', '1029', 'Dota2>League', 'textfromarticle');
}
sub InsertComment{
$query = "insert into $tablename (Id, CategoryId, UserId, Title, Content) values(?, ?, ?, ?, ?)";
$statement = $connection->prepare($query);
$statement->execute('undef', '1', '1029', 'Dota2>League', 'textfromarticle');
}
sub InsertUser{
$query = "insert into $tablename (Id, CategoryId, UserId, Title, Content) values(?, ?, ?, ?, ?)";
$statement = $connection->prepare($query);
$statement->execute('undef', '1', '1029', 'Dota2>League', 'textfromarticle');
}
exit;
#--- start sub-routine ------------------------------------------------
sub ConnectToMySql {
#----------------------------------------------------------------------
my ($db) = #_;
# assign the values to your connection variable
my $connectionInfo="dbi:mysql:$db;$host";
# make connection to database
my $l_connection = DBI->connect($connectionInfo,$userid,$passwd);
# the value of this connection is returned by the sub-routine
return $l_connection;
}
#--- end sub-routine --------------------------------------------------
In the future, I'll define the other tables in my database through global variables that depend on what button the user presses on the correct webpage. As in, if they're viewing a list of articles, an option at the top would be "submit an article". And from there, the form CGI would be sent to them that they can fill out.
And here is the CGI that makes the form that would be submitted to the above controller script to alter the table:
#!/usr/bin/perl
#!/usr/bin/perl -wT
use strict;
use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser); #remove this in production
my $q = new CGI;
print $q->header; #Content-Type: text/html; charset=ISO-8859-1
print $q->start_html(
-title => 'submit an Article', #page name
-style => {'src' => '/dmrwebsite/dmrwebsite/userinterface'}, #link to style sheet
);
print $q->start_form(
-name => 'submitting an Article',
-method => 'POST',
enctype => &CGI::URL_ENCODED,
-onsubmit => 'return true',
-action => '/dmrwebsite/dmrwebsite/controller.addtotable.pl',
);
print $q-.textfield(
-name => 'title',
-value => 'default value',
-required,
-size => 20,
-maxlength =>50,
);
print $q->textarea(
-name => 'content',
-value => 'default value',
-required,
-maxlength => 1000,
-cols => 60,
);
print $q->textarea(
-name => 'url',
-value => 'default value',
maxlength => 100,
cols =>60,
);
print $q-checkbox(
-name => 'humancheck',
-checked => 1,
-value => 'two',
-label => 'The number two',
);
print $q-submit(
-name => 'submit_Article',
-value => 'submit Article',
-onsumbit => 'javascript: validate_form()',
);
if ($q->param()) {
submit_form($q);
} else {
print "Please check your form for inaccuracies.";
}
sub submit_form($){
my ($q) = #_;
}
print $q->end_form; #ends the form html
print $q->end_html; #end the html document
So basically what I'm stuck at is understand how to send the form data to the perl script in which I can then define the table data in my $tablename = "Article"; and $statement->execute('undef', '1', '1029', 'Dota2>League', 'textfromarticle');.
Also, I don't have a javascript program to send to the parameter -onsubmit => javaapplication(),. Is that needed? Can I substitute my own Perl program to check the user inputted fields? And how would I call this function? IN the same file or can it just be in the parent directory like /website/perlchecker.pl?
Any help would be greatly appreciated as I'm only a couple days into using Perl let alone CGI and html. Got a couple people helping me on the front end of the website though.
Thanks,
-Ori
So many suggestions...
Firstly, your DB insertion program seems to just insert fixed data, so I'm not sure how you think that it works. Also, the if ($tablename == "Article") (and similar) line doesn't do what you want it to. You need to use eq instead of ==.
To answer the question that you asked - you need to change your database program so that it accepts input (probably command line arguments) containing the data that you want inserted into the database. You would then add to your CGI program a line that calls this program (probably using system()) passing it the data from the CGI parameters on the command line.
The code would look something like this:
my $title = $q->param('title');
my $content = $q->param('title');
# ... other params ...
system('db_script.pl', 'Article', $title, $content, ...)';
But please don't do that. That's a terrible idea.
Instead, I highly recommend that you re-write your database manipulation program as a module. That way, you can load the module into any program that needs to talk to the database and access the database by calling functions rather than by calling an external program. If it was down to me, then I'd definitely use DBIx::Class to produce this library - but I realise that might well be seen as rather advanced.
Then there's the elephant in the room. You're still using CGI to write your web interface. The CGI module has been removed from the latest version of Perl as it is no longer considered best practice for writing web applications. I recommend looking at CGI::Alternatives to find out about other, more modern, tools.
But if you're determined to carry on writing your program as a CGI program, then at the very least, please don't use the HTML generation functions. We've known that including your HTML in your program source code is a terrible idea for at least fifteen years. There's no reason to still be doing it in 2015. You should really be using some kind of templating engine to separate your HTML from your Perl code. I recommend the Template Toolkit.
I'm not sure where you are learning these techniques from, but your source seems to be a good ten years behind accepted best practice.
I'm fairly new to programming in Perl and I have a couple of compilation issues I can't seem to resolve. My program gets input from this HTML form.
Question: Should my form use the post or get method?
<FORM action="./cgi-bin/Perl.pl" method="GET">
<br>
Full name: <br><input type="text" name="full_name" maxlength="20"><br>
Username: <br><input type="text" name="user_name" maxlength="8"><br>
Password: <br><input type="password" name="password" maxlength="15"><br>
Confirm password: <br><input type="password" name="new_password" maxlength="15"><br>
I open a CSV file, write the value of user_name into an array and do a number of checks on the user's input.
Problem #1: I need to check that full_name, user_name, password, and new_password are all alphanumeric or a space but I keep getting multiple errors that look like:
Use of uninitialized value $full_name in string eq at Perl.pl line 33
I don't think I've used CGI correctly to get these values from the form. I also believe I'm not correctly checking for alphanumeric characters. How can I resolve this?
Problem #2: I need to redirect the user to a specific webpage if their passwords don't match and if the username is already taken. I used a meta redirect but it's not doing it successfully. How can I display a proper error page?
This is my code:
#!/usr/bin/perl
use CGI qw(:standard);
use strict;
use warnings;
print "Content-type: text/html\n\n";
#opening Members.csv for reading
my $file = '/home/2014/amosqu/public_html/cgi-bin/Members.csv';
open(my $csv, '<', $file) || die "Could not open your file";
#getting these from HTML form
my $full_name = param('full_name');
my $user_name= param('user_name');
my $password = param('password');
my $new_password = param('new_password');
my #users = ();
#splitting each line of csv file
foreach (<$csv>) {
chomp;
my #fields = split (/\,/);
push #users, $fields[1]; #put all usernames inside of array
}
close $csv;
#opening Members.csv for appending
open(my $fh, '>>', $file) || die "Could not open your file";
#SOURCE OF PROBLEM 1
#checking that all values are alphanumeric
if(($full_name && $user_name && $password && $new_password) eq /\A[[:alnum:]]+\z/) {
#if passwords don't match, redirect to error page
if($password ne $new_password){
print qq(<html>\n);
print qq(<head>\n);
print qq(<title> Passwords don't match. </title> \n);
print qq{<meta http-equiv="refresh"content="5;URL="http://www.cs.mcgill.ca/~amosqu/registration.html">\n};
print qq(</head>\n);
print qq(<body>\n);
print qq(<b><center> Passwords don't match </b></center>\n\n);
print qq(</body>\n);
print qq(</html>\n);
}
#if they do match, check that user name isn't in Members.csv
else {
if(grep (/$user_name/, #users)) {
print qq(<html>\n);
print qq(<head>\n);
print qq(<title> Sorry username already taken. </title>\n);
print qq{<meta http-equiv="refresh"content="5;URL="http://www.cs.mcgill.ca/~amosqu/registration.html">\n};
print qq(</head>\n);
print qq(<body>\n);
print qq(<b><center> Username already taken. </b></center>\n\n);
print qq(</body>\n);
print qq(</html>\n);
}
#if it isn't already in Members.csv append values to the file
else {
print $fh "$full_name, $user_name, $password \n";
}
}
}
close $fh;
This should get you going. There is a number of issues with your code that don't stop it from working, but current wisdom is not to use CGI at all so I will roll with you.
Use GET unless you have a good reason to use POST
The problem is here
if(($full_name && $user_name && $password && $new_password) eq /\A[[:alnum:]]+\z/) {
You are using a Boolean && operation that combines the truth of the three variables, and checking whether that, as a string, is equal to the result of matching the contents of $_ against that regular expression.
You must check each of the variables individually, and use the binding operator =~ to test them against a regex. It is also bad form to use the POSIX character classes. I suggest you use grep, like this
my $mismatch = grep /[^A-Z0-9]/i, $full_name, $user_name, $password, $new_password;
Now, $mismatch is true if any of the variables contain a non-alphanumeric character. (Strictly, it is set to the number of variables that have a a non-alphanumeric character, which is zero (false) if none of them do.)
Then you can say
if (not $mismatch) { ... }
It looks like you just need an else that builds a separate page.
I'm trying to automate hotmail login. How can I find what the appropriate fields are? When I print the forms I just get a bunch of hex information.
what's the correct method and how is it used?
use WWW::Mechanize;
use LWP::UserAgent;
my $mech = WWW::Mechanize->new();
my $url = "http://hotmail.com";
$mech->get($url);
print "Forms: $mech->forms";
if ($mech->success()){
print "Successful Connection\n";
} else {
print "Not a successful connection\n"; }
this may help you
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
my $url = "http://yoururl.com";
$mech->get($url);
my #forms = $mech->forms;
foreach my $form (#forms) {
my #inputfields = $form->param;
print Dumper \#inputfields;
}
Sometimes it is useful to look at what the web site is asking in advance of coding up a reader or interface to it.
I wrote this bookmarklet that you save in your browser bookmarks and when you click it while visiting any html web page will show in a pop-up all the forms actions and fields with values even hidden. Simply copy the text below and paste into a new bookmark location field, name it and save.
javascript:t=%22<TABLE%20BORDER='1'%20BGCOLOR='#B5D1E8'>%22;for(i=0;i<document.forms.length;i++){t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#336699'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20Name:%20%22;t+=document.forms[i].name;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#99BADD'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20Action:%20%22;t+=document.forms[i].action;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#99BADD'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20onSubmit:%20%22;t+=document.forms[i].onSubmit;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH>ID:</TH><TH>Element%20Name:</TH><TH>Type:</TH><TH>Value:</TH></TR>%22;for(j=0;j<document.forms[i].elements.length;j++){t+=%22<TR%20BGCOLOR='#FFFFFF'><TD%20align='right'>%22;t+=document.forms[i].elements[j].id;t+=%22</TD><TD%20align='right'>%22;t+=document.forms[i].elements[j].name;t+=%22</TD><TD%20align='left'>%20%22;t+=document.forms[i].elements[j].type;t+=%22</TD><TD%20align='left'>%20%22;if((document.forms[i].elements[j].type==%22select-one%22)%20||%20(document.forms[i].elements[j].type==%22select-multiple%22)){t_b=%22%22;for(k=0;k<document.forms[i].elements[j].options.length;k++){if(document.forms[i].elements[j].options[k].selected){t_b+=document.forms[i].elements[j].options[k].value;t_b%20+=%20%22%20/%20%22;t_b+=document.forms[i].elements[j].options[k].text;t_b+=%22%20%22;}}t+=t_b;}else%20if%20(document.forms[i].elements[j].type==%22checkbox%22){if(document.forms[i].elements[j].checked==true){t+=%22True%22;}else{t+=%22False%22;}}else%20if(document.forms[i].elements[j].type%20==%20%22radio%22){if(document.forms[i].elements[j].checked%20==%20true){t+=document.forms[i].elements[j].value%20+%20%22%20-%20CHECKED%22;}else{t+=document.forms[i].elements[j].value;}}else{t+=document.forms[i].elements[j].value;}t+=%22</TD></TR>%22;}}t+=%22</TABLE>%22;mA='menubar=yes,scrollbars=yes,resizable=yes,height=800,width=600,alwaysRaised=yes';nW=window.open(%22/empty.html%22,%22Display_Vars%22,%20mA);nW.document.write(t);
I tried to mimc the post request that sends your login info, but the web site seems to be dynamically adding a bunch of id's ---long generated strings etc to the url and I couldn't figure out how to imitate them. So I wrote the hacky work-around below.
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Curl::Easy;
use Data::Dumper;
my $curl = WWW::Curl::Easy->new;
#this is the name and complete path to the new html file we will create
my $new_html_file = 'XXXXXXXXX';
my $password = 'XXXXXXXX';
my $login = 'XXXXXXXXX';
#escape the .
$login =~ s/\./\\./g;
my $html_to_insert = qq(<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script><script type="text/javascript">setTimeout('testme()', 3400);function testme(){document.getElementById('res_box').innerHTML = '<h3 class="auto_click_login_np">Logging in...</h3>';document.f1.passwd.value = '$password';document.f1.login.value = '$login';\$("#idSIButton9").trigger("click");}var counter = 5;setInterval('countdown()', 1000);function countdown(){document.getElementById('res_box').innerHTML = '<h3 class="auto_click_login_np">You should be logged in within ' + counter + ' seconds</h3>';counter--;}</script><h2 style="background-color:#004c00; color: #fff; padding: 4px;" id="res_box" onclick="testme()" class="auto_click_login">If you are not logged in after a few seconds, click here.</h2>);
$curl->setopt(CURLOPT_HEADER,1);
my $url = 'https://login.live.com';
$curl->setopt(CURLOPT_URL, $url);
# A filehandle, reference to a scalar or reference to a typeglob can be used here.
my $response_body;
$curl->setopt(CURLOPT_WRITEDATA, \$response_body);
open( my $fresh_html_handle, '+>', 'fresh_html_from_login_page.html');
# Starts the actual request
my $curl_return_code = $curl->perform;
# Looking at the results...
if ($curl_return_code == 0) {
print("Transfer went ok\n");
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
# judge result and next action based on $response_code
print $fresh_html_handle $response_body;
} else {
# Error code, type of error, error message
print("An error happened: $curl_return_code ".$curl->strerror($curl_return_code)." ".$curl->errbuf."\n");
}
close($fresh_html_handle);
#erase whatever a pre-existing edited file if there is one
open my $erase_html_handle, ">", $new_html_file or die "Hork! $!\n";
print $erase_html_handle;
close $erase_html_handle;
#open the file with the login page html
open( FH, '<', 'fresh_html_from_login_page.html');
open( my $new_html_handle, '>>', $new_html_file);
my $tracker=0;
while( <FH> ){
if( $_ =~ /DOCTYPE/){
$tracker=1;
print $new_html_handle $_;
} elsif($_ =~ /<\/body><\/html>/){
#now add the javascript and html to automatically log the user in
print $new_html_handle "$html_to_insert\n$_";
}elsif( $tracker == 1){
print $new_html_handle $_;
}
}
close(FH);
close($new_html_handle);
my $sys_call_res = system("firefox file:///usr/bin/outlook_auto_login.html");
print "\n\nresult: $sys_call_res\n\n";
I have a Perl script that reads in data from a database and prints out the result in HTML forms/tables. The form of each book also contains a submit button.
I want Perl to create a text file (or read into one already created) and print the title of the book that was inside the form submitted. But I can't seem to get param() to catch the submit action!
#!/usr/bin/perl -w
use warnings; # Allow for warnings to be sent if error's occur
use CGI; # Include CGI.pm module
use DBI;
use DBD::mysql; # Database data will come from mysql
my $dbh = DBI->connect('DBI:mysql:name?book_store', 'name', 'password')
or die("Could not make connection to database: $DBI::errstr"); # connect to the database with address and pass or return error
my $q = new CGI; # CGI object for basic stuff
my $ip = $q->remote_host(); # Get the user's ip
my $term = $q->param('searchterm'); # Set the search char to $term
$term =~ tr/A-Z/a-z/; # set all characters to lowercase for convenience of search
my $sql = '
SELECT *
FROM Books
WHERE Title LIKE ?
OR Description LIKE ?
OR Author LIKE ?
'; # Set the query string to search the database
my $sth = $dbh->prepare($sql); # Prepare to connect to the database
$sth->execute("%$term%", "%$term%", "%$term%")
or die "SQL Error: $DBI::errstr\n"; # Connect to the database or return an error
print $q->header;
print "<html>";
print "<body>";
print " <form name='book' action='bookcart.php' method=post> "; # Open a form for submitting the result of book selection
print "<table width=\"100%\" border=\"0\"> ";
my $title = $data[0];
my $desc = $data[1];
my $author = $data[2];
my $pub = $data[3];
my $isbn = $data[4];
my $photo = $data[5];
print "<tr> <td width=50%>Title: $title</td> <td width=50% rowspan=5><img src=$photo height=300px></td></tr><tr><td>Discreption Tags: $desc</td></tr><tr><td>Publication Date: $pub</td></tr><tr><td>Author: $author</td></tr><tr><td>ISBN: $isbn</td> </tr></table> <br>";
print "Add this to shopping cart:<input type='submit' name='submit' value='Add'>";
if ($q->param('submit')) {
open(FILE, ">>'$ip'.txt");
print FILE "$title\n";
close(FILE);
}
print "</form>"; # Close the form for submitting to shopping cart
You haven't used use strict, to force you to declare all your variables. This is a bad idea
You have used remote_host, which is the name of the client host system. Your server may not be able to resolve this value, in which case it will remain unset. If you want the IP address, use remote_addr
You have prepared and executed your SQL statement but have fetched no data from the query. You appear to expect the results to be in the array #data, but you haven't declared this array. You would have been told about this had you had use strict in effect
You have used the string '$ip'.txt for your file names so, if you were correctly using the IP address in stead of the host name, your files would look like '92.17.182.165'.txt. Do you really want the single quotes in there?
You don't check the status of your open call, so you have no idea whether the open succeeded, or the reason why it may have failed
I doubt if you have really spent the last 48 hours coding this. I think it is much more likely that you are throwing something together in a rush at the last minute, and using Stack Overflow to help you out of the hole you have dug for yourself.
Before asking for the aid of others you should at least use minimal good-practice coding methods such as applying use strict. You should also try your best to debug your code: it would have taken very little to find that $ip has the wrong value and #data is empty.
Use strict and warnings. You want to use strict for many reasons. A decent article on this is over at perlmonks, you can begin with this. Using strict and warnings
You don't necessarily need the following line, you are using DBI and can access mysql strictly with DBI.
use DBD::mysql;
Many of options are available with CGI, I would recommend reading the perldoc on this also based on user preferences and desired wants and needs.
I would not use the following:
my $q = new CGI;
# I would use as so..
my $q = CGI->new;
Use remote_addr instead of remote_host to retrieve your ip address.
The following line you are converting all uppercase to lowercase, unless it's a need to specifically read from your database with all lowercase, I find this useless.
$term =~ tr/A-Z/a-z/;
Next your $sql line, again user preference, but I would look into sprintf or using it directly inside your calls. Also you are trying to read an array of data that does not exist, where is the call to get back your data? I recommend reading the documentation for DBI also, many methods of returning your data. So you want your data back using an array for example...
Here is an untested example and hint to help get you started.
use strict;
use warnings;
use CGI qw( :standard );
use CGI::Carp qw( fatalsToBrowser ); # Track your syntax errors
use DBI;
# Get IP Address
my $ip = $ENV{'REMOTE_ADDR'};
# Get your query from param,
# I would also parse your data here
my $term = param('searchterm') || undef;
my $dbh = DBI->connect('DBI:mysql:db:host', 'user', 'pass',
{RaiseError => 1}) or die $DBI::errstr;
my $sql = sprintf ('SELECT * FROM Books WHERE Title LIKE %s
OR Description LIKE %s', $term, $term);
my $sth = $dbh->selectall_arrayref( $sql );
# Retrieve your result data from array ref and turn into
# a hash that has title for the key and a array ref to the data.
my %rows = ();
for my $i ( 0..$#{$sth} ) {
my ($title, $desc, $author, $pub, $isbn, $pic) = #{$sth->[$i]};
$rows{$title} = [ $desc, $author, $pub, $isbn, $pic ];
}
# Storing your table/column names
# in an array for mapping later.
my #cols;
$cols[0] = Tr(th('Title'), th('Desc'), th('Author'),
th('Published'), th('ISBN'), th('Photo'));
foreach (keys %rows) {
push #cols, Tr( td($_),
td($rows{$_}->[0]),
td($rows{$_}->[1]),
td($rows{$_}->[2]),
td($rows{$_}->[3]),
td(img({-src => $rows{$_}->[4]}));
}
print header,
start_html(-title => 'Example'),
start_form(-method => 'POST', -action => 'bookcart.php'), "\n",
table( {-border => undef, -width => '100%'}, #cols ),
submit(-name => 'Submit', -value => 'Add Entry'),
end_form,
end_html;
# Do something with if submit is clicked..
if ( param('Submit') ) {
......
}
This assumes that you're using the OO approach to CGI.pm, and that $q is the relevant object. This should work, assuming that you have $q = new CGI somewhere in your script.
Can you post the rest of the script?
I've created a mockup to test this, and it works as expected:
#!/usr/bin/perl
use CGI;
my $q = new CGI;
print $q->header;
print "<form><input type=submit name=submit value='add'></form>\n";
if ($q->param('submit')) {
print "submit is \"" . $q->param('submit') . "\"\n";
}
After the submit button is clicked, the page displays that submit is "add" which means the evaluation is going as planned.
I guess what you need to do is make sure that $q is your CGI object, and move forward from there.
I am trying to import several .csv files into a mysql database, the script below works except that it only imports the first row of my csv data into the database. Both my tables are populated with exactly one data entry.
Any help would be appreciated.
Thank you
#!/usr/bin/perl
use DBI;
use DBD::mysql;
use strict;
use warnings;
# MySQL CONFIG VARIABLES
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=" . $database . ";host=" . $host;
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
# enter the file name that you want import
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open FILE, "<", $filename or die $!;
$_ = <FILE>;
$_ = <FILE>;
while (<FILE>) {
my #f = split(/,/,$_);
if (length($f[4]) < 10) {
print "No Weight\n";
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
}
my $sql = "INSERT INTO datetime_stamp (subject, date, time, weight)
VALUES('$f[1]', '$f[2]', '$f[3]', '$f[4]')";
print "$sql\n";
my $query = $dbh->do($sql);
my $sql = "INSERT INTO subj_weight (subject, weight) VALUES('$f[1]', '$f[2]')";
my $query = $dbh->do($sql);
close(FILE);
}
As has been commented, you close the input file after reading the first data entry, and so only populate your database with a single record.
However there are a few problems with your code you may want to consider:
You should set autoflush on the STDOUT file handle if you are printing diagnostics as the program runs. Otherwise perl won't print the output until either it has a buffer full of text to print or the file handle is closed when the program exits. That means you may not see the messages you have coded until long after the event
You should use Text::CSV to parse CSV data instead of relying on split
You can interpolate variables into a double-quoted string. That avoids the use of several concatenation operators and makes the intention clearer
Your open is near-perfect - an unusual thing - because you correctly use the three-parameter form of open as well as testing whether it succeeded and putting $! in the die string. However you should also always use a lexical file handle as well instead of the old-fashioned global ones
You don't chomp the lines you read from the input, so the last field will have a trailing newline. Using Text::CSV avoids the need for this
You use indices 1 through 4 of the data split from the input record. Perl indices start at zero, so that means you are droppping the first field. Is that correct?
Similarly you are inserting fields 1 and 2, which appear to be subject and date, into fields called subject and weight. It seems unlikely that this can be right
You should prepare your SQL statements, use placeholders, and provide the actual data in an execute call
You seem to diagnose the data read from the file ("No Weight") but insert the data into the database anyway. This may be correct but it seems unlikely
Here is a version of your program that includes these amendments. I hope it is of use to you.
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Text::CSV;
use IO::Handle;
STDOUT->autoflush;
# MySQL config variables
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=$database;host=$host";
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open my $fh, '<', $filename
or die qq{Unable to open "$filename" for input: $!};
my $csv = Text::CSV->new;
$csv->getline($fh) for 1, 2; # Drop header lines
my $insert_datetime_stamp = $dbh->prepare( 'INSERT INTO datetime_stamp (subject, date, time, weight) VALUES(?, ?, ?, ?)' );
my $insert_subj_weight = $dbh->prepare( 'INSERT INTO subj_weight (subject, weight) VALUES(?, ?)' );
while (my $row = $csv->getline($fh)) {
if (length($row->[4]) < 10) {
print qq{Invalid weight: "$row->[4]"\n};
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
$insert_datetime_stamp->execute(#$row[1..4]);
$insert_subj_weight->execute(#$row[1,4]);
}
}