Compare commits
65 Commits
4665e1706f
...
switchToCa
Author | SHA1 | Date | |
---|---|---|---|
e262987298 | |||
5f1f5e847d | |||
b6fd7876d3 | |||
1dc31bcb1b | |||
3ff406d053 | |||
6abe519a44 | |||
c6c88456cf | |||
5a805aba07 | |||
1541d05715 | |||
dc78e203ea | |||
17a90358a7 | |||
0266a89ae3 | |||
56a3e584e7 | |||
e2bf38299b | |||
24c8a3d9d7 | |||
dba37e57f9 | |||
c68cf1643e | |||
affe02ec04 | |||
b678720ebd | |||
14f9f247bc | |||
65a87acc48 | |||
e04cf94edd | |||
9ee8ae39df | |||
70aa4d1f06 | |||
2f99531780 | |||
60860a0bce | |||
bb3d0f6e1b | |||
7ee8e8860e | |||
32dcab7592 | |||
88cf11d45d | |||
6c203a0213 | |||
9867df0a04 | |||
0fba7bc0a0 | |||
f46e73c647 | |||
1a8b31b3a2 | |||
a832a3c60b | |||
904fd798f6 | |||
4ed3dcd603 | |||
8bc252ba4d | |||
78f25fdfd3 | |||
e2da6000b3 | |||
32133a0d05 | |||
572cc1eb89 | |||
48585f14ab | |||
47439fe358 | |||
8e0a2ff6e8 | |||
ce798e3b65 | |||
21fd674dd0 | |||
8864875699 | |||
fe0a3c212d | |||
d2f5427df1 | |||
5ed539a471 | |||
5bc0b7966b | |||
d0e1d2e87d | |||
1b794f775d | |||
744117d958 | |||
cce18f6516 | |||
a973a4362f | |||
3bf0c2b44f | |||
ec022d09e4 | |||
148eff1557 | |||
ab4a00d25c | |||
753b832cbc | |||
e5f4656d71 | |||
117903ac7c |
.gitignore.gitinoreREADME.md
Vanilla
LICENSEREADME.mdaddressbook.sqlconfig.jsonconfig.json.sample
public
src
templates
public/assets
src/Controller
templates
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/config.json
|
@ -1,2 +0,0 @@
|
||||
config.json
|
||||
|
@ -1,2 +1,8 @@
|
||||
As I was not allowed to use any framework, respectively no foreign code, most of the time was spent, well creating some kind of framework myself. :-)
|
||||
This repo hold a basic programming task I had to do for my new job.
|
||||
|
||||
The task was to write a simple address book with plain PHP and Javascript.
|
||||
No external resources were allowed, so I had to write my own router DBAL and so on.
|
||||
The result can be found in the Vanilla-Folder.
|
||||
|
||||
Now I got the job and will start working on a CakePHP project, so I decided to rewrite the address book in CakePHP.
|
||||
Work will happen in the CakePHP-Folder.
|
||||
|
3
Vanilla/README.md
Normal file
3
Vanilla/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
As I was not allowed to use any framework, respectively no foreign code, most of the time was spent, well creating some kind of framework myself. :-)
|
||||
|
||||
The address book itself was then done in a few hours.
|
88
Vanilla/addressbook.sql
Normal file
88
Vanilla/addressbook.sql
Normal file
@ -0,0 +1,88 @@
|
||||
-- MariaDB dump 10.19 Distrib 10.5.15-MariaDB, for debian-linux-gnu (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: tracer_addressbook
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.5.15-MariaDB-0+deb11u1-log
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `addresses`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `addresses`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `addresses` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`owner` int(11) NOT NULL,
|
||||
`first` varchar(80) NOT NULL,
|
||||
`last` varchar(80) NOT NULL,
|
||||
`street` varchar(80) NOT NULL,
|
||||
`zip` varchar(10) NOT NULL,
|
||||
`city` varchar(80) NOT NULL,
|
||||
`phone` varchar(30) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `fk_user` (`owner`),
|
||||
CONSTRAINT `fk_user` FOREIGN KEY (`owner`) REFERENCES `users` (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `addresses`
|
||||
--
|
||||
|
||||
LOCK TABLES `addresses` WRITE;
|
||||
/*!40000 ALTER TABLE `addresses` DISABLE KEYS */;
|
||||
INSERT INTO `addresses` VALUES (1,2,'a \"test\"','a','Webfoot Street','1313','Duckburg2','555-12345'),(4,1,'c','b','street4','zip4','city4','phone4'),(6,1,'Huey','Duck','Webfoot Street','1010','Duckburg','555.3456'),(7,1,'Dewey','Duck','Webfoot Street','2020','Duckburg','555-9876'),(8,1,'Louie','Duck','Webfoot Street','3030','Duckburg 3','555-8765'),(11,1,'b','Clapton','sdfg','12456^^^>','https://xd.adobe.com/','23343'),(14,1,'d','aa','','','<script>alert(\'test\')</script>','x'),(16,1,'Adam \"The Badass\"','Black','piouhpouhpouh','132213','piugpiugh','9760978');
|
||||
/*!40000 ALTER TABLE `addresses` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`password` varchar(256) NOT NULL,
|
||||
`nick` varchar(20) NOT NULL,
|
||||
`first` varchar(40) NOT NULL,
|
||||
`last` varchar(40) NOT NULL,
|
||||
`is_admin` tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `nick` (`nick`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `users`
|
||||
--
|
||||
|
||||
LOCK TABLES `users` WRITE;
|
||||
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||
INSERT INTO `users` VALUES (1,'$argon2i$v=19$m=65536,t=4,p=1$OFV0Rnl6OXNWZXZJNTFjTw$fC9K1ykszZ/UaEm21XR9M3+XxnBc+dZ7PRIn5aaGw8I','donald','Donald','Duck',1),(2,'$argon2i$v=19$m=65536,t=4,p=1$NVRKNm0xUmplYkcwTFZXdw$GLp1jjLDBRjKSw6nH8SqqSls6fQPi4Hb7ot0k3naf5s','Daisy','Daisy','Duck',0);
|
||||
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2022-11-01 18:37:04
|
@ -3,5 +3,5 @@
|
||||
"dbPort": 3306,
|
||||
"dbDatabase": "tracer_addressbook",
|
||||
"dbUser": "tracer_addressbook",
|
||||
"dbPassword": "C%oAQU%m$6!3"
|
||||
"dbPassword": "SKTh_6#YM?%q"
|
||||
}
|
206
Vanilla/public/assets/js/functions.js
Normal file
206
Vanilla/public/assets/js/functions.js
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
function addAddress(url) {
|
||||
location.href = url
|
||||
}
|
||||
|
||||
function editAddress(id) {
|
||||
if (document.getElementById('edit_button_' + id).value === 'Save') {
|
||||
// save
|
||||
const url = "/address/update";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
id: id,
|
||||
owner: document.getElementById('owner_' + id).value,
|
||||
first: document.getElementById('first_' + id).value,
|
||||
last: document.getElementById('last_' + id).value,
|
||||
street: document.getElementById('street_' + id).value,
|
||||
zip: document.getElementById('zip_' + id).value,
|
||||
city: document.getElementById('city_' + id).value,
|
||||
phone: document.getElementById('phone_' + id).value,
|
||||
})
|
||||
})
|
||||
.then(
|
||||
response => response.text()
|
||||
).then(
|
||||
json => {
|
||||
let jsonObject = JSON.parse(json)
|
||||
if (jsonObject.status === 200) {
|
||||
setInfo('Data successfully saved.')
|
||||
} else {
|
||||
setError(jsonObject.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
document.getElementById('first_' + id).disabled = true
|
||||
document.getElementById('last_' + id).disabled = true
|
||||
document.getElementById('street_' + id).disabled = true
|
||||
document.getElementById('zip_' + id).disabled = true
|
||||
document.getElementById('city_' + id).disabled = true
|
||||
document.getElementById('phone_' + id).disabled = true
|
||||
|
||||
document.getElementById('edit_button_' + id).value = 'Edit'
|
||||
} else {
|
||||
//switch to edit
|
||||
document.getElementById('first_' + id).disabled = false
|
||||
document.getElementById('last_' + id).disabled = false
|
||||
document.getElementById('street_' + id).disabled = false
|
||||
document.getElementById('zip_' + id).disabled = false
|
||||
document.getElementById('city_' + id).disabled = false
|
||||
document.getElementById('phone_' + id).disabled = false
|
||||
|
||||
document.getElementById('edit_button_' + id).value = 'Save'
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAddress(id) {
|
||||
if (confirm('Are you sure?')) {
|
||||
const url = "/address/delete";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(
|
||||
response => response.text()
|
||||
).then(
|
||||
json => {
|
||||
let jsonObject = JSON.parse(json)
|
||||
if (jsonObject.status === 200) {
|
||||
setInfo('Data successfully saved.')
|
||||
} else {
|
||||
setError(jsonObject.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
let row = document.getElementById('row_' + id)
|
||||
row.parentNode.removeChild(row)
|
||||
}
|
||||
}
|
||||
|
||||
function upCase(text) {
|
||||
return text[0].toUpperCase() + text.substring(1);
|
||||
}
|
||||
|
||||
function sortBy(column) {
|
||||
// clear titles
|
||||
const titles = ['first', 'last', 'street', 'zip', 'city', 'phone']
|
||||
titles.forEach((title) =>
|
||||
document.getElementById(title).innerHTML = upCase(title)
|
||||
)
|
||||
|
||||
console.log("col", column)
|
||||
console.log("curcol", currentColumn)
|
||||
|
||||
if (currentColumn === column) {
|
||||
console.log("in switch")
|
||||
// switch direction on every call on same column
|
||||
if (currentSortOrder === 'asc') {
|
||||
currentSortOrder = 'desc'
|
||||
} else {
|
||||
currentSortOrder = 'asc'
|
||||
}
|
||||
console.log("col", column)
|
||||
} else {
|
||||
currentColumn = column
|
||||
}
|
||||
|
||||
let currentTitleElement = document.getElementById(column)
|
||||
let currentTitle = currentTitleElement.innerHTML
|
||||
let newTitle
|
||||
|
||||
if (currentSortOrder === 'asc') {
|
||||
newTitle = currentTitle[0] + currentTitle.substring(1) + ' ⬇'
|
||||
} else {
|
||||
newTitle = currentTitle[0] + currentTitle.substring(1) + ' ⬆'
|
||||
}
|
||||
currentTitleElement.innerHTML = newTitle
|
||||
|
||||
const table = document.getElementById('address_table');
|
||||
let dirty = true;
|
||||
// loop until clean
|
||||
while (dirty) {
|
||||
// assume we are finished
|
||||
dirty = false
|
||||
const rows = table.rows;
|
||||
for (let i = 1; i < (rows.length - 2); i++) {
|
||||
let x = rows[i]
|
||||
let rowXId = x.id
|
||||
let rowXNumber = rowXId.match(/\d+/)
|
||||
let valueX = document.getElementById(column + '_' + rowXNumber).value
|
||||
|
||||
let y = rows[i + 1]
|
||||
let rowYId = y.id
|
||||
let rowYNumber = rowYId.match(/\d+/)
|
||||
let valueY = document.getElementById(column + '_' + rowYNumber).value
|
||||
|
||||
let sortOrder
|
||||
if (currentSortOrder === 'asc') {
|
||||
sortOrder = 1
|
||||
} else {
|
||||
sortOrder = -1
|
||||
}
|
||||
if (valueX.localeCompare(valueY) === sortOrder) {
|
||||
x.parentNode.insertBefore(y, x);
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setInfo(info) {
|
||||
const infoBox = document.getElementById('info_box')
|
||||
infoBox.innerHTML = info
|
||||
infoBox.style.display = 'block'
|
||||
infoBox.classList.add('panel_float')
|
||||
setTimeout(() => {
|
||||
infoBox.style.display = 'none'
|
||||
}, 2500)
|
||||
}
|
||||
|
||||
function setError(error) {
|
||||
const errorBox = document.getElementById('error_box')
|
||||
const errorText = document.getElementById('error_text')
|
||||
const infoButton = document.getElementById('info_button')
|
||||
if (errorBox.style.display === 'block') {
|
||||
errorBox.style.display = 'none'
|
||||
return
|
||||
}
|
||||
if (infoButton != null) {
|
||||
infoButton.disabled = true
|
||||
}
|
||||
errorText.innerHTML = error
|
||||
errorBox.style.display = 'block'
|
||||
errorBox.classList.add('panel_float')
|
||||
}
|
||||
|
||||
function closeError() {
|
||||
const errorBox = document.getElementById('error_box')
|
||||
const infoButton = document.getElementById('info_button')
|
||||
if (infoButton) {
|
||||
infoButton.disabled = false
|
||||
}
|
||||
errorBox.style.display = 'none'
|
||||
|
||||
}
|
||||
|
||||
// global scope
|
||||
let currentSortOrder = 'desc'
|
||||
let currentColumn = 'last'
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const table = document.getElementById('address_table') || false
|
||||
if (table) {
|
||||
sortBy('last')
|
||||
}
|
||||
})
|
127
Vanilla/public/assets/styles/main.css
Normal file
127
Vanilla/public/assets/styles/main.css
Normal file
@ -0,0 +1,127 @@
|
||||
body {
|
||||
background: #1f1f1f;
|
||||
color: #cdcdcd;
|
||||
}
|
||||
|
||||
/* unvisited link */
|
||||
a:link {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* visited link */
|
||||
a:visited {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* mouse over link */
|
||||
a:hover {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* selected link */
|
||||
a:active {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 1ex;
|
||||
}
|
||||
|
||||
.panel_float {
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
z-index: 2400;
|
||||
opacity: 0.70;
|
||||
margin: auto;
|
||||
top: 110px !important;
|
||||
-webkit-transition: all 0.5s ease-in-out;
|
||||
-moz-transition: all 0.5s ease-in-out;
|
||||
-ms-transition: all 0.5s ease-in-out;
|
||||
-o-transition: all 0.5s ease-in-out;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
#info_box {
|
||||
background-color: #3fc52a;
|
||||
border: solid #cdcdcd;
|
||||
border-radius: 5px;
|
||||
color: #1f1f1f;
|
||||
display: none;
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 50%;
|
||||
margin-left: 200px;
|
||||
margin-right: 200px;
|
||||
}
|
||||
|
||||
.info_button {
|
||||
border-radius: 5px;
|
||||
border: solid #cdcdcd;
|
||||
color: #1f1f1f;
|
||||
padding: 8px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
background-color: #3fc52a;
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#error_box {
|
||||
background-color: #e06844;
|
||||
border: solid #cdcdcd;
|
||||
border-radius: 5px;
|
||||
color: #1f1f1f;
|
||||
display: none;
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 50%;
|
||||
margin-left: 200px;
|
||||
margin-right: 200px;
|
||||
}
|
||||
|
||||
.error_button {
|
||||
border-radius: 5px;
|
||||
border: solid #cdcdcd;
|
||||
color: #1f1f1f;
|
||||
padding: 8px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
background-color: #e06844;
|
||||
transition-duration: 0.4s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close_button {
|
||||
margin-left: 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
font-size: 22px;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.close_button:hover {
|
||||
color: black;
|
||||
}
|
@ -12,6 +12,11 @@ ini_set(option: 'display_startup_errors', value: 1);
|
||||
// no one sane should ignore deprecations
|
||||
error_reporting(error_level: E_ALL);
|
||||
|
||||
// just during dev
|
||||
opcache_reset();
|
||||
|
||||
|
||||
|
||||
session_start();
|
||||
|
||||
require dirname(path: __DIR__) . '/src/bootstrap.php';
|
||||
@ -28,10 +33,6 @@ $security = $container->get(className: SecurityController::class);
|
||||
$addressBook = $container->get(className: AddressBookController::class);
|
||||
$addressBookAdmin = $container->get(className: AddressBookAdminController::class);
|
||||
|
||||
|
||||
// TODO maybe refactor route adding to the controllers?
|
||||
// I currently think that makes sense.
|
||||
|
||||
$router->addRoute(name: 'app_login', route: '/login', callback: function () use ($security) {
|
||||
$security->login();
|
||||
});
|
||||
@ -64,5 +65,16 @@ $router->addRoute(name: 'app_admin_users_delete', route: '/admin/users/delete/{n
|
||||
$addressBookAdmin->adminUserDelete(parameters: $parameters);
|
||||
});
|
||||
|
||||
$router->addRoute(name: 'address_add', route: '/address/add', callback: function () use ($addressBook) {
|
||||
$addressBook->addAddress();
|
||||
});
|
||||
|
||||
$router->addRoute(name: 'address_add', route: '/address/update', callback: function () use ($addressBook) {
|
||||
$addressBook->updateAddress();
|
||||
});
|
||||
|
||||
$router->addRoute(name: 'address_add', route: '/address/delete', callback: function () use ($addressBook) {
|
||||
$addressBook->deleteAddress();
|
||||
});
|
||||
|
||||
$router->handleRouting();
|
@ -13,7 +13,6 @@ use App\Entity\User;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
use App\Repository\UserRepository;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class AddressBookAdminController
|
||||
{
|
||||
@ -39,7 +38,7 @@ class AddressBookAdminController
|
||||
|
||||
public function admin(): never
|
||||
{
|
||||
// TODO $this->adminCheck();
|
||||
$this->adminCheck();
|
||||
$this->template->render(templateName: 'admin/index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
@ -49,7 +48,7 @@ class AddressBookAdminController
|
||||
|
||||
public function adminUser(): never
|
||||
{
|
||||
// TODO $this->adminCheck();
|
||||
$this->adminCheck();
|
||||
|
||||
$users = $this->userRepository->findAll();
|
||||
|
||||
@ -62,7 +61,7 @@ class AddressBookAdminController
|
||||
|
||||
public function adminUserEdit(array $parameters): never
|
||||
{
|
||||
// TODO $this->adminCheck();
|
||||
$this->adminCheck();
|
||||
|
||||
if (!empty($_POST)) {
|
||||
if (!empty($_POST['is_admin'])) {
|
||||
@ -105,11 +104,14 @@ class AddressBookAdminController
|
||||
{
|
||||
$this->adminCheck();
|
||||
|
||||
// TODO currently breaks on inserting a duplicate nick
|
||||
$nick = $_POST['nick'];
|
||||
|
||||
if ($this->userRepository->findByNick(nick: $nick)) {
|
||||
die("User: $nick already exists");
|
||||
}
|
||||
if (!empty($_POST)) {
|
||||
$isAdmin = empty($_POST['is_admin']) ? 0 : 1;
|
||||
echo "isA: $isAdmin";
|
||||
$user = new User(nick: $_POST['nick'], password: $_POST['password'], first: $_POST['first'], last: $_POST['last'], isAdmin: $isAdmin);
|
||||
$user = new User(nick: $_POST['nick'], newPassword: $_POST['new_password'], first: $_POST['first'], last: $_POST['last'], isAdmin: $isAdmin);
|
||||
|
||||
if ($this->userRepository->insert(user: $user)) {
|
||||
$users = $this->userRepository->findAll();
|
||||
@ -149,8 +151,10 @@ class AddressBookAdminController
|
||||
die("Error deleting user");
|
||||
}
|
||||
} else {
|
||||
// TODO use 404
|
||||
die("Nick: $nick not found");
|
||||
$this->template->render(templateName: 'status/404.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
133
Vanilla/src/Controller/AddressBookController.php
Normal file
133
Vanilla/src/Controller/AddressBookController.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\AddressBookEntry;
|
||||
use App\Enums\StatusCode;
|
||||
use App\Enums\UserAuth;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
use App\Repository\AddressRepository;
|
||||
|
||||
class AddressBookController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly User $user,
|
||||
private readonly AddressRepository $addressRepository,
|
||||
private readonly Router $router
|
||||
)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function main(): never
|
||||
{
|
||||
if ($this->user->getAuth() != UserAuth::AUTH_ANONYMOUS) {
|
||||
$addresses = $this->addressRepository->findAll();
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router,
|
||||
'addresses' => $addresses ?? []
|
||||
]);
|
||||
}
|
||||
|
||||
public function addAddress(): never
|
||||
{
|
||||
if (!empty($_POST)) {
|
||||
$address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone']);
|
||||
|
||||
if ($this->addressRepository->insert(address: $address)) {
|
||||
$addresses = $this->addressRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'addresses' => $addresses,
|
||||
'router' => $this->router
|
||||
]);
|
||||
} else {
|
||||
die("Error inserting user");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'addressbook/add_address.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function updateAddress(): void
|
||||
{
|
||||
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
|
||||
|
||||
if (empty($_POST)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone'], id: $_POST['id'])) {
|
||||
if ($this->addressRepository->update(address: $address)) {
|
||||
$status = 200;
|
||||
$message = 'OK';
|
||||
} else {
|
||||
$status = 400;
|
||||
$message = 'BAD_REQUEST';
|
||||
}
|
||||
} else {
|
||||
$status = 400;
|
||||
$message = "BAD REQUEST";
|
||||
}
|
||||
|
||||
$this->template->renderJson(results: [
|
||||
'status' => $status,
|
||||
'message' => $message
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteAddress(): void
|
||||
{
|
||||
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
|
||||
|
||||
if (empty($_POST)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($address = $this->addressRepository->findByID(id: $_POST['id'])) {
|
||||
if ($this->addressRepository->delete(addressBookEntry: $address)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 200,
|
||||
'message' => 'OK'
|
||||
]);
|
||||
} else {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,7 @@ class SecurityController
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($nick && $password) {
|
||||
$nick = strtolower(string: $nick);
|
||||
if ($user = $this->userRepository->findbyNick(nick: $nick)) {
|
||||
if (password_verify(password: $password, hash: $user->getPassword())) {
|
||||
$_SESSION['user_id'] = $user->getId();
|
@ -11,7 +11,6 @@ namespace App\Repository;
|
||||
|
||||
use App\Entity\AddressBookEntry;
|
||||
use App\Service\DatabaseConnection;
|
||||
use App\Entity\User;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
@ -27,7 +26,6 @@ class AddressRepository
|
||||
|
||||
public function findAll(string $orderBy = 'last'): array
|
||||
{
|
||||
$users = [];
|
||||
$sql = "
|
||||
SELECT id, owner, first, last, street, zip, city, phone
|
||||
FROM " . DatabaseConnection::TABLE_ADDRESSES . "
|
||||
@ -40,7 +38,15 @@ class AddressRepository
|
||||
$statement->execute();
|
||||
$addresses = [];
|
||||
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
$address = new AddressBookEntry(owner: $result['owner'], first: $result['first'], last: $result['last'], street: $result['street'], zip: $result['zip'], city: $result['city'], phone: $result['phone'], id: $result['id']);
|
||||
$address = new AddressBookEntry(
|
||||
owner: htmlspecialchars(string: $result['owner']),
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
street: htmlspecialchars(string: $result['street']),
|
||||
zip: htmlspecialchars(string: $result['zip']),
|
||||
city: htmlspecialchars(string: $result['city']),
|
||||
phone: htmlspecialchars(string: $result['phone']),
|
||||
id: htmlspecialchars(string: $result['id']));
|
||||
$addresses[] = $address;
|
||||
}
|
||||
return $addresses;
|
||||
@ -54,7 +60,7 @@ class AddressRepository
|
||||
{
|
||||
$sql = "
|
||||
SELECT id, owner, first, last, street, zip, city, phone
|
||||
FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
FROM " . DatabaseConnection::TABLE_ADDRESSES . "
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
@ -62,7 +68,15 @@ class AddressRepository
|
||||
$statement->bindParam(param: ':id', var: $id);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new AddressBookEntry(owner: $result['owner'], first: $result['first'], last: $result['last'], street: $result['street'], zip: $result['zip'], city: $result['city'], phone: $result['phone'], id: $result['id']);
|
||||
return new AddressBookEntry(
|
||||
owner: htmlspecialchars(string: $result['owner']),
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
street: htmlspecialchars(string: $result['street']),
|
||||
zip: htmlspecialchars(string: $result['zip']),
|
||||
city: htmlspecialchars(string: $result['city']),
|
||||
phone: htmlspecialchars(string: $result['phone']),
|
||||
id: htmlspecialchars(string: $result['id']));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -104,48 +118,40 @@ class AddressRepository
|
||||
}
|
||||
|
||||
|
||||
public function update(Address $address): bool|int
|
||||
public function update(AddressBookEntry $address): bool|int
|
||||
{
|
||||
/*
|
||||
$id = $user->getId();
|
||||
$nick = $user->getNick();
|
||||
$first = $user->getFirst();
|
||||
$last = $user->getLast();
|
||||
$isAdmin = $user->isAdmin() ? 1 : 0;
|
||||
|
||||
if ($user->getPassword()) {
|
||||
$password = $user->getPassword();
|
||||
} else {
|
||||
$current = $this->findByID(id: $id);
|
||||
$password = $current->getPassword();
|
||||
}
|
||||
$id = $address->getId();
|
||||
$first = $address->getFirst();
|
||||
$last = $address->getLast();
|
||||
$street = $address->getStreet();
|
||||
$zip = $address->getZip();
|
||||
$city = $address->getCity();
|
||||
$phone = $address->getPhone();
|
||||
|
||||
$sql = "
|
||||
UPDATE " . DatabaseConnection::TABLE_USERS . " SET
|
||||
nick = :nick,
|
||||
password = :password,
|
||||
UPDATE " . DatabaseConnection::TABLE_ADDRESSES . " SET
|
||||
first = :first,
|
||||
last = :last,
|
||||
is_admin = :is_admin
|
||||
street = :street,
|
||||
zip = :zip,
|
||||
city = :city,
|
||||
phone = :phone
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
$statement->bindParam(param: 'nick', var: $nick);
|
||||
$statement->bindParam(param: 'password', var: $password);
|
||||
$statement->bindParam(param: 'first', var: $first);
|
||||
$statement->bindParam(param: 'last', var: $last);
|
||||
$statement->bindParam(param: 'is_admin', var: $isAdmin);
|
||||
$statement->execute();
|
||||
|
||||
return $statement->rowCount();
|
||||
$statement->bindParam(param: 'street', var: $street);
|
||||
$statement->bindParam(param: 'zip', var: $zip);
|
||||
$statement->bindParam(param: 'city', var: $city);
|
||||
$statement->bindParam(param: 'phone', var: $phone);
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -159,9 +165,7 @@ class AddressRepository
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$id = $addressBookEntry->getId();
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
$statement->execute();
|
||||
|
||||
return $statement->rowCount();
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
@ -38,7 +38,13 @@ class UserRepository
|
||||
|
||||
$statement->execute();
|
||||
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
$user = new User(nick: $result['nick'], password: $result['password'], first: $result['first'], last: $result['last'], id: $result['id'], isAdmin: $result['is_admin']);
|
||||
$user = new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
$users[] = $user;
|
||||
}
|
||||
return $users;
|
||||
@ -60,7 +66,13 @@ class UserRepository
|
||||
$statement->bindParam(param: ':id', var: $id);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new User(nick: $result['nick'], password: $result['password'], first: $result['first'], last: $result['last'], id: $result['id'], isAdmin: $result['is_admin']);
|
||||
return new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -71,6 +83,8 @@ class UserRepository
|
||||
|
||||
public function findByNick(string $nick): ?User
|
||||
{
|
||||
$nick = strtolower(string: $nick);
|
||||
|
||||
$sql = "
|
||||
SELECT id, nick, password, first, last, is_admin
|
||||
FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
@ -81,7 +95,13 @@ class UserRepository
|
||||
$statement->bindParam(param: ':nick', var: $nick);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new User(nick: $result['nick'], password: $result['password'], first: $result['first'], last: $result['last'], id: $result['id'], isAdmin: $result['is_admin']);
|
||||
return new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -117,7 +137,7 @@ class UserRepository
|
||||
}
|
||||
|
||||
|
||||
public function update(User $user): bool|int
|
||||
public function update(User $user): bool
|
||||
{
|
||||
$id = $user->getId();
|
||||
$nick = $user->getNick();
|
||||
@ -149,9 +169,7 @@ class UserRepository
|
||||
$statement->bindParam(param: 'first', var: $first);
|
||||
$statement->bindParam(param: 'last', var: $last);
|
||||
$statement->bindParam(param: 'is_admin', var: $isAdmin);
|
||||
$statement->execute();
|
||||
|
||||
return $statement->rowCount();
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
@ -159,7 +177,7 @@ class UserRepository
|
||||
}
|
||||
|
||||
|
||||
public function delete(User $user): int
|
||||
public function delete(User $user): bool
|
||||
{
|
||||
$sql = "
|
||||
DELETE FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
@ -169,9 +187,7 @@ class UserRepository
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$id = $user->getId();
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
$statement->execute();
|
||||
|
||||
return $statement->rowCount();
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
@ -9,8 +9,6 @@
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
@ -13,6 +13,7 @@ use App\Controller\AddressBookAdminController;
|
||||
use App\Controller\AddressBookController;
|
||||
use App\Controller\SecurityController;
|
||||
use App\Entity\User;
|
||||
use App\Repository\AddressRepository;
|
||||
use App\Repository\UserRepository;
|
||||
|
||||
/*
|
||||
@ -26,6 +27,7 @@ class Container
|
||||
|
||||
private AddressBookController $addressBook;
|
||||
private AddressBookAdminController $addressBookAdmin;
|
||||
private AddressRepository $addressRepository;
|
||||
private Config $config;
|
||||
private DatabaseConnection $databaseConnection;
|
||||
private Router $router;
|
||||
@ -41,13 +43,14 @@ class Container
|
||||
$this->template = new Template(templateDir: dirname(path: __DIR__, levels: 2) . '/templates/');
|
||||
$this->router = new Router(template: $this->template);
|
||||
$this->userRepository = new UserRepository(databaseConnection: $this->databaseConnection);
|
||||
$this->addressRepository = new AddressRepository(databaseConnection: $this->databaseConnection);
|
||||
$this->securityController = new SecurityController(template: $this->template, userRepository: $this->userRepository, router: $this->router);
|
||||
if (empty($_SESSION['user_id'])) {
|
||||
$this->user = new User(); // ANONYMOUS
|
||||
} else {
|
||||
$this->user = $this->userRepository->findByID(id: $_SESSION['user_id']);
|
||||
}
|
||||
$this->addressBook = new AddressBookController(template: $this->template, user: $this->user, userRepository: $this->userRepository, router: $this->router);
|
||||
$this->addressBook = new AddressBookController(template: $this->template, user: $this->user, addressRepository: $this->addressRepository, router: $this->router);
|
||||
$this->addressBookAdmin = new AddressBookAdminController(template: $this->template, user: $this->user, userRepository: $this->userRepository, router: $this->router);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class DatabaseConnection
|
||||
username: $dbUser,
|
||||
password: $dbPassword
|
||||
);
|
||||
$this->dbConnection->setAttribute(attribute: PDO::ATTR_ERRMODE, value: PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
public function getConnection(): PDO
|
@ -35,7 +35,7 @@ class Router
|
||||
|
||||
/*
|
||||
* This method takes a route like /admin/users/{user} and creates a regex to match on call
|
||||
* More complex routes as /posts/{thread}/show/{page} are supported as well.
|
||||
* More complex routes like /posts/{thread}/show/{page} are supported as well.
|
||||
*/
|
||||
function addRoute(string $name, string $route, Closure $callback): void
|
||||
{
|
||||
@ -84,7 +84,7 @@ class Router
|
||||
|
||||
foreach ($this->dynamicRoutes as $route) {
|
||||
// PHPStorm doesn't know that $parameters are always available,
|
||||
// (as it is a dynamic route) so the init the array just to mke PHPstorm happy.
|
||||
// (as these are dynamic routes) so I init the array just to make PHPStorm happy.
|
||||
$parameters = [];
|
||||
if (preg_match(pattern: $route->getRegex(), subject: $requestUri, matches: $matches)) {
|
||||
foreach ($route->getParameters() as $id => $parameter) {
|
||||
@ -104,10 +104,9 @@ class Router
|
||||
public function path(string $routeName, array $vars = [])
|
||||
{
|
||||
foreach (array_merge($this->dynamicRoutes, $this->staticRoutes) as $route) {
|
||||
|
||||
if ($route->getName() == $routeName) {
|
||||
if ($vars) {
|
||||
// build route
|
||||
// build route for dynamic routes
|
||||
$route = $route->getRoute();
|
||||
// replace placeholder with current values
|
||||
foreach ($vars as $key => $value) {
|
||||
@ -119,7 +118,7 @@ class Router
|
||||
}
|
||||
}
|
||||
}
|
||||
// no 404, this is reached only if the code is wrong
|
||||
// no 404, this is reached only if the code is buggy
|
||||
die("Missing Route: $routeName");
|
||||
}
|
||||
}
|
@ -13,8 +13,6 @@ namespace App\Service;
|
||||
* using PHP as a templating engine.
|
||||
*/
|
||||
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class Template
|
||||
{
|
||||
/*
|
||||
@ -28,8 +26,7 @@ class Template
|
||||
/*
|
||||
* Add variables to template and throw it out
|
||||
*/
|
||||
#[NoReturn]
|
||||
public function render(string $templateName, array $vars = []): void
|
||||
public function render(string $templateName, array $vars = []): never
|
||||
{
|
||||
// assign template vars
|
||||
foreach ($vars as $name => $value) {
|
||||
@ -44,4 +41,14 @@ class Template
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For AJAX calls, return json
|
||||
*/
|
||||
public function renderJson(array $results): never
|
||||
{
|
||||
http_response_code(response_code: $results['status']);
|
||||
echo json_encode(value: $results);
|
||||
exit(0);
|
||||
}
|
||||
}
|
7
Vanilla/templates/_footer.html.php
Normal file
7
Vanilla/templates/_footer.html.php
Normal file
@ -0,0 +1,7 @@
|
||||
<script src="/assets/js/functions.js"></script>
|
||||
<?php if (!empty($message)): ?>
|
||||
<script>setError('<?= $message ?>')</script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
27
Vanilla/templates/_header.html.php
Normal file
27
Vanilla/templates/_header.html.php
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Address Book
|
||||
<?php if (!empty($user->getNick())): ?>
|
||||
- <?= $user->getNick() ?>
|
||||
<?php endif; ?>
|
||||
</title>
|
||||
<link rel="stylesheet" href="/assets/styles/main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Address Book</h1>
|
||||
<a href="<?= $router->path('app_main'); ?>">⌂ Home</a>
|
||||
<a href="<?= $router->path('app_admin'); ?>">⚙ Admin</a>
|
||||
<?php if (empty($user) || $user->getAuth() == \App\Enums\UserAuth::AUTH_ANONYMOUS): ?>
|
||||
<a href="<?= $router->path('app_login'); ?>">⎆ Login</a>
|
||||
<?php else: ?>
|
||||
<a href="<?= $router->path('app_logout'); ?>">⎋ Logout</a>
|
||||
<?php endif; ?>
|
||||
<br>
|
||||
<div id="info_box">Info</div>
|
||||
<div id="error_box">
|
||||
<span class="close_button" onclick="closeError()">×</span>
|
||||
<div id="error_text"></div>
|
||||
</div>
|
||||
<br>
|
35
Vanilla/templates/addressbook/add_address.html.php
Normal file
35
Vanilla/templates/addressbook/add_address.html.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php include dirname(path: __DIR__) . '/_header.html.php'; ?>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="owner" id="owner" value="<?= $user->getId() ?>">
|
||||
|
||||
<label for="first">First</label>
|
||||
<input type="text" name="first" id="first" required>
|
||||
<br>
|
||||
|
||||
<label for="last">Last</label>
|
||||
<input type="text" name="last" id="last" required>
|
||||
<br>
|
||||
|
||||
<label for="city">City</label>
|
||||
<input type="text" name="city" id="city">
|
||||
<br>
|
||||
|
||||
<label for="zip">Zip</label>
|
||||
<input type="text" name="zip" id="zip">
|
||||
<br>
|
||||
|
||||
<label for="street">Street</label>
|
||||
<input type="text" name="street" id="street">
|
||||
<br>
|
||||
|
||||
<label for="phone">Phone</label>
|
||||
<input type="text" name="phone" id="phone">
|
||||
<br>
|
||||
|
||||
<!-- maybe later -->
|
||||
<!-- <input type="hidden" name="_csrf" value="csrf_token" -->
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
|
||||
<?php include dirname(path: __DIR__) . '/_footer.html.php' ?>
|
10
Vanilla/templates/admin/index.html.php
Normal file
10
Vanilla/templates/admin/index.html.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php include dirname(path: __DIR__) . '/_header.html.php'; ?>
|
||||
|
||||
<br>
|
||||
<h1>Address Book - Admin</h1>
|
||||
<a href="<?= $router->path('app_admin_users'); ?>">👱 Users</a>
|
||||
|
||||
<?php include dirname(path: __DIR__) . '/_footer.html.php' ?>
|
||||
|
||||
<button type="button" class="info_button" id="info_button" onclick="setInfo('Test Info - auto hide')">Info</button>
|
||||
<button type="button" class="error_button" onclick="setError('Test Error - must be closed manually')">Error</button>
|
@ -2,19 +2,19 @@
|
||||
|
||||
<form method="POST">
|
||||
<label for="nick">Username</label>
|
||||
<input type="text" name="nick" id="nick" required>
|
||||
<input type="text" name="nick" id="nick" maxlength="20" required>
|
||||
<br>
|
||||
|
||||
<label for="new_password">Password</label>
|
||||
<input type="password" name="new_password" id="new_password" required>
|
||||
<input type="password" name="new_password" id="new_password" maxlength="40" required>
|
||||
<br>
|
||||
|
||||
<label for="first">First</label>
|
||||
<input type="text" name="first" id="first" required>
|
||||
<input type="text" name="first" id="first" maxlength="40" required>
|
||||
<br>
|
||||
|
||||
<label for="last">Last</label>
|
||||
<input type="text" name="last" id="last" required>
|
||||
<input type="text" name="last" id="last" maxlength="40" required>
|
||||
<br>
|
||||
|
||||
<label for="is_admin">Is Admin</label>
|
@ -4,19 +4,19 @@
|
||||
<input type="hidden" name="id" value="<?= $editUser->getId() ?>">
|
||||
|
||||
<label for="nick">Username</label>
|
||||
<input type="text" name="nick" id="nick" value="<?= $editUser->getNick() ?>" required>
|
||||
<input type="text" name="nick" id="nick" value="<?= $editUser->getNick() ?>" maxlength="20" required>
|
||||
<br>
|
||||
|
||||
<label for="new_password">Password (leave empty to keep the current one)</label>
|
||||
<input type="password" name="new_password" id="new_password">
|
||||
<input type="password" name="new_password" id="new_password" maxlength="40">
|
||||
<br>
|
||||
|
||||
<label for="first">First</label>
|
||||
<input type="text" name="first" id="first" value="<?= $editUser->getFirst() ?>" required>
|
||||
<input type="text" name="first" id="first" value="<?= $editUser->getFirst() ?>" maxlength="40" required>
|
||||
<br>
|
||||
|
||||
<label for="last">Last</label>
|
||||
<input type="text" name="last" id="last" value="<?= $editUser->getLast() ?>" required>
|
||||
<input type="text" name="last" id="last" value="<?= $editUser->getLast() ?>" maxlength="40" required>
|
||||
<br>
|
||||
|
||||
<label for="is_admin">Is Admin</label>
|
45
Vanilla/templates/index.html.php
Normal file
45
Vanilla/templates/index.html.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php include '_header.html.php' ?>
|
||||
|
||||
<br>
|
||||
<h2>Welcome to Address Book</h2>
|
||||
|
||||
<?php if(!empty($addresses)): ?>
|
||||
<form method="POST">
|
||||
<table id="address_table">
|
||||
<tr>
|
||||
<th id="first" onclick="sortBy('first')">First</th>
|
||||
<th id="last" onclick="sortBy('last')">Last</th>
|
||||
<th id="street" onclick="sortBy('street')">Street</th>
|
||||
<th id="zip" onclick="sortBy('zip')">Zip</th>
|
||||
<th id="city" onclick="sortBy('city')">City</th>
|
||||
<th id="phone" onclick="sortBy('phone')">Phone</th>
|
||||
<th colspan="2"> </th>
|
||||
</tr>
|
||||
<?php foreach ($addresses as $address): ?>
|
||||
<?php $id = $address->getId(); ?>
|
||||
<tr id="row_<?= $id ?>">
|
||||
<td><input type="text" id="first_<?= $id ?>" value="<?= $address->getFirst(); ?>" maxlength="80" disabled></td>
|
||||
<td><input type="text" id="last_<?= $id ?>" value="<?= $address->getLast(); ?>" maxlength="80" disabled></td>
|
||||
<td><input type="text" id="street_<?= $id ?>" value="<?= $address->getStreet(); ?>" maxlength="80" disabled></td>
|
||||
<td><input type="text" id="zip_<?= $id ?>" value="<?= $address->getZip(); ?>" maxlength="10" disabled></td>
|
||||
<td><input type="text" id="city_<?= $id ?>" value="<?= $address->getCity(); ?>" maxlength="80" disabled></td>
|
||||
<td><input type="text" id="phone_<?= $id ?>" value="<?= $address->getPhone(); ?>" maxlength="30" disabled></td>
|
||||
<td>
|
||||
<input type="button" value="Edit" id="edit_button_<?= $id ?>" onclick="editAddress('<?= $id ?>')">
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" value="Delete" onclick="deleteAddress('<?= $id ?>')">
|
||||
<input type="hidden" id="owner_<?= $id ?>" value="<?= $id ?>">
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<?php $addAddress = $router->path('address_add'); ?>
|
||||
<input type="button" value="Add Address" onclick="addAddress('<?= $addAddress ?>')">
|
||||
<?php else: ?>
|
||||
Your addresses will be listed soon …
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include '_footer.html.php' ?>
|
@ -5,12 +5,6 @@
|
||||
<?php else: ?>
|
||||
<br>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="info">
|
||||
<?= $message ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<label for="nick">Username</label>
|
||||
<input type="text" name="nick" id="nick">
|
3
Vanilla/templates/status/404.html.php
Normal file
3
Vanilla/templates/status/404.html.php
Normal file
@ -0,0 +1,3 @@
|
||||
<h2>404 Page not found</h2>
|
||||
|
||||
The requested URL cannot be found on this server.
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
function addAddress(url) {
|
||||
location.href = url
|
||||
}
|
||||
|
||||
function editAddress(id) {
|
||||
console.log("editButon")
|
||||
if (document.getElementById('edit_button_' + id).value === 'Save') {
|
||||
// save
|
||||
console.log("save")
|
||||
const url = "/address/update";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
id: id,
|
||||
owner: document.getElementById('owner_' + id).value,
|
||||
first: document.getElementById('first_' + id).value,
|
||||
last: document.getElementById('last_' + id).value,
|
||||
street: document.getElementById('street_' + id).value,
|
||||
zip: document.getElementById('zip_' + id).value,
|
||||
city: document.getElementById('city_' + id).value,
|
||||
phone: document.getElementById('phone_' + id).value,
|
||||
})
|
||||
})
|
||||
.then(
|
||||
response => response.text() // .json(), etc.
|
||||
// same as function(response) {return response.text();}
|
||||
).then(
|
||||
html => console.log(html)
|
||||
);
|
||||
|
||||
document.getElementById('first_' + id).disabled = true
|
||||
document.getElementById('last_' + id).disabled = true
|
||||
document.getElementById('street_' + id).disabled = true
|
||||
document.getElementById('zip_' + id).disabled = true
|
||||
document.getElementById('city_' + id).disabled = true
|
||||
document.getElementById('phone_' + id).disabled = true
|
||||
|
||||
document.getElementById('edit_button_' + id).value = 'Edit'
|
||||
} else {
|
||||
//switch to edit
|
||||
console.log("switch to edit")
|
||||
document.getElementById('first_' + id).disabled = false
|
||||
document.getElementById('last_' + id).disabled = false
|
||||
document.getElementById('street_' + id).disabled = false
|
||||
document.getElementById('zip_' + id).disabled = false
|
||||
document.getElementById('city_' + id).disabled = false
|
||||
document.getElementById('phone_' + id).disabled = false
|
||||
|
||||
document.getElementById('edit_button_' + id).value = 'Save'
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAddress() {
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
body {
|
||||
background: #1f1f1f;
|
||||
color: #cdcdcd;
|
||||
}
|
||||
|
||||
/* unvisited link */
|
||||
a:link {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* visited link */
|
||||
a:visited {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* mouse over link */
|
||||
a:hover {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* selected link */
|
||||
a:active {
|
||||
color: #ff8844;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 1ex;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
use App\Repository\UserRepository;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class AddressBookController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly User $user,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly Router $router
|
||||
)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
#[NoReturn]
|
||||
public function main(): void
|
||||
{
|
||||
$this->template->render(templateName: 'index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
<!-- mind the javascript -->
|
||||
</body>
|
||||
</html>
|
@ -1,32 +0,0 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Address Book
|
||||
<?php if (!empty($user)): ?>
|
||||
- <?= $user->getNick() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
</title>
|
||||
<link rel="stylesheet" href="/assets/styles/main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Address Book</h1>
|
||||
<a href="<?= $router->path('app_main'); ?>">🏠 Home</a>
|
||||
<a href="<?= $router->path('app_admin'); ?>">⚙ Admin</a>
|
||||
<?php if (empty($user) || $user->getAuth() == \App\Enums\UserAuth::AUTH_ANONYMOUS): ?>
|
||||
<a href="/login">🚪Login</a>
|
||||
<?php else: ?>
|
||||
<a href="/logout">🚪Logout</a>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
if ($user->getAuth() == \App\Enums\UserAuth::AUTH_ANONYMOUS) {
|
||||
echo "no";
|
||||
} else {
|
||||
echo "yes";
|
||||
}
|
||||
?>
|
||||
<?php if (!empty($user) && !$user->getAuth() == \App\Enums\UserAuth::AUTH_ANONYMOUS): ?>
|
||||
<br>
|
||||
Welcome back, <?= $user->getNick(); ?>
|
||||
<?php endif; ?>
|
||||
<br>
|
@ -1,6 +0,0 @@
|
||||
<?php include dirname(path: __DIR__) . '/_header.html.php'; ?>
|
||||
|
||||
<h1>Address Book - Admin</h1>
|
||||
<a href="<?= $router->path('app_admin_users') ?>">👱Users</a>
|
||||
|
||||
<?php include dirname(path: __DIR__) . '/_footer.html.php' ?>
|
@ -1,9 +0,0 @@
|
||||
<?php include '_header.html.php' ?>
|
||||
|
||||
<br>
|
||||
Welcome to Address Book
|
||||
|
||||
Your addresses wil be listed soon …
|
||||
|
||||
|
||||
<?php include '_footer.html.php' ?>
|
@ -1,5 +0,0 @@
|
||||
<?php include dirname(path: __DIR__) . '/_header.html.php'; ?>
|
||||
|
||||
<h2>404 Page not found</h2>
|
||||
|
||||
<?php include dirname(path: __DIR__) . '/_footer.html.php' ?>
|
Reference in New Issue
Block a user