changed repo structure
This commit is contained in:
21
2025/cscg/web/canteenfood/challenge/Database.php
Normal file
21
2025/cscg/web/canteenfood/challenge/Database.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
class Database {
|
||||
|
||||
private static $db;
|
||||
private $connection;
|
||||
|
||||
private function __construct() {
|
||||
$this->connection = new MySQLi('127.0.0.1', $_SERVER['DB_USER'], $_SERVER['DB_PASS'], $_SERVER['DB_NAME']);
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
$this->connection->close();
|
||||
}
|
||||
|
||||
public static function getConnection() {
|
||||
if (self::$db == null) {
|
||||
self::$db = new Database();
|
||||
}
|
||||
return self::$db->connection;
|
||||
}
|
||||
}
|
||||
114
2025/cscg/web/canteenfood/challenge/Routing.php
Normal file
114
2025/cscg/web/canteenfood/challenge/Routing.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// From makelarisjr & makelaris.
|
||||
class Routing
|
||||
{
|
||||
public $rts = [];
|
||||
|
||||
public function new($method, $route, $controller)
|
||||
{
|
||||
$r = [
|
||||
'method' => $method,
|
||||
'route' => $route,
|
||||
];
|
||||
|
||||
if (is_callable($controller))
|
||||
{
|
||||
$r['controller'] = $controller;
|
||||
$this->rts[] = $r;
|
||||
}
|
||||
else if (strpos($controller, '@'))
|
||||
{
|
||||
$split = explode('@', $controller);
|
||||
$class = $split[0];
|
||||
$function = $split[1];
|
||||
|
||||
$r['controller'] = [
|
||||
'class' => $class,
|
||||
'function' => $function
|
||||
];
|
||||
|
||||
$this->rts[] = $r;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('Invalid controller');
|
||||
}
|
||||
}
|
||||
|
||||
public function match()
|
||||
{
|
||||
foreach($this->rts as $route)
|
||||
{
|
||||
if ($this->_match_route($route['route']))
|
||||
{
|
||||
if ($route['method'] != $_SERVER['REQUEST_METHOD'])
|
||||
{
|
||||
$this->abort(405);
|
||||
}
|
||||
$params = $this->getRouteParameters($route['route']);
|
||||
|
||||
if (is_array($route['controller']))
|
||||
{
|
||||
$controller = $route['controller'];
|
||||
$class = $controller['class'];
|
||||
$function = $controller['function'];
|
||||
|
||||
return (new $class)->$function($this,$params);
|
||||
}
|
||||
return $route['controller']($this,$params);
|
||||
}
|
||||
}
|
||||
|
||||
$this->abort(404);
|
||||
}
|
||||
|
||||
public function _match_route($route)
|
||||
{
|
||||
$uri = explode('/', strtok($_SERVER['REQUEST_URI'], '?'));
|
||||
$route = explode('/', $route);
|
||||
|
||||
if (count($uri) != count($route)) return false;
|
||||
|
||||
foreach ($route as $key => $value)
|
||||
{
|
||||
if ($uri[$key] != $value && $value != '{param}') return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRouteParameters($route)
|
||||
{
|
||||
$params = [];
|
||||
$uri = explode('/', strtok($_SERVER['REQUEST_URI'], '?'));
|
||||
$route = explode('/', $route);
|
||||
|
||||
foreach ($route as $key => $value)
|
||||
{
|
||||
if ($uri[$key] == $value) continue;
|
||||
if ($value == '{param}')
|
||||
{
|
||||
if ($uri[$key] == '')
|
||||
{
|
||||
$this->abort(404);
|
||||
}
|
||||
$params[] = $uri[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
public function abort($code)
|
||||
{
|
||||
http_response_code($code);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function view($view, $data = [])
|
||||
{
|
||||
extract($data);
|
||||
include __DIR__."/views/${view}.php";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
BIN
2025/cscg/web/canteenfood/challenge/assets/food.gif
Normal file
BIN
2025/cscg/web/canteenfood/challenge/assets/food.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
class AdminController
|
||||
{
|
||||
public function admin($router)
|
||||
{
|
||||
if($_SESSION["admin"] === false) {
|
||||
return $router->view('admin', ['logs' => "Only access allowed for canteen admin!!!"]);
|
||||
} else {
|
||||
$admin = new AdminModel("/logs.txt","");
|
||||
return $router->view('admin', ['logs' => $admin->read_logs("/logs.txt")]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
class CanteenController
|
||||
{
|
||||
public function index($router)
|
||||
{
|
||||
$food = new CanteenModel(0);
|
||||
if(isset($_GET['price'])) {
|
||||
return $router->view('index', ['food' => $food->filterFood($_GET['price'])]);
|
||||
}
|
||||
return $router->view('index', ['food' => $food->getFood()]);
|
||||
}
|
||||
}
|
||||
20
2025/cscg/web/canteenfood/challenge/index.php
Normal file
20
2025/cscg/web/canteenfood/challenge/index.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
|
||||
$_SESSION["admin"] = false;
|
||||
|
||||
include_once "controllers/AdminController.php";
|
||||
include_once "controllers/CanteenController.php";
|
||||
include_once "models/AdminModel.php";
|
||||
include_once "models/CanteenModel.php";
|
||||
include_once "Routing.php";
|
||||
include_once "Database.php";
|
||||
|
||||
$router = new Routing();
|
||||
$router->new('GET', '/', 'CanteenController@index');
|
||||
$router->new('GET', '/admin', 'AdminController@admin');
|
||||
|
||||
$response = $router->match();
|
||||
|
||||
die($response);
|
||||
32
2025/cscg/web/canteenfood/challenge/models/AdminModel.php
Normal file
32
2025/cscg/web/canteenfood/challenge/models/AdminModel.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
if($_SESSION["admin"] === false){
|
||||
return "You're not welcome. This part is only for canteen workers.";
|
||||
}
|
||||
|
||||
|
||||
class AdminModel {
|
||||
public $filename;
|
||||
public $logcontent;
|
||||
|
||||
public function __construct($filename, $content) {
|
||||
$this->filename = $filename;
|
||||
$this->logcontent = $content;
|
||||
file_put_contents($filename, $content, FILE_APPEND);
|
||||
}
|
||||
|
||||
public function __wakeup() {
|
||||
new LogFile($this->filename, $this->logcontent);
|
||||
}
|
||||
|
||||
public static function read_logs($log) {
|
||||
$contents = file_get_contents($log);
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
|
||||
class LogFile {
|
||||
public function __construct($filename, $content) {
|
||||
file_put_contents($filename, $content, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
71
2025/cscg/web/canteenfood/challenge/models/CanteenModel.php
Normal file
71
2025/cscg/web/canteenfood/challenge/models/CanteenModel.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
class CanteenModel {
|
||||
|
||||
|
||||
public function getFood() {
|
||||
$log_entry = 'Access at: '. date('Y-m-d H:i:s') . "<br>\n";
|
||||
$logger = new AdminModel("/logs.txt", $log_entry);
|
||||
|
||||
$db = Database::getConnection();
|
||||
$sql = "SELECT * FROM food";
|
||||
if ($result = $db->query($sql)) {
|
||||
$result_string = "";
|
||||
while($obj = $result->fetch_object()){
|
||||
if($obj->name !== '') {
|
||||
$result_string .= $obj->name . ' for ' . $obj->price . '€ <br>';
|
||||
$db->query("UPDATE food SET price = " . (rand(0, 100000) / 100) . " WHERE name = \"" . $obj->name. "\"");
|
||||
}
|
||||
|
||||
if($obj->oldvalue !== '') {
|
||||
$dec_result = base64_decode($obj->oldvalue);
|
||||
if (preg_match_all('/O:\d+:"([^"]*)"/', $dec_result, $matches)) {
|
||||
return 'Not allowed';
|
||||
}
|
||||
$uns_result = unserialize($dec_result);
|
||||
$result_string .= $uns_result[0] . ' for ' . $uns_result[1] . '€<br>';
|
||||
$new_value = [$uns_result[0], (rand(0, 100000) / 100)];
|
||||
$db->query("UPDATE food SET oldvalue = \"" . base64_encode(serialize($new_value)) . "\" WHERE oldvalue = \"" . $obj->oldvalue . "\"");
|
||||
}
|
||||
}
|
||||
return $result_string;
|
||||
}
|
||||
return 'No food this week - we\'re closed';
|
||||
}
|
||||
|
||||
public function filterFood($price_param) {
|
||||
$log_entry = 'Access at: '. date('Y-m-d H:i:s') . "<br>\n";
|
||||
$logger = new AdminModel("../logs.txt", $log_entry);
|
||||
|
||||
|
||||
$db = Database::getConnection();
|
||||
$sql = "SELECT * FROM food where price < " . $price_param;
|
||||
error_log(print_r($sql, true));
|
||||
if ($result = $db->query($sql)) {
|
||||
error_log(print_r($result, true));
|
||||
$result_string = "";
|
||||
while($obj = $result->fetch_object()){
|
||||
if($obj->name !== '') {
|
||||
$result_string .= $obj->name . ' for ' . $obj->price . '€ <br>';
|
||||
}
|
||||
|
||||
if($obj->oldvalue !== '') {
|
||||
$dec_result = base64_decode($obj->oldvalue);
|
||||
if (preg_match_all('/O:\d+:"([^"]*)"/', $dec_result, $matches)) {
|
||||
return 'Not allowed';
|
||||
}
|
||||
$uns_result = unserialize($dec_result);
|
||||
if ($uns_result[1] < $price_param) {
|
||||
$result_string .= $uns_result[0] . ' for ' . $uns_result[1] . '€<br>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($result_string === "") {
|
||||
return 'Everything is too expensive for you this week, Sir/Madame. We\'re sorry!';
|
||||
}
|
||||
return $result_string;
|
||||
}
|
||||
|
||||
return 'Everything is too expensive for you this week, Sir/Madame. We\'re sorry!';
|
||||
}
|
||||
}
|
||||
67
2025/cscg/web/canteenfood/challenge/static/css/main.css
Normal file
67
2025/cscg/web/canteenfood/challenge/static/css/main.css
Normal file
@@ -0,0 +1,67 @@
|
||||
body {
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
min-width: 260px;
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
background-color: #006400;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
width: 93%;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-top: 55px !important;
|
||||
}
|
||||
|
||||
#main {
|
||||
margin: 30px auto;
|
||||
padding: 15px;
|
||||
border: 0px solid;
|
||||
border-radius: 5px;
|
||||
background: #556b2f;
|
||||
}
|
||||
|
||||
#title {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
#title i {
|
||||
color: #D34156;
|
||||
font-size: 50px;
|
||||
margin: 0px 40px;
|
||||
}
|
||||
#time {
|
||||
color: #ff0a94;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 550px;
|
||||
margin-top: 1.4% !important;
|
||||
height: 560px;
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
height: auto;
|
||||
margin: auto;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img:hover {
|
||||
animation: dance 1.6s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.pulse{
|
||||
animation: pound 0.84s infinite alternate;
|
||||
-webkit-animation: pound 0.84s infinite alternate;
|
||||
}
|
||||
16
2025/cscg/web/canteenfood/challenge/views/admin.php
Normal file
16
2025/cscg/web/canteenfood/challenge/views/admin.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name='author' content='poory'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
|
||||
<title>Canteen Plan</title>
|
||||
<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css' integrity='sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm' crossorigin='anonymous'>
|
||||
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css' integrity='sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==' crossorigin='anonymous' />
|
||||
<link rel='stylesheet' href='/static/css/main.css' />
|
||||
<link rel='preconnect' href='//fonts.gstatic.com'>
|
||||
<link link='preload' href='//fonts.googleapis.com/css2?family=Press+Start+2P&display=swap' rel='stylesheet'>
|
||||
<link rel='icon' href='/assets/favicon.png' />
|
||||
</head>
|
||||
<body>
|
||||
<span id='logs'> <?= $logs ?></span>
|
||||
</body>
|
||||
</html>
|
||||
36
2025/cscg/web/canteenfood/challenge/views/index.php
Normal file
36
2025/cscg/web/canteenfood/challenge/views/index.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name='author' content='poory'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
|
||||
<title>Canteen Plan</title>
|
||||
<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css' integrity='sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm' crossorigin='anonymous'>
|
||||
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css' integrity='sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==' crossorigin='anonymous' />
|
||||
<link rel='stylesheet' href='/static/css/main.css' />
|
||||
<link rel='preconnect' href='//fonts.gstatic.com'>
|
||||
<link link='preload' href='//fonts.googleapis.com/css2?family=Press+Start+2P&display=swap' rel='stylesheet'>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a class='fas fa-heart pulse' href="/admin">admin</a>
|
||||
<div id='main' class='container'>
|
||||
<h1 id='title'>
|
||||
<i class='fas fa-heart pulse'></i> <b>Canteen Plan</b> <i class='fas fa-heart pulse'></i>
|
||||
</h1>
|
||||
<br>
|
||||
<div id='img-div'>
|
||||
<img id='image' src='/assets/food.gif' alt='yuuuuuuummy'> <!-- Not modified and taken from https://commons.wikimedia.org/wiki/File:Foods_-_Idil_Keysan_-_Wikimedia_Giphy_stickers_2019.gif . Thanks -->
|
||||
<br>
|
||||
</div>
|
||||
<div id='searchfield'>
|
||||
<form action="/" method="get">
|
||||
<input type="search" id="site-search" placeholder="300" name="price" />
|
||||
<button>Cheap Price Search</button>
|
||||
</form>
|
||||
</div>
|
||||
<br>
|
||||
<h2>Today we will satisfy you with:</h2>
|
||||
<br>
|
||||
<span id='food'> <?= $food ?></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user