446 lines
16 KiB
PHP
446 lines
16 KiB
PHP
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Flashy</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="stylesheet" href="static/reset.css">
|
|
<link rel="stylesheet" href="static/style.css">
|
|
</head>
|
|
<body>
|
|
<?php
|
|
// Following 2 lines are for debugging only and may be removed for security.
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 1);
|
|
|
|
class Auth {
|
|
private $db;
|
|
|
|
public function __construct() {
|
|
if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] === '' ||
|
|
!isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_PW'] === '')
|
|
{
|
|
$this->respondWithAccessDenied();
|
|
}
|
|
|
|
$dbFileName = preg_replace("/[^A-Za-z0-9]/", '', $_SERVER['PHP_AUTH_USER']) . '.db';
|
|
if(!file_exists($dbFileName)) {
|
|
$this->respondWithAccessDenied();
|
|
}
|
|
|
|
$this->db = new DB($dbFileName);
|
|
if($this->db == null) {
|
|
$this->respondWithAccessDenied();
|
|
}
|
|
|
|
$pass = $this->db->getConfigValueByKey('PASSWORD');
|
|
if(hash('sha256', $_SERVER['PHP_AUTH_PW']) !== $pass) {
|
|
$this->respondWithAccessDenied();
|
|
}
|
|
}
|
|
|
|
public function getDBHandle() {
|
|
return $this->db;
|
|
}
|
|
|
|
private function respondWithAccessDenied() {
|
|
header('HTTP/1.0 401 Unauthorized');
|
|
header('WWW-Authenticate: Basic realm="Flashy"');
|
|
echo 'You are not authorized to access this site. Please log in.';
|
|
exit;
|
|
}
|
|
}
|
|
|
|
class Card {
|
|
public $id, $boxID, $classID, $front, $back;
|
|
public function __construct($id, $boxID, $classID, $front, $back) {
|
|
$this->id=$id;
|
|
$this->boxID=$boxID;
|
|
$this->classID=$classID;
|
|
$this->front=$front;
|
|
$this->back=$back;
|
|
}
|
|
|
|
public static function createFromDBRow($row) {
|
|
return new Card($row['id'], $row['box_id'], $row['class_id'], $row['front'], $row['back']);
|
|
}
|
|
}
|
|
|
|
class DB {
|
|
private $db;
|
|
|
|
public function __construct($dbFileName) {
|
|
try {
|
|
$this->db = new PDO('sqlite:' . $dbFileName);
|
|
$this->db->query("PRAGMA foreign_keys = ON");
|
|
}
|
|
catch(PDOException $e) {
|
|
$this->db = null;
|
|
}
|
|
}
|
|
|
|
public function __destruct() {
|
|
$db = null;
|
|
}
|
|
|
|
public function databaseIsOpen() {
|
|
if($this->db != null) return true;
|
|
else return false;
|
|
}
|
|
|
|
public function getClasses() {
|
|
return $this->db->query("SELECT * FROM classes");
|
|
}
|
|
|
|
public function getBoxes() {
|
|
return $this->db->query("SELECT * FROM boxes");
|
|
}
|
|
|
|
public function getConfigValueByKey($key) {
|
|
$query = $this->db->prepare("SELECT value FROM config where key=?");
|
|
$query->execute([$key]);
|
|
return $query->fetchColumn();
|
|
}
|
|
|
|
public function getBoxNameByBoxID($boxID) {
|
|
$query = $this->db->prepare("SELECT name FROM boxes WHERE id=? LIMIT 1");
|
|
$query->execute([$boxID]);
|
|
return $query->fetchColumn();
|
|
}
|
|
|
|
public function getNewBoxIDOnCorrectByBoxID($boxID) {
|
|
$query = $this->db->prepare("SELECT class_id_on_correct FROM boxes WHERE id=? LIMIT 1");
|
|
$query->execute([$boxID]);
|
|
return $query->fetchColumn();
|
|
}
|
|
|
|
public function getNewBoxIDOnIncorrectByBoxID($boxID) {
|
|
$query = $this->db->prepare("SELECT class_id_on_incorrect FROM boxes WHERE id=? LIMIT 1");
|
|
$query->execute([$boxID]);
|
|
return $query->fetchColumn();
|
|
}
|
|
|
|
public function getClassNameByClassID($classID) {
|
|
$query = $this->db->prepare("SELECT name FROM classes WHERE id=? LIMIT 1");
|
|
$query->execute([$classID]);
|
|
return $query->fetchColumn();
|
|
}
|
|
|
|
public function getNumBoxes() {
|
|
return $this->db->query("SELECT COUNT(*) FROM boxes")->fetchColumn();
|
|
}
|
|
|
|
public function getFirstCardByBoxID($boxID) {
|
|
$query = $this->db->prepare("SELECT * FROM cards WHERE box_id=? LIMIT 1");
|
|
$query->execute([$boxID]);
|
|
$res = $query->fetchAll();
|
|
if (count($res) > 0) {
|
|
$row = $res[0];
|
|
return Card::createFromDBRow($row);
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function getCardByCardID($card_id) {
|
|
$res = $this->db->query("SELECT * FROM cards WHERE id = '" . $card_id . "' LIMIT 1")->fetchAll();
|
|
if (count($res) > 0) {
|
|
$row = $res[0];
|
|
return Card::createFromDBRow($row);
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function getNumCardsByBoxID($boxID) {
|
|
return $this->db->query("SELECT COUNT(*) FROM cards where box_id='" . $boxID . "'")->fetchColumn();
|
|
}
|
|
|
|
public function getNumCards() {
|
|
return $this->db->query("SELECT COUNT(*) FROM cards")->fetchColumn();
|
|
}
|
|
|
|
public function updateCard($card) {
|
|
$origCard = $this->getCardByCardID($card->id);
|
|
if($origCard->boxID != $card->boxID) {
|
|
$this->db->prepare("UPDATE cards SET box_id=? WHERE id=?")->execute([$card->boxID, $card->id]);
|
|
}
|
|
if($origCard->classID != $card->classID) {
|
|
$this->db->prepare("UPDATE cards SET class_id=? WHERE id=?")->execute([$card->classID, $card->id]);
|
|
}
|
|
if($origCard->front != $card->front) {
|
|
$this->db->prepare("UPDATE cards SET front=? WHERE id=?")->execute([$card->id]);
|
|
}
|
|
if($origCard->back != $card->back) {
|
|
$this->db->prepare("UPDATE cards SET back=? WHERE id=?")->execute([$card->back, $card->id]);
|
|
}
|
|
}
|
|
|
|
public function createCard($card) {
|
|
if($card->classID != null && $card->front != null && $card->back != null) {
|
|
$this->db->prepare("INSERT INTO cards (box_id,class_id,front,back) VALUES(?,?,?,?)")
|
|
->execute([$card->boxID, $card->classID, $card->front, $card->back]);
|
|
}
|
|
}
|
|
|
|
public function deleteCard($cardID) {
|
|
if($cardID != null) {
|
|
$this->db->prepare("DELETE FROM cards WHERE id=?")->execute([$cardID]);
|
|
}
|
|
}
|
|
|
|
public function getRandomCardByBoxID($boxID) {}
|
|
public function getAllCardsByBoxID($boxID) {}
|
|
}
|
|
|
|
class App {
|
|
private $db,
|
|
$selectedCard,
|
|
$selectedBoxID,
|
|
$testInput,
|
|
$message,
|
|
$checkCase = true;
|
|
|
|
public function __construct($db) {
|
|
if($db == null) {
|
|
echo 'App cannot start without an active database. Good bye!';
|
|
exit;
|
|
}
|
|
$this->db = $db;
|
|
$this->handleAllInput();
|
|
$this->selectCardIfBoxSelected();
|
|
$this->runTestIfUserInput();
|
|
$this->renderTest();
|
|
$this->renderBoxes();
|
|
$this->renderEditor();
|
|
}
|
|
|
|
private function renderTest() {
|
|
echo '<div class="test_view">' . "\n";
|
|
echo '<div class="std_row">' . $this->message . "</div>\n";
|
|
echo '<div class="std_row">'.($this->selectedCard == null ? '' : $this->selectedCard->front.' | '.$this->db->getClassNameByClassID($this->selectedCard->classID))."</div>\n";
|
|
echo '<form method="POST">' . "\n";
|
|
echo '<input type="text" name="test_input" autocomplete="off"'
|
|
.($this->testInput == null ? '' : ' value="' . $this->testInput . '"')
|
|
.($this->selectedCard == null ? '' : ' autofocus')
|
|
.'>'."\n";
|
|
echo '<input class="btn_row" type="submit" value="Check">' . "\n";
|
|
echo '<input type="hidden" name="card_id" value="'.($this->selectedCard == null ? '' : $this->selectedCard->id).'">'."\n";
|
|
echo '</form>' . "\n";
|
|
echo '<a class="btn_row'.($this->checkCase ? ' selected' : '').'" href="'.$this->createExtendedGetParameterString('check_case', (int)!$this->checkCase).'">Case sensitive</a>' . "\n";
|
|
echo "</div>\n";
|
|
}
|
|
|
|
private function selectCardIfBoxSelected() {
|
|
// Select random card from selected box since none is selected yet:
|
|
if($this->selectedBoxID != null && $this->selectedCard == null) {
|
|
$this->selectNewCard();
|
|
/*
|
|
if($this->selectedCard != null) {
|
|
// Give the user some info...
|
|
$this->message = $this->selectedCard->front.' | '.$this->db->getClassNameByClassID($this->selectedCard->classID);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
private function runTestIfUserInput() {
|
|
// Do some evaluation...
|
|
if(isset($this->selectedCard) && $this->testInput != null) {
|
|
// Copy values to be mangled :)
|
|
$a = $this->selectedCard->back;
|
|
$b = $this->testInput;
|
|
// Normalize Umlauts
|
|
$umlauts = array(
|
|
"ä"=>"ae",
|
|
"Ä"=>"Ae",
|
|
"ö"=>"oe",
|
|
"Ö"=>"Oe",
|
|
"ü"=>"ue",
|
|
"Ü"=>"Ue",
|
|
"ß"=>"ss",
|
|
"ẞ"=>"Ss"
|
|
);
|
|
$a = str_replace(array_keys($umlauts), array_values($umlauts), $a);
|
|
$b = str_replace(array_keys($umlauts), array_values($umlauts), $b);
|
|
// Case insensitive...
|
|
if(!$this->checkCase) {
|
|
$a = strtolower($a);
|
|
$b = strtolower($b);
|
|
}
|
|
//Answer correct:
|
|
if($a == $b) {
|
|
if($this->selectedCard->back == $this->testInput) {
|
|
$this->message = 'Correct';
|
|
}
|
|
else {
|
|
$this->message = 'Correct spelling but wrong upper/lower case';
|
|
}
|
|
/*
|
|
if($this->selectedCard->boxID >= 0 && $this->selectedCard->boxID < 3) {
|
|
$this->selectedCard->boxID = 4;
|
|
}
|
|
else if($this->selectedCard->boxID >= 3 && $this->selectedCard->boxID < $this->db->getNumBoxes()-1) {
|
|
$this->selectedCard->boxID += 1;
|
|
}
|
|
*/
|
|
$this->selectedCard->boxID = $this->db->getNewBoxIDOnCorrectByBoxID($this->selectedCard->boxID);
|
|
}
|
|
//Answer wrong:
|
|
else {
|
|
$this->message = 'Wrong - Correct answer:<br>'.$this->selectedCard->back;
|
|
/*
|
|
if($this->selectedCard->boxID > 0 && $this->selectedCard->boxID <= 3) {
|
|
$this->selectedCard->boxID -= 1;
|
|
}
|
|
else if($this->selectedCard->boxID > 3 && $this->selectedCard->boxID < $this->db->getNumBoxes()) {
|
|
$this->selectedCard->boxID = 2;
|
|
}
|
|
*/
|
|
$this->selectedCard->boxID = $this->db->getNewBoxIDOnIncorrectByBoxID($this->selectedCard->boxID);
|
|
}
|
|
// Flush box_id update to database
|
|
$this->db->updateCard($this->selectedCard);
|
|
// Clear the input field
|
|
$this->testInput = '';
|
|
// select the next card for the new round
|
|
$this->selectNewCard();
|
|
}
|
|
}
|
|
|
|
private function renderBoxes() {
|
|
echo '<div class="box_view">'."\n";
|
|
echo "<ul>\n";
|
|
foreach ($this->db->getBoxes() as $box) {
|
|
$this->addGetParameter('box_id', $box['id']);
|
|
$this->addGetParameter('check_case', $this->checkCase);
|
|
if (isset($this->selectedBoxID)) {
|
|
$html_selected = $box['id'] == $this->selectedBoxID ? ' class="selected"' : '';
|
|
}
|
|
else {
|
|
$html_selected = '';
|
|
}
|
|
|
|
echo "<li>\n";
|
|
echo '<a href="'.$this->createGetParameterString().'"'.$html_selected.'>'.$box['name'].' ('.$this->db->getNumCardsByBoxID($box['id']).")</a>\n";
|
|
echo "</li>\n";
|
|
}
|
|
echo "</ul>\n";
|
|
echo "</div>\n";
|
|
}
|
|
|
|
private function renderEditor() {
|
|
echo '<div class="editor_view">' . "\n";
|
|
echo '<form method="POST">' . "\n";
|
|
echo '<div><input type="text" name="card_front" value="'.($this->selectedCard != null ? $this->selectedCard->front : 'Front').'"></div>' . "\n";
|
|
#echo '<div><input type="text" name="card_back" value="'. ($this->selectedCard != null ? $this->selectedCard->back : 'Back') .'"></div>' . "\n";
|
|
echo '<div><input type="text" name="card_back" value="'. ($this->selectedCard != null ? '' : 'Back') .'"></div>' . "\n";
|
|
echo '<select name="class_id">'."\n";
|
|
foreach ($this->db->getClasses() as $class) {
|
|
if($this->selectedCard != null) {
|
|
$setSelected = $class['id'] == $this->selectedCard->classID ? ' selected="selected"' : '';
|
|
}
|
|
else {
|
|
$setSelected = '';
|
|
}
|
|
echo '<option value="'.$class['id'].'"'.$setSelected.'>'.$class['name']."</option>\n";
|
|
}
|
|
echo "</select>\n";
|
|
echo '<div><input class="btn_row" type="submit" name="action_create" value="Create"></div>' . "\n";
|
|
echo '<div><input class="btn_row" type="submit" name="action_replace" value="Replace"></div>' . "\n";
|
|
echo '<div><input class="btn_row" type="submit" name="action_delete" value="Delete"></div>' . "\n";
|
|
echo '<input type="hidden" name="card_id" value="'.($this->selectedCard == null ? '' : $this->selectedCard->id).'">'."\n";
|
|
echo '</form>' . "\n";
|
|
echo "</div>\n";
|
|
}
|
|
|
|
private function handleAllInput() {
|
|
if (isset($_REQUEST['box_id']) &&
|
|
$_REQUEST['box_id'] >= 0 &&
|
|
$_REQUEST['box_id'] < $this->db->getNumBoxes())
|
|
{
|
|
$this->selectedBoxID = $_REQUEST['box_id'];
|
|
}
|
|
if (isset($_REQUEST['card_id']) &&
|
|
$_REQUEST['card_id'] >= 0)
|
|
{
|
|
$this->selectedCard = $this->db->getCardByCardID($_REQUEST['card_id']);
|
|
}
|
|
if (isset($_REQUEST['check_case']) &&
|
|
$_REQUEST['check_case'] == 1)
|
|
{
|
|
$this->checkCase = true;
|
|
}
|
|
else $this->checkCase = false;
|
|
|
|
if (isset($_REQUEST['test_input']) && $_REQUEST['test_input'] != '') {
|
|
$this->testInput = trim($_REQUEST['test_input']);
|
|
}
|
|
|
|
// Create a new Card in the database
|
|
if (isset($_REQUEST['action_create']) &&
|
|
isset($_REQUEST['class_id']) && $_REQUEST['class_id'] != null &&
|
|
isset($_REQUEST['card_front']) && $_REQUEST['card_front'] != null &&
|
|
isset($_REQUEST['card_back']) && $_REQUEST['card_back'] != null)
|
|
{
|
|
$card = new Card(null, (int)$this->db->getConfigValueByKey('NEW_CARD_BOX_ID'), $_REQUEST['class_id'], trim($_REQUEST['card_front']), trim($_REQUEST['card_back']));
|
|
$this->db->createCard($card);
|
|
}
|
|
// Modify an existing card in the database
|
|
else if (isset($_REQUEST['action_replace']) &&
|
|
isset($_REQUEST['class_id']) && $_REQUEST['class_id'] != null &&
|
|
isset($_REQUEST['card_front']) && $_REQUEST['card_front'] != null &&
|
|
isset($_REQUEST['card_back']) && $_REQUEST['card_back'] != null &&
|
|
$this->selectedCard != null)
|
|
{
|
|
$card = new Card($this->selectedCard->id, $this->selectedCard->boxID, $_REQUEST['class_id'], trim($_REQUEST['card_front']), trim($_REQUEST['card_back']));
|
|
$this->db->updateCard($card);
|
|
// Reload the updated card
|
|
$this->selectedCard = $this->db->getCardByCardID($card->id);
|
|
}
|
|
// Delete a card from the database
|
|
else if (isset($_REQUEST['action_delete']) &&
|
|
$this->selectedCard != null)
|
|
{
|
|
$this->db->deleteCard($this->selectedCard->id);
|
|
// Make sure the deleted card is no longer selected
|
|
$this->selectedCard = null;
|
|
}
|
|
}
|
|
|
|
private function selectNewCard() {
|
|
$this->selectedCard = $this->db->getFirstCardByBoxID($this->selectedBoxID);
|
|
}
|
|
|
|
private function addGetParameter($parameterName, $parameterValue) {
|
|
$_GET[$parameterName] = $parameterValue;
|
|
}
|
|
|
|
private function createGetParameterString() {
|
|
$tmp = '?';
|
|
foreach($_GET as $key => $value) {
|
|
$tmp = $tmp . $key . '=' . $value . '&';
|
|
}
|
|
return trim($tmp, '&');//substr($tmp, 0, -1);
|
|
}
|
|
|
|
//Add a temporary extra key/value pair to string
|
|
private function createExtendedGetParameterString($key, $value) {
|
|
$this->deleteGetParameter($key);
|
|
$existingParams = $this->createGetParameterString();
|
|
return $existingParams == '?' ? '?'.$key.'='.$value : $existingParams.'&'.$key.'='.$value;
|
|
}
|
|
|
|
private function deleteGetParameter($key) {
|
|
unset($_GET[$key]);
|
|
}
|
|
}
|
|
//$db = new DB('username.db');
|
|
//new App($db);
|
|
$auth = new Auth();
|
|
new App($auth->getDBHandle());
|
|
?>
|
|
</body>
|
|
</html>
|