Solution
index.php
<?php
session_start();
require_once('dbObjects.php');
function printCSS() {
echo <<<__CSS__
/* CSS inspired by http://codepen.io/alassetter/full/cyrfB/ */
@import url(http://fonts.googleapis.com/css?family=Roboto:400,500,700,300,100);
body {
background-color: #F5F5F5;
font-family: "Roboto", helvetica, arial, sans-serif;
font-size: 14px;
font-weight: 400;
}
header {
width: 100%;
height: 3em;
background-color: #ef6c00a6;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
header > .site {
float: left;
color: white;
font-weight: bold;
font-size: 2.2em;
padding: 5px;
}
header > nav {
float: right;
}
header > nav > div {
display: inline-block;
font-size: 1.6em;
color: white;
padding: 10px;
}
header > nav > div:hover {
background-color: white;
color: #EF6C00;
}
header a {
text-decoration: none;
color: inherit;
}
main {
margin: auto;
max-width: 900px;
}
div.title {
padding:5px;
width: 100%;
}
.title h1 {
color: black;
font-size: 26px;
font-weight: 500;
font-family: "Roboto", helvetica, arial, sans-serif;
text-transform:uppercase;
}
.work {
clear: both;
}
.work img {
float: right;
padding: 20px;
width: min(40%,400px);
}
.work .name {
font-weight: bold;
font-size: 4em;
display: block;
}
.work .artist {
font-style: italic;
display: block;
}
/*** Grid Styles **/
.grid {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
gap: 5px;
}
.card {
width: 250px;
padding: 8px;
border: 1px solid black;
border-radius: 10px;
flex-grow: 1;
}
.card a {
text-decoration: none;
color: inherit;
}
.card img {
max-width: min(100%, 250px);
margin: auto;
display: block;
}
.card .work {
font-weight: bold;
display: block;
text-align: center;
}
.card .artist {
font-style: italic;
display: block;
text-align: center;
}
/*** Table Styles **/
.table-fill {
background: white;
border-radius:3px;
border-collapse: collapse;
height: 320px;
padding:5px;
width: 100%;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}
.table-fill img {
width: min(100%, 200px);
}
th {
color: white;
background: #EF6C00;
border-bottom:4px solid #9ea7af;
border-right: 1px solid #343a45;
font-size:20px;
font-weight: 400;
padding:24px;
text-align:left;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
vertical-align:middle;
}
th:first-child {
border-top-left-radius:3px;
}
th:last-child {
border-top-right-radius:3px;
border-right:none;
}
tr {
border-top: 1px solid #C1C3D1;
border-bottom-: 1px solid #C1C3D1;
color:#666B85;
font-size:16px;
font-weight:normal;
text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
}
tr:first-child {
border-top:none;
}
tr:last-child {
border-bottom:none;
}
tr:last-child td:first-child {
border-bottom-left-radius:3px;
}
tr:last-child td:last-child {
border-bottom-right-radius:3px;
}
td {
background:#FFFFFF;
padding:10px;
text-align:left;
vertical-align:middle;
font-weight:300;
font-size:18px;
text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
border-right: 1px solid #C1C3D1;
}
td:last-child {
border-right: 0px;
}
tr.totals td {
background:#FFF3E0;
text-align: right;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
.focus td {
color: #E65100;
font-weight: 500;
}
form {
width: 400px;
margin-left: auto;
margin-right: auto;
}
label {
margin: 20px;
}
input {
margin: 20px;
}
__CSS__;
}
function printHeader($title) {
$userspace = '';
if (isset($_SESSION['user'])) {
if ($_SESSION['user']['user_type'] == 'admin') {
/* Admin */
$userspace .= '<div><a href="?pg=admin">Admin</a></div>';
} else {
$userspace .= '<div><a href="?pg=cart">Cart</a></div>';
}
$userspace .= '<div><a href="?pg=logout">Logout</a></div>';
} else {
/* Not logged in */
$userspace .= '<div><a href="?pg=login">Login</a></div>';
}
echo <<<__HTML__
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0,width=device-width">
<title>General Store - {$title}</title>
<link href="?pg=css" rel="stylesheet" />
</head>
<body>
<header>
<div class="site">Art Shop</div>
<nav>
<div><a href="?pg=home">Home</a></div>
{$userspace}
</nav>
</header>
<main>
__HTML__;
}
function printFooter() {
echo <<<__HTML__
</main>
</body>
</html>
__HTML__;
}
function printLogin($msg) {
echo <<<__HTML__
<form method="POST" action="?pg=home">
<h2>{$msg}</h2>
<label for="username">Username</label>
<input id="username" name="user" value="" type="text" /><br>
<label for="password">Password</label>
<input id="password" name="pass" value="" type="password" /><br>
<input type="submit" value="Login"/>
</form>
__HTML__;
}
function printCarts() {
$carts = getData('SELECT `user_display`,'.
' SUM(`ci_qty`*`variation_price`) AS total'.
' FROM `Cart`'.
' INNER JOIN `CartItem` ON `ci_cart`=`cart_id`'.
' INNER JOIN `Variation` ON `variation_id`=`ci_variation`'.
' INNER JOIN `User` ON `user_id`=`cart_user`'.
' WHERE `cart_status`=\'open\''.
' GROUP BY `user_id`, `user_display`',[]);
$variations = getData('SELECT `variation_name`,'.
' `work_name`,'.
' `artist_name`,'.
' SUM(`ci_qty`) AS total'.
' FROM `Variation`'.
' INNER JOIN `CartItem` ON `variation_id`=`ci_variation`'.
' INNER JOIN `Work` ON `work_id`=`variation_work`'.
' INNER JOIN `Artist` ON `artist_id`=`work_artist`'.
' GROUP BY `variation_name`,`work_name`,`artist_name`',[]);
$cartTable = '<tr><th>User</th><th>Open Cart</th></tr>';
foreach ($carts as $c) {
$cartTable .= '<tr><td>'.$c['user_display'].
'</td><td>$'.$c['total'].
'</td><tr>';
}
$variationTable = '<tr><th>Work</th><th>Variation</th><th>Sold</th></tr>';
$lastArtist = '';
foreach ($variations as $v) {
if ($lastArtist != $v['artist_name']) {
$variationTable .= '<tr><th colspan="3">'.$v['artist_name'].'</th></tr>';
$lastArtist=$v['artist_name'];
}
$variationTable .= '<tr><td>'.$v['work_name'].
'</td><td>'.$v['variation_name'].
'</td><td>'.$v['total'].
'</td></tr>';
}
echo <<<__HTML__
<h3>Open Carts</h3>
<table>{$cartTable}<table>
<hr>
<h3>Sales by Work</h3>
<table>{$variationTable}</table>
__HTML__;
}
$msg = '';
if (isset($_POST['user']) && isset($_POST['pass'])) {
/* A login attempt */
$dbPass = getData('SELECT * FROM `User` WHERE `user_name`=?',[$_POST['user']]);
if (password_verify($_POST['pass'],$dbPass['user_hash'])) {
/* Success */
unset($dbPass['user_hash']);
$_SESSION['user'] = $dbPass;
} else {
$_GET['pg'] = 'login';
$msg = 'Username/Password combination was not correct';
}
}
if (!isset($_GET['pg'])) { $_GET['pg'] = ''; }
switch ($_GET['pg']) {
case 'login':
printHeader('Login');
printLogin($msg??'');
break;
case 'logout':
unset($_SESSION['user']);
header('Location: ?pg=home');
die();
break;
case 'css':
header("Content-type: text/css");
printCSS();
die();
break;
case 'admin':
if (!isset($_SESSION['user'])) {
header("Location: ?pg=home");
die();
}
if ($_SESSION['user']['user_type'] != 'admin') {
header("Location: ?pg=home");
die();
}
printHeader('Admin Console');
printCarts();
break;
case 'cart':
/* Display the cart */
if (!isset($_SESSION['user'])) {
header("Location: ?pg=login");
die();
}
printHeader('Shopping Cart');
$openCart = getData('SELECT `cart_id` FROM `Cart` WHERE `cart_user`=? AND `cart_status`=\'open\'',
[$_SESSION['user']['user_id']??-1]);
$cart = new Cart($openCart['cart_id']??-1);
echo $cart->toHTML();
break;
case 'work':
/* Display the information about an artwork */
printHeader('Art Shop');
$work = new Work(($_GET['work'])??-1);
echo $work->toHTML();
break;
case 'search':
$filter = $_GET['search'];
printHeader('Search Results');
case 'home':
default:
if (!isset($filter)) {
printHeader('');
}
/* Display the list of artworks */
echo Work::allToHTML($filter??null);
break;
}
printFooter();
?>
dbObjects.php
<?php
function getData($query,$args=[],$default=[]) {
$DB = 'csci2006_artShop';
$USER = 'csci2006_art';
$PASS = 'hz0fs9vV4vkKL4An';
try {
$pdo = new PDO("mysql:host=localhost;dbname={$DB}",$USER,$PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo '<h2>Database Error</h2>';
die();
}
$rs = $pdo->prepare($query,[PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
$rs->setFetchMode(PDO::FETCH_NAMED);
$rs->execute($args);
$data = [];
while ($row = $rs->fetch()) {
$data[] = $row;
}
unset($DB);
unset($USER);
unset($PASS);
unset($rs);
unset($pdo);
if (count($data) == 0) {
return $default;
}
if (count($data) == 1 && isset($data[0])) {
return $data[0];
}
return $data;
}
class Artist {
private $id = null;
private $info = [];
public function __construct($id) {
$this->id = intval($id);
}
private function loadData() {
if (count($this->info) > 0) {
/* data is already loaded */
return;
}
/* Load the data from the database */
$this->info = getData(
'SELECT * FROM Artist WHERE artist_id=?',
[$this->id],
[
'artist_id'=>-1,
'artist_name'=>'',
'artist_birth'=>-1,
'artist_death'=>-1,
],
);
}
public function getName() {
$this->loadData();
return $this->info['artist_name'];
}
}
class Work {
private $id = null;
private $info = [];
private $desc = [];
private $variations = [];
public function __construct($id) {
$this->id = intval($id);
}
private function loadData() {
if (count($this->info) > 0) {
/* data is already loaded */
return;
}
/* Load the data from the database */
$this->info = getData(
'SELECT * FROM Work WHERE work_id=?',
[$this->id],
[
'work_id'=>-1,
'work_artist'=>null,
'work_name'=>'',
'work_created'=>-1,
]
);
/* Load the description text as well */
$this->desc = getData(
'SELECT * FROM Description WHERE desc_work=? ORDER BY desc_ord ASC',
[$this->id],
[]
);
}
private function getVariations() {
if (count($this->variations) > 0) {
/* data is already loaded */
return;
}
/* Load the data from the database */
$this->variations = getData(
'SELECT * FROM Variation WHERE variation_work=?',
[$this->id],
[]
);
}
public function toHTML() {
$this->loadData();
$artist = new Artist($this->info['work_artist']);
$name = $artist->getName();
$html = <<<__HTML__
<div class="work">
<img src="/resources/csci2006/csci2006_work_{$this->id}.png">
<div class="text">
<span class="name">{$this->info['work_name']}</span>
<span class="artist">{$name}</span>
<div class="desc">
__HTML__;
foreach ($this->desc as $d) {
$html .= '<p>'.$d['desc_text'].'</p>';
}
$html .= <<<__HTML__
</div>
</div>
</div>
__HTML__;
return $html;
}
public static function allToHTML($filter) {
$works = getData(
'SELECT * FROM Work'.
' INNER JOIN Artist on artist_id=work_artist'.
((strlen($filter)>0)?' WHERE work_name LIKE %?%':''),
[$filter],
[]
);
$html = '<div class="grid">';
foreach ($works as $work) {
$html .= <<<__HTML__
<div class="card"><a href="?pg=work&work={$work['work_id']}">
<img src="/resources/csci2006/csci2006_work_{$work['work_id']}.png">
<span class="work">{$work['work_name']}</span>
<span class="artist">{$work['artist_name']}</span>
</a></div>
__HTML__;
}
$html .= '</div>';
return $html;
}
}
class Cart {
private $id = null;
private $info = [];
private $items = [];
public function __construct($id) {
$this->id = intval($id);
}
private function loadData() {
if (count($this->info) > 0) {
/* data is already loaded */
return;
}
/* Load the data from the database */
$this->info = getData(
'SELECT * FROM Cart WHERE cart_id=?',
[$this->id],
[
'cart_id'=>-1,
'cart_user'=>null,
'cart_status'=>'open',
'cart_created'=>'CURRENT_DATE',
]
);
/* Load the cart items as well */
$this->items = getData(
'SELECT * FROM CartItem'.
' INNER JOIN Variation ON variation_id=ci_variation'.
' INNER JOIN Work ON work_id=variation_work'.
' WHERE ci_cart=?',
[$this->id],
[]
);
}
public function toHTML() {
$this->loadData();
$html = <<<__HTML__
<div class="title">
<h1>Shopping Cart</h1>
</div>
<table class="table-fill">
<thead>
<tr>
<th colspan="2">Product</th>
<th>#</th>
<th>Price</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
__HTML__;
$subTotal = 0;
foreach ($this->items as $item) {
$unit=number_format($item['variation_price'],2);
$subTotal += $item['variation_price']*$item['ci_qty'];
$total=number_format($item['variation_price']*$item['ci_qty'],2);
$html .= <<<__HTML__
<tr>
<td><img src="/resources/csci2006/csci2006_work_{$item['work_id']}.png"></td>
<td>{$item['work_name']} - {$item['variation_name']}</td>
<td>{$item['ci_qty']}</td>
<td class="right">\${$unit}</td>
<td class="right">\${$total}</td>
</tr>
__HTML__;
}
$grand = $subTotal;
$st = number_format($subTotal,2);
$grand += $subTotal*0.1;
$tax = number_format($subTotal*0.1,2);
$grand += ($subTotal>500)?0:40;
$ship = number_format(($subTotal>500)?0:40,2);
$total = number_format($grand,2);
$html .= <<<__HTML__
<tr class="totals">
<td colspan="4">Subtotal</td>
<td>\${$st}</td>
</tr>
<tr class="totals">
<td colspan="4">Tax</td>
<td>\${$tax}</td>
</tr>
<tr class="totals">
<td colspan="4">Shipping</td>
<td>\${$ship}</td>
</tr>
<tr class="totals">
<td colspan="4">Grand Total</td>
<td>\${$total}</td>
</tr>
</tbody></table>
__HTML__;
return $html;
}
}
?>
Go To Live Site