Commit 1cd586b8 authored by William Paul Liggett's avatar William Paul Liggett

Admin Page: The capability to add user tests with an auto-generated login code works!

parent 2b71078a
......@@ -89,7 +89,7 @@
echo "$tab$tab<form action=\".\" name=\"ScheduledTests\" method=\"post\">" . PHP_EOL;
// Sets up the <table> heading.
echo "$tab$tab$tab<table>" . PHP_EOL .
echo "$tab$tab$tab<table name=\"table_scheduled_tests\" id=\"table_scheduled_tests\">" . PHP_EOL .
"$tab$tab$tab$tab<thead>" . PHP_EOL .
"$tab$tab$tab$tab$tab<tr>" . PHP_EOL .
"$tab$tab$tab$tab$tab$tab<th>&nbsp;</th>" . PHP_EOL .
......@@ -128,6 +128,25 @@
"$tab$tab</form>" . PHP_EOL;
// ---------------------------------------------------------------------------------------------------------
// Pop-up Dialog Form: This the sub-form to add a scheduled test. Uses jQuery UI to pop-out when needed.
echo "$tab$tab<div id=\"dialog_form_add_test\" title=\"Add Test\" class=\"hidden\">" . PHP_EOL .
"$tab$tab$tab<p class=\"validateTips\">All form fields are required.</p>" . PHP_EOL .
"$tab$tab$tab<form id=\"AddTest\">" . PHP_EOL .
"$tab$tab$tab$tab<fieldset>" . PHP_EOL .
//"$tab$tab$tab$tab$tab<input type=\"hidden\" name=\"add_test_sk\" id=\"add_test_sk\" value=\"\" />" . PHP_EOL .
"$tab$tab$tab$tab$tab<label for=\"add_test_subject_id\">Subject ID</label>" . PHP_EOL .
"$tab$tab$tab$tab$tab<input type=\"text\" name=\"add_test_subject_id\" id=\"add_test_subject_id\" value=\"\" class=\"text ui-widget-content ui-corner-all\" />" . PHP_EOL .
"$tab$tab$tab$tab$tab<label for=\"add_test_condition\">Test Condition</label>" . PHP_EOL .
"$tab$tab$tab$tab$tab<input type=\"text\" name=\"add_test_condition\" id=\"add_test_condition\" value=\"\" class=\"text ui-widget-content ui-corner-all\" />" . PHP_EOL .
"$tab$tab$tab$tab$tab<label for=\"add_login_code\">Login Code</label>" . PHP_EOL .
"$tab$tab$tab$tab$tab<input type=\"text\" name=\"add_login_code\" id=\"add_login_code\" value=\"Auto-generated.\" disabled=\"disabled\" title=\"Cannot edit as the code is auto-generated.\" class=\"text ui-widget-content ui-corner-all\" />" . PHP_EOL .
// Allow form submission with keyboard without duplicating the dialog button
"$tab$tab$tab$tab$tab<input type=\"submit\" tabindex=\"-1\" style=\"position:absolute; top:-1000px\">" . PHP_EOL .
"$tab$tab$tab$tab</fieldset>" . PHP_EOL .
"$tab$tab$tab</form>" . PHP_EOL .
"$tab$tab</div>" . PHP_EOL;
// Pop-up Dialog Form: This the sub-form to edit a scheduled test. Uses jQuery UI to pop-out when needed.
echo "$tab$tab<div id=\"dialog_form_edit_test\" title=\"Edit Test\" class=\"hidden\">" . PHP_EOL .
"$tab$tab$tab<p class=\"validateTips\">All form fields are required.</p>" . PHP_EOL .
......
......@@ -9,25 +9,29 @@
* Purpose: Function to standardise the output of various Ajax requests using JSON as the data storage.
* -------------------------------------------------------------------------------------------------------------- */
function output_json_response($successful = false, $error_message = "Incorrect usage.") {
function output_json_response($successful = false, $error_message = "Incorrect usage.", $extra_data1 = "", $extra_data2 = "") {
// Ensures the variables must be set correctly.
if(!isset($successful) || !is_bool($successful)) {
$successful = false;
$error_message = "output_json_response($successful, $error_message): 1st argument, $successful, must be a boolean value.";
$error_message = "output_json_response($successful, $error_message, $extra_data): 1st argument, $successful, must be a boolean value.";
}
if(!isset($error_message) || !is_string($error_message)) {
$successful = false;
$error_message = "output_json_response($successful, $error_message): 2nd argument, $error_message, must be a string.";
$error_message = "output_json_response($successful, $error_message, $extra_data): 2nd argument, $error_message, must be a string.";
}
// Gets rid of potentially harmful injection characters.
$error_message = htmlspecialchars($error_message);
$extra_data1 = htmlspecialchars($extra_data1);
$extra_data2 = htmlspecialchars($extra_data2);
// JSON array to output as a web service.
$json_data = [
'successful' => $successful,
'error_message' => $error_message,
'extra_data1' => $extra_data1,
'extra_data2' => $extra_data2,
];
echo json_encode($json_data);
......
<?php
/* --------------------------------------------------------------------------------------------------------------
* Copyright (C) 2018 by William Paul Liggett (junktext@junktext.com)
* This Source Code Form is subject to the terms of the Mozilla Public License (MPL), v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Filename: scheduled_test_add.php
*
* Purpose: Add a new OpenVigilance Task test in the database.
* The page is meant to be used as a simple web service, such as with Ajax or the like.
*
* Output: JSON array as defined in ``output_json_response.php''.
* -------------------------------------------------------------------------------------------------------------- */
// Maintains the authenticated user session across different admin pages.
// Ensures only valid administrators can do anything.
session_start();
if(!isset($_SESSION['ov_admin_user'])) {
output_json_response(false, "Not logged on as an administrator.");
return;
}
// Function: output_json_response($successful, $error_message)
require_once "output_json_response.php";
// -------------------------------------------------------------------------------
// POST input variables sent by the web browser.
// Note: The login_code cannot be set by an OV admin. The code is auto-generated.
// -------------------------------------------------------------------------------
//$sk = intval($_POST['add_test_sk']); // Surrogate Key identifier of the scheduled test in the database.
$subject_id = intval($_POST['add_test_subject_id']); // The ID of the person meant to take the test.
$test_condition = intval($_POST['add_test_condition']); // The kind of test being taken.
// Validate the data.
if($subject_id <= 0 || $test_condition <= 0) {
output_json_response(false, "The subject_id nor the test_condition cannot be less than or equal to zero.");
return;
}
else if($test_condition > 4) {
output_json_response(false, "The test_condition cannot be greater than 4.");
return;
}
// Logs into the OpenVigilance Task tests database to control and alter user tests.
// `$pdo' is defined as the database connection.
require_once "../../../../../protected_site_configs/junktext.com/openvigilance_db_connection.php";
// Auto-generate the subject's (user's) login code.
$login_code = "";
while(strlen($login_code) !== 4) {
$rand_num = rand(48, 122); // ASCII: Between numbers and the letter 'z'.
// Only create a login code that matches the style of numbers or lower-case letters, like: 7a2b
// So, we don't want these ASCII codes. (Punctuation and Upper-case letters.)
if($rand_num >= 58 and $rand_num <= 96) {
continue;
}
else {
// Appends the ASCII char to the `$login_code' string.
$login_code .= chr($rand_num);
}
}
// Test Control: Add the OV test to the database.
$sql = "INSERT INTO test_control (subject_id, test_condition, login_code) VALUES (:subject_id, :test_condition, :login_code)";
$statement = $pdo->prepare($sql);
$statement->bindValue(":subject_id", $subject_id, PDO::PARAM_INT);
$statement->bindValue(":test_condition", $test_condition, PDO::PARAM_INT);
$statement->bindValue(":login_code", $login_code, PDO::PARAM_STR);
$successful_insert = $statement->execute(); // true or false
if($successful_insert) {
// Finds the new surrogate key (sk) from the recently inserted row.
$sql = "SELECT sk WHERE subject_id=:subject_id, test_condition=:test_condition, login_code=:login_code";
$statement = $pdo->prepare($sql);
$statement->bindValue(":subject_id", $subject_id, PDO::PARAM_INT);
$statement->bindValue(":test_condition", $test_condition, PDO::PARAM_INT);
$statement->bindValue(":login_code", $login_code, PDO::PARAM_STR);
$statement->execute();
$new_sk = $statement->fetch()[0];
// Inform the web browser that all went well and informs the Ajax client what the login_code is for the subject.
output_json_response(true, "", strval($login_code), strval($new_sk));
}
else {
// The database was not properly updated for some reason.
output_json_response(false, "The input data was valid, but the database could not be updated at this time for some reason.");
}
......@@ -33,8 +33,8 @@ $subject_id = intval($_POST['edit_test_subject_id']); // The ID of the person m
$test_condition = intval($_POST['edit_test_condition']); // The kind of test being taken.
// Validate the data.
if($sk <= 0 || $subject_id <= 0 || $test_condition <= 0) {
output_json_response(false, "The surrogate key (sk), subject_id, nor the test_condition cannot be less than or equal to zero.");
if($subject_id <= 0 || $test_condition <= 0) {
output_json_response(false, "The subject_id nor the test_condition cannot be less than or equal to zero.");
return;
}
......@@ -43,17 +43,63 @@ else if($test_condition > 4) {
return;
}
else if($sk <= 0) {
output_json_response(false, "Invalid surrogate key (sk).");
return;
}
// Logs into the OpenVigilance Task tests database to control and alter user tests.
// `$pdo' is defined as the database connection.
require_once "../../../../../protected_site_configs/junktext.com/openvigilance_db_connection.php";
// Test Control: Submit the edit request to the database.
// Uses MySQL transactions to be atomic.
// MySQL Command: START TRANSACTION;
$pdo->beginTransaction();
// Modifies the record in the database to match the OV admin's edit request.
$sql = "UPDATE test_control SET subject_id=:subject_id, test_condition=:test_condition WHERE sk=:sk";
$statement = $pdo->prepare($sql);
$statement->bindValue(":subject_id", $subject_id, PDO::PARAM_INT);
$statement->bindValue(":test_condition", $test_condition, PDO::PARAM_INT);
$statement->bindValue(":sk", $sk, PDO::PARAM_INT);
$db_updated = $statement->execute(); // true if the SQL was executed successfully
if($db_updated) {
// MySQL Transaction: COMMIT;
$pdo->commit();
}
else {
// MySQL Transaction: ROLLBACK;
$pdo->rollBack();
output_json_response(false, "The input data was valid, but the database could not be updated at this time for some reason.");
return;
}
/* !@#$ ... I MIGHT TRY TO FIGURE OUT HOW TO RESOLVE THIS LATER.
*
// Ensures that two tests cannot be scheduled for the same subject at the same time.
// In other words, the subject_id cannot have two tests in the database with the value `test_scheduled=1'.
$sql = "SELECT COUNT(*) FROM test_control WHERE subject_id=:subject_id AND test_scheduled=2";
$statement = $pdo->prepare($sql);
$statement->bindValue(":subject_id", $subject_id, PDO::PARAM_INT);
$statement->execute();
$num_rows = $statement->fetch()[0];
if(intval($num_rows) === 2) {
// Deletes the record we just apparently added by mistake.
$sql = "DELETE FROM test_control WHERE sk=:sk";
$statement = $pdo->prepare($sql);
$statement->bindValue(":subject_id", $subject_id, PDO::PARAM_INT);
$statement->bindValue(":test_condition", $test_condition, PDO::PARAM_INT);
$statement->bindValue(":sk", $sk, PDO::PARAM_INT);
output_json_response(false, "The subject_id already has a test scheduled. Please edit or remove that pre-existing test instead.");
return;
}
*
*/
// Verify the database has updated the test record.
$sql = "SELECT subject_id, test_condition FROM test_control WHERE sk=:sk";
......@@ -69,9 +115,11 @@ $modified_record = $statement->fetch();
if((intval($modified_record['subject_id']) === $subject_id) && (intval($modified_record['test_condition']) === $test_condition)) {
// Inform the web browser that all went well.
output_json_response(true, "");
return;
}
else {
// The database was not properly updated for some reason.
output_json_response(false, "The input data was valid, but the database could not be updated at this time for some reason.");
return;
}
......@@ -30,19 +30,20 @@
<body>
<h1>OpenVigilance Task: Change Log</h1>
<h2>Programmer: William Paul Liggett (junktext@junktext.com)</h2>
<p><em>Total labor time: 29 hours.</em></p>
<p><em>Total labor time: 31 hours.</em></p>
<p class="version_info"><strong>To Do:</strong></p>
<ol>
<li>Admin Page: Capability to add and remove user tests.</li>
<li>Admin Page: Capability to remove user tests.</li>
<li>Subject Login Page: Allow a test participant to login if they have a test scheduled.</li>
<li>Vigilance Tests: Record the activity of each subject during the test and save it as a CSV data file on the server.</li>
<li>Admin Page: Add a section to show the stored CSV files so that they can be downloaded or deleted at will.</li>
</ol>
<p class="version_info">Version 1.5.1 (2018-02-26):</p>
<p><em>Estimated labor time: 0.5 hours.</em></p>
<p><em>Estimated labor time: 2.5 hours.</em></p>
<ul>
<li>Admin Page: The capability to add user tests with an auto-generated login code works!</li>
<li>Admin Page: Made the process to update a scheduled test to be less fragile by confirming the database was updated correctly.</li>
</ul>
......
......@@ -148,22 +148,26 @@ td {
}
/* --- START of jQuery UI: Pop-up Dialog Boxes --- */
div#dialog_form_add_test label, div#dialog_form_add_test input,
div#dialog_form_edit_test label, div#dialog_form_edit_test input {
display: block;
}
div#dialog_form_add_test input.text,
div#dialog_form_edit_test input.text {
margin-bottom: 12px;
width: 95%;
padding: 0.4em;
}
div#dialog_form_add_test fieldset,
div#dialog_form_edit_test fieldset {
padding: 0;
border: 0;
margin-top: 25px;
}
div#dialog_form_add_test h1,
div#dialog_form_edit_test h1 {
font-size: 1.2em;
margin: 0.6em 0;
......@@ -178,6 +182,7 @@ div#dialog_form_edit_test h1 {
padding: 0.3em;
}
form#AddTest input[disabled = "disabled"],
form#EditTest input[disabled = "disabled"] {
background-color: #dedede; /* Gray */
}
......
......@@ -19,13 +19,7 @@
;(function(window, $) {
$(document).ready(function() {
// Edit Test
var edit_dialog, edit_form,
edit_subject_id = $("#edit_test_subject_id"),
edit_test_condition = $("#edit_test_condition"),
edit_login_code = $("#edit_login_code"),
edit_allFields = $([]).add(edit_subject_id).add(edit_test_condition).add(edit_login_code),
tips = $(".validateTips");
var tips = $(".validateTips");
function updateTips(t) {
tips
......@@ -55,6 +49,119 @@
return true;
}
}
// -------------------------------------------------------------------------------------------------
// Add Test
// -------------------------------------------------------------------------------------------------
var add_dialog, add_form,
add_subject_id = $("#add_test_subject_id"),
add_test_condition = $("#add_test_condition"),
add_login_code = $("#add_login_code"),
add_allFields = $([]).add(add_subject_id).add(add_test_condition).add(add_login_code);
function addTest() {
var valid = true;
add_allFields.removeClass("ui-state-error");
/*
valid = valid && checkLength(name, "username", 3, 16);
valid = valid && checkLength(email, "email", 6, 80);
valid = valid && checkLength(password, "password", 5, 16);
valid = valid && checkRegexp(name, /^[a-z]([0-9a-z_\s])+$/i, "Username may consist of a-z, 0-9, underscores, spaces and must begin with a letter.");
valid = valid && checkRegexp(email, emailRegex, "eg. ui@jquery.com");
valid = valid && checkRegexp(password, /^([0-9a-zA-Z])+$/, "Password field only allow : a-z 0-9");
if (valid) {
$("#users tbody").append("<tr>" +
"<td>" + name.val() + "</td>" +
"<td>" + email.val() + "</td>" +
"<td>" + password.val() + "</td>" +
"</tr>");
dialog.dialog("close");
}
*/
if(valid) {
new_scheduled_subject_id = $("#add_test_subject_id").val();
new_scheduled_test_condition = $("#add_test_condition").val();
// The data sent to the server is what the admin wanted modified via the pop-up dialog defined further below.
// Specifically, see the Getters and Setters near the bottom.
$.ajax({
type: 'POST',
url: "../admin/scheduled_test_add.php",
dataType: "json",
data: $("form#AddTest").serialize(),
success: function(response) {
// Success! Updates the DOM by modifying the rows in the <table>.
if(response.successful === true) {
// New surrogate key (sk) for the recently added row.
var new_scheduled_login_code = response.extra_data1;
var new_scheduled_sk = response.extra_data2;
$("#table_scheduled_tests tbody").append('' +
'<tr>' +
'<td><input type="radio" name="scheduled_test" id="' + new_scheduled_sk + '" value="' + new_scheduled_sk + '" data-ov-subject-id="' + new_scheduled_subject_id + '" data-ov-test-condition="' + new_scheduled_test_condition + '" data-ov-login-code="' + new_scheduled_login_code + '" /></td>' +
'<td>' + new_scheduled_subject_id + '</td>' +
'<td>' + new_scheduled_test_condition + '</td>' +
'<td>' + new_scheduled_login_code + '</td>' +
'</tr>');
// Places the new test in the correct order (ascending) by subject_id into the <table>.
//
}
// Error
else {
alert("Error: " + response.error_message);
}
}
});
// Auto-closes the pop-up add dialog for the user.
add_dialog.dialog("close");
}
return valid;
}
add_dialog = $("#dialog_form_add_test").dialog({
autoOpen: false,
height: 400,
width: 350,
modal: true,
buttons: {
"Submit": addTest,
Cancel: function() {
add_dialog.dialog("close");
}
},
close: function() {
document.getElementById("AddTest").reset();
add_allFields.removeClass("ui-state-error");
}
});
add_form = add_dialog.find("form#dialog_form_add_test").on("submit", function(event) {
event.preventDefault();
addTest();
});
// The "Add Test" button on the main <form> that causes the pop-up dialog to show.
$("#add_test").button().on("click", function() {
// Opens the pop-up add dialog for the user.
add_dialog.dialog("open");
});
// -------------------------------------------------------------------------------------------------
// Edit Test
// -------------------------------------------------------------------------------------------------
var edit_dialog, edit_form,
edit_subject_id = $("#edit_test_subject_id"),
edit_test_condition = $("#edit_test_condition"),
edit_login_code = $("#edit_login_code"),
edit_allFields = $([]).add(edit_subject_id).add(edit_test_condition).add(edit_login_code);
function editTest() {
var valid = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment