Reputation: 27
IN SUMMARY: My online website has a stopwatch, that stopwatch is run on my header.php using Javascript. When user finishes the quiz and clicks the "submit" button, the data of the stopwatch(total seconds the user took to finish the quiz), should be saved on my PhpMyAdmin database.
So, In my header.php I have the following code inside my <head> ... </head>
tag. This code will start a stopwatch as soon as the user is transferred to the page https://mathspeedtest.com/questions/ . The stopwatch will stop when the user clicks the submit button, and then the data of the stopwatch (totalSeconds) should be transferred to my php code snippet. My php code snippet is supposed to save the totalSeconds in my PhpMyAdmin database. but that's not what's happening. My stopwatch data is not being saved in my database.
In my header.php:
<head>
<script>
//STOPWATCH JAVASCRIPT CODE
var stopwatchInterval; // Variable to store the interval ID
var startTime; // Variable to store the start time
var totalSeconds = 0; // Variable to store the total number of seconds
window.onload = function() {
if (window.location.href.indexOf('/questions/') > -1) {
startTime = new Date().getTime();
var stopwatchDiv = document.createElement('div');
stopwatchDiv.id = 'stopwatch';
stopwatchDiv.style.position = 'fixed';
stopwatchDiv.style.top = '40px';
stopwatchDiv.style.right = '20px';
stopwatchDiv.style.background = '#ffffff';
stopwatchDiv.style.padding = '20px';
stopwatchDiv.style.borderRadius = '10px';
stopwatchDiv.style.zIndex = '9999';
stopwatchDiv.style.fontSize = '24px'; // Increase font size for better visibility
stopwatchDiv.innerText = '00:00:00';
document.body.appendChild(stopwatchDiv);
stopwatchInterval = setInterval(function() {
var currentTime = new Date().getTime();
var timeDifference = currentTime - startTime;
var totalMilliseconds = Math.floor(timeDifference); // Calculate the total number of milliseconds
totalSeconds = timeDifference / 1000; // Calculate the total number of seconds
var hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
var milliseconds = totalMilliseconds % 1000; // Extract milliseconds from the total milliseconds
var formattedMilliseconds = milliseconds.toString().padStart(3, '0').slice(0, 2); // Format milliseconds to 2 significant digits
var formattedTime = (hours < 10 ? "0" + hours : hours) + ":" +
(minutes < 10 ? "0" + minutes : minutes) + ":" +
(seconds < 10 ? "0" + seconds : seconds) + "." +
formattedMilliseconds; // Include formatted milliseconds in the time string
document.getElementById('stopwatch').innerText = formattedTime;
}, 1); // Change the interval to 1 millisecond
}
// Save the totalSeconds in the database
var submitButton = document.querySelector('input[name="submit_answer"]');
if (submitButton) {
submitButton.addEventListener('click', function() {
clearInterval(stopwatchInterval); // Stop the stopwatch interval when the submit button is clicked
console.log("Total seconds: " + totalSeconds); // Log the total number of seconds in the console
// send the total seconds to my php code snippet
var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>'; // Get the correct URL path to admin-ajax.php
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('action=save_total_seconds&total_seconds=' + totalSeconds);
});
};
};
</script>
</head>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MY SHORTCODE PHP CODE SNIPPET I HAVE IN MY https://mathspeedtest.com/questions/ page:
Only focus on the BOLD part of the code. everything else works fine.
$total_seconds = $_POST['total_seconds']; // Make sure to sanitize the input
$data = array('time_taken' => $total_seconds);
$where = array('user_name' => $user_name);
$wpdb->update($table_name, $data, $where);
<?php
get_header();
// Check if the form has been submitted
if (isset($_POST['submit_answer'])) {
$totalScore = 0; // Initialize total score
$userAnswers = $_POST['answerIn']; // Get the user's answers from the submitted form
$user_name = $_SESSION['user_name']; // Retrieve the username from the session
for ($i = 1; $i <= 20; $i++) {
$answerKey = 'answer' . $i;
if ($userAnswers[$i] == $_SESSION[$answerKey]) {
$totalScore++;
}
}
global $wpdb; // Access the global $wpdb object
$table_name = $wpdb->prefix . 'users_test_info'; // Prefix the table name with the WordPress prefix
// Check if the user already exists in the database
$user_exists = $wpdb->get_var($wpdb->prepare("SELECT user_name FROM $table_name WHERE user_name = %s", $user_name));
if ($user_exists) {
// If the user exists, update their test score
$data = array('test_score' => $totalScore);
$where = array('user_name' => $user_name);
$wpdb->update($table_name, $data, $where);
} else {
// If the user doesn't exist, insert a new record
$data = array(
'user_name' => $user_name,
'test_score' => $totalScore
);
$wpdb->insert($table_name, $data);
}
// Retrieve the current user_id
$current_user_id = $wpdb->get_var($wpdb->prepare("SELECT user_id FROM $table_name WHERE user_name = %s", $user_name));
$_SESSION['current_user_id'] = $current_user_id;
$questions_table = $wpdb->prefix . 'users_questions_info'; // Prefix the table name with the WordPress prefix
// Check if the user exists in the questions table
$user_exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $questions_table WHERE user_id = %d", $current_user_id));
$questions_data = array();
for ($i = 1; $i <= 20; $i++) {
$question_key = 'Q' . $i;
$user_answer_key = 'Q' . $i . 'UserAns';
$correct_answer_key = 'Q' . $i . 'CorrectAns';
$TestingNum1 = strval($_SESSION['questions'][$i - 1]['number1']);
$TestingNum2 = strval($_SESSION['questions'][$i - 1]['number2']);
$questions_data[$question_key] = "What is $TestingNum1 + $TestingNum2 ?";
$questions_data[$user_answer_key] = $userAnswers[$i];
$questions_data[$correct_answer_key] = $_SESSION['answer' . $i];
}
if ($user_exists) {
// User exists, update the existing row, including questions
$where = array('user_id' => $current_user_id);
$wpdb->update($questions_table, $questions_data, $where);
} else {
// User doesn't exist, insert a new row
$questions_data['user_id'] = $current_user_id;
$wpdb->insert($questions_table, $questions_data);
}
sleep(3);// wait 3 seconds incase the database is slow or to allow time for data to transfer
$total_seconds = $_POST['total_seconds']; // Make sure to sanitize the input
$data = array('time_taken' => $total_seconds);
$where = array('user_name' => $user_name);
$wpdb->update($table_name, $data, $where);
// Direct the user to a different webpage
wp_redirect(admin_url('/results-page/'));
exit;
} else {
// Generating 20 random numbers and calculating answers
$_SESSION['questions'] = array();
$userAnswers = array();
for ($i = 1; $i <= 20; $i++) {
$number1 = rand(1, 10); // Generate a random number between 1 and 10
$number2 = rand(1, 10); // Generate another random number between 1 and 10
$answer = $number1 + $number2;
$_SESSION['questions'][] = array(
'number1' => $number1,
'number2' => $number2
);
$answerKey = 'answer' . $i;
$_SESSION[$answerKey] = $answer;
$userAnswers[$i] = '';
}
}
?>
<form method="POST">
<?php for ($i = 1; $i <= 20; $i++) : ?>
<p>Question <?php echo $i; ?>: What is <?php echo $_SESSION['questions'][$i - 1]['number1']; ?> + <?php echo $_SESSION['questions'][$i - 1]['number2']; ?>?</p>
<input type="text" name="answerIn[<?php echo $i; ?>]" placeholder="Enter your answer" required><br>
<?php endfor; ?>
<div class="centered-container-ForSubmitBtn">
<input type="submit" name="submit_answer" value="Submit" style="border: 1px solid;">
</div>
</form>
<?php
get_footer();
?>
I tried to debug by printing out the value for the total seconds from stopwatch:
<?php
$total_seconds = $_POST['total_seconds']; // Make sure to sanitize the input
var_dump($total_seconds);
?>
and this is the result that I'm getting:
If it was working properly, I should be getting the value of the $total_seconds (i.e. 49.425), just as an example.
Note: Line 35 is $total_seconds = $_POST['total_seconds'];
Upvotes: 0
Views: 77
Reputation: 5294
It seems that you are trying to manage two different request (XMLHttpRequest and a form submit from the browser) as if they were a single request. In fact, XMLHttpRequest will generate a first request and immediately after the form submit will generate another.
From your code I assume that the endpoint of the XMLHttpRequest is the same of the form submit. So the second call (form submit) will probably cancel the first one (XMLHttpRequest).
Even if that doesn't happen, the request invoked by XMLHttpRequest will send only the action and total_seconds variables, so when the PHP script is executed it can't find any submit_answer variable and the if (isset($_POST['submit_answer'])) {
will skip to the else
branch and the code:
sleep(3);// wait 3 seconds incase the database is slow or to allow time for data to transfer
$total_seconds = $_POST['total_seconds']; // Make sure to sanitize the input
$data = array('time_taken' => $total_seconds);
$where = array('user_name' => $user_name);
$wpdb->update($table_name, $data, $where);
will not be executed (the sleep(3) is anyway unnecessary).
Instead, when the form is submitted, the request does not contains any total_seconds field and thus the error is displayed.
Bearing in mind that anyone could tamper with the data whether sent via XMLHttpRequest or via form submit, I suggest you this approach:
The section of JS code that manage this functionality will result as:
// Save the totalSeconds in the database
const submitButton = document.querySelector('input[name="submit_answer"]');
if (submitButton) {
submitButton.addEventListener('click', function() {
clearInterval(stopwatchInterval); // Stop the stopwatch interval when the submit button is clicked
console.log("Total seconds: " + totalSeconds); // Log the total number of seconds in the console
// ADDED CODE
const input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", "total_seconds");
input.setAttribute("value", totalSeconds);
this.form.appendChild(input);
/* REMOVED CODE
// send the total seconds to my php code snippet
var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>'; // Get the correct URL path to admin-ajax.php
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('action=save_total_seconds&total_seconds=' + totalSeconds);
*/
});
}
In any case remove the sleep(3)
from the PHP
Upvotes: 2