Compare commits
	
		
			75 Commits
		
	
	
		
			e1d1ef5eeb
			...
			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 | |||
| 4665e1706f | |||
| 8189abfd29 | |||
| 72503d7fe2 | |||
| 3f4653b81f | |||
| 0ed667d594 | |||
| fddc58fb14 | |||
| ad63a40a0d | |||
| e13bbee1e6 | |||
| c6af0d1b1c | |||
| fd368d895d | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
/config.json
 | 
			
		||||
@@ -1,3 +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 address book itself was than done in a few hours.
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										7
									
								
								Vanilla/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Vanilla/config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "dbHost": "localhost",
 | 
			
		||||
  "dbPort": 3306,
 | 
			
		||||
  "dbDatabase": "tracer_addressbook",
 | 
			
		||||
  "dbUser": "tracer_addressbook",
 | 
			
		||||
  "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();
 | 
			
		||||
							
								
								
									
										161
									
								
								Vanilla/src/Controller/AddressBookAdminController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								Vanilla/src/Controller/AddressBookAdminController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
			
		||||
<?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;
 | 
			
		||||
 | 
			
		||||
class AddressBookAdminController
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly Template       $template,
 | 
			
		||||
        private readonly User           $user,
 | 
			
		||||
        private readonly UserRepository $userRepository,
 | 
			
		||||
        private readonly Router         $router
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function adminCheck(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->user->isAdmin()) {
 | 
			
		||||
            $this->template->render(templateName: 'status/403.html.php', vars: [
 | 
			
		||||
                'user'   => $this->user,
 | 
			
		||||
                'router' => $this->router
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function admin(): never
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminCheck();
 | 
			
		||||
        $this->template->render(templateName: 'admin/index.html.php', vars: [
 | 
			
		||||
            'user'   => $this->user,
 | 
			
		||||
            'router' => $this->router
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function adminUser(): never
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminCheck();
 | 
			
		||||
 | 
			
		||||
        $users = $this->userRepository->findAll();
 | 
			
		||||
 | 
			
		||||
        $this->template->render(templateName: 'admin/users.html.php', vars: [
 | 
			
		||||
            'user'   => $this->user,
 | 
			
		||||
            'users'  => $users,
 | 
			
		||||
            'router' => $this->router
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function adminUserEdit(array $parameters): never
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminCheck();
 | 
			
		||||
 | 
			
		||||
        if (!empty($_POST)) {
 | 
			
		||||
            if (!empty($_POST['is_admin'])) {
 | 
			
		||||
                $isAdmin = 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                $isAdmin = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (empty($_POST['new_password'])) {
 | 
			
		||||
                $current = $this->userRepository->findByID(id: $_POST['id']);
 | 
			
		||||
                $password = $current->getPassword();
 | 
			
		||||
                $updateUser = new User(nick: $_POST['nick'], password: $password, first: $_POST['first'], last: $_POST['last'], id: $_POST['id'], isAdmin: $isAdmin);
 | 
			
		||||
            } else {
 | 
			
		||||
                $password = $_POST['new_password'];
 | 
			
		||||
                $updateUser = new User(nick: $_POST['nick'], newPassword: $password, first: $_POST['first'], last: $_POST['last'], id: $_POST['id'], isAdmin: $isAdmin);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->userRepository->update(user: $updateUser);
 | 
			
		||||
 | 
			
		||||
            $users = $this->userRepository->findAll();
 | 
			
		||||
 | 
			
		||||
            $this->template->render(templateName: 'admin/users.html.php', vars: [
 | 
			
		||||
                'user'   => $this->user,
 | 
			
		||||
                'users'  => $users,
 | 
			
		||||
                'router' => $this->router
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $editUser = $this->userRepository->findByNick(nick: $parameters['nick']);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $this->template->render(templateName: 'admin/users_edit.html.php', vars: [
 | 
			
		||||
            'user'     => $this->user,
 | 
			
		||||
            'editUser' => $editUser,
 | 
			
		||||
            'router'   => $this->router
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function adminUserAdd(): never
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminCheck();
 | 
			
		||||
 | 
			
		||||
        $nick = $_POST['nick'];
 | 
			
		||||
 | 
			
		||||
        if ($this->userRepository->findByNick(nick: $nick)) {
 | 
			
		||||
            die("User: $nick already exists");
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($_POST)) {
 | 
			
		||||
            $isAdmin = empty($_POST['is_admin']) ? 0 : 1;
 | 
			
		||||
            $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();
 | 
			
		||||
 | 
			
		||||
                $this->template->render(templateName: 'admin/users.html.php', vars: [
 | 
			
		||||
                    'user'   => $this->user,
 | 
			
		||||
                    'users'  => $users,
 | 
			
		||||
                    'router' => $this->router
 | 
			
		||||
                ]);
 | 
			
		||||
            } else {
 | 
			
		||||
                die("Error inserting user");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->template->render(templateName: 'admin/users_add.html.php', vars: [
 | 
			
		||||
            'user'   => $this->user,
 | 
			
		||||
            'router' => $this->router
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function adminUserDelete(array $parameters): never
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminCheck();
 | 
			
		||||
 | 
			
		||||
        $nick = $parameters['nick'];
 | 
			
		||||
        if ($user = $this->userRepository->findByNick(nick: $nick)) {
 | 
			
		||||
            if ($this->userRepository->delete(user: $user)) {
 | 
			
		||||
                $users = $this->userRepository->findAll();
 | 
			
		||||
 | 
			
		||||
                $this->template->render(templateName: 'admin/users.html.php', vars: [
 | 
			
		||||
                    'user'   => $this->user,
 | 
			
		||||
                    'users'  => $users,
 | 
			
		||||
                    'router' => $this->router
 | 
			
		||||
                ]);
 | 
			
		||||
            } else {
 | 
			
		||||
                die("Error deleting user");
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $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();
 | 
			
		||||
							
								
								
									
										107
									
								
								Vanilla/src/Entity/AddressBookEntry.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								Vanilla/src/Entity/AddressBookEntry.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
<?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\Entity;
 | 
			
		||||
 | 
			
		||||
class AddressBookEntry
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private int    $owner,
 | 
			
		||||
        private string $first,
 | 
			
		||||
        private string $last,
 | 
			
		||||
        private string $street,
 | 
			
		||||
        private string $zip,
 | 
			
		||||
        private string $city,
 | 
			
		||||
        private string $phone,
 | 
			
		||||
        private int    $id = 0,
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        // empty body
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getOwner(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->owner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setOwner(int $owner): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->owner = $owner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getStreet(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->street;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setStreet(string $street): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->street = $street;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getZip(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->zip;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setZip(string $zip): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->zip = $zip;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCity(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->city;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setCity(string $city): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->city = $city;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getPhone(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->phone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setPhone(string $phone): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->phone = $phone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getId(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setId(int $id): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFirst(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setFirst(string $first): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->first = $first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getLast(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->last;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setLast(string $last): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->last = $last;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										174
									
								
								Vanilla/src/Repository/AddressRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								Vanilla/src/Repository/AddressRepository.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
<?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\Repository;
 | 
			
		||||
 | 
			
		||||
use App\Entity\AddressBookEntry;
 | 
			
		||||
use App\Service\DatabaseConnection;
 | 
			
		||||
use PDO;
 | 
			
		||||
use PDOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handles CRUD of Addresses class.
 | 
			
		||||
 */
 | 
			
		||||
class AddressRepository
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(private readonly DatabaseConnection $databaseConnection)
 | 
			
		||||
    {
 | 
			
		||||
        // empty body
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function findAll(string $orderBy = 'last'): array
 | 
			
		||||
    {
 | 
			
		||||
        $sql = "
 | 
			
		||||
            SELECT id, owner, first, last, street, zip, city, phone 
 | 
			
		||||
            FROM " . DatabaseConnection::TABLE_ADDRESSES . "
 | 
			
		||||
            ORDER BY :order";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
 | 
			
		||||
            $statement->bindParam(param: ':order', var: $orderBy);
 | 
			
		||||
 | 
			
		||||
            $statement->execute();
 | 
			
		||||
            $addresses = [];
 | 
			
		||||
            while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
 | 
			
		||||
                $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;
 | 
			
		||||
        } catch (PDOException $e) {
 | 
			
		||||
            exit($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function findByID(int $id): ?AddressBookEntry
 | 
			
		||||
    {
 | 
			
		||||
        $sql = "
 | 
			
		||||
            SELECT id, owner, first, last, street, zip, city, phone 
 | 
			
		||||
            FROM " . DatabaseConnection::TABLE_ADDRESSES . "
 | 
			
		||||
            WHERE id = :id";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
 | 
			
		||||
            $statement->bindParam(param: ':id', var: $id);
 | 
			
		||||
            $statement->execute();
 | 
			
		||||
            if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
 | 
			
		||||
                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;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (PDOException $e) {
 | 
			
		||||
            exit($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function insert(AddressBookEntry $address): bool|string
 | 
			
		||||
    {
 | 
			
		||||
        $sql = "
 | 
			
		||||
            INSERT INTO " . DatabaseConnection::TABLE_ADDRESSES . " (owner, first, last, city, zip, street, phone)
 | 
			
		||||
            VALUES (:owner, :first, :last, :city, :zip, :street, :phone)";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $owner = $address->getOwner();
 | 
			
		||||
            $first = $address->getFirst();
 | 
			
		||||
            $last = $address->getLast();
 | 
			
		||||
            $city = $address->getCity();
 | 
			
		||||
            $zip = $address->getZip();
 | 
			
		||||
            $street = $address->getStreet();
 | 
			
		||||
            $phone = $address->getPhone();
 | 
			
		||||
 | 
			
		||||
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
 | 
			
		||||
            $statement->bindParam(param: ':owner', var: $owner);
 | 
			
		||||
            $statement->bindParam(param: ':first', var: $first);
 | 
			
		||||
            $statement->bindParam(param: ':last', var: $last);
 | 
			
		||||
            $statement->bindParam(param: ':city', var: $city);
 | 
			
		||||
            $statement->bindParam(param: ':zip', var: $zip);
 | 
			
		||||
            $statement->bindParam(param: ':street', var: $street);
 | 
			
		||||
            $statement->bindParam(param: ':phone', var: $phone);
 | 
			
		||||
            $statement->execute();
 | 
			
		||||
 | 
			
		||||
            return $this->databaseConnection->getConnection()->lastInsertId();
 | 
			
		||||
        } catch (PDOException $e) {
 | 
			
		||||
            exit($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function update(AddressBookEntry $address): bool|int
 | 
			
		||||
    {
 | 
			
		||||
        $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_ADDRESSES . " SET
 | 
			
		||||
				first = :first,
 | 
			
		||||
				last = :last,
 | 
			
		||||
				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: 'first', var: $first);
 | 
			
		||||
            $statement->bindParam(param: 'last', var: $last);
 | 
			
		||||
            $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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function delete(AddressBookEntry $addressBookEntry): int
 | 
			
		||||
    {
 | 
			
		||||
        $sql = "
 | 
			
		||||
            DELETE FROM " . DatabaseConnection::TABLE_ADDRESSES . "
 | 
			
		||||
            WHERE id = :id";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
 | 
			
		||||
            $id = $addressBookEntry->getId();
 | 
			
		||||
            $statement->bindParam(param: 'id', var: $id);
 | 
			
		||||
            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
 | 
			
		||||
    {
 | 
			
		||||
@@ -45,6 +45,7 @@ class Router
 | 
			
		||||
 | 
			
		||||
        // create regex for route:
 | 
			
		||||
        $regex = preg_replace(pattern: '/{.+?}/', replacement: '([a-zA-Z0-9]*)', subject: $route);
 | 
			
		||||
        // escape \ in regex
 | 
			
		||||
        $regex = '/^' . str_replace(search: "/", replace: '\\/', subject: $regex) . '$/i';
 | 
			
		||||
 | 
			
		||||
        $route = new Route(name: $name, route: $route, regEx: $regex, parameters: $parameters, callback: $callback);
 | 
			
		||||
@@ -64,7 +65,7 @@ class Router
 | 
			
		||||
        $requestUri = $_SERVER['REQUEST_URI'];
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Static routes have preference over dynamic ones, so
 | 
			
		||||
         * Static routes have precedence over dynamic ones, so
 | 
			
		||||
         * /admin/user/add to add and
 | 
			
		||||
         * /admin/user/{name} to edit is possible.
 | 
			
		||||
         * A user named "add" of course not :)
 | 
			
		||||
@@ -82,6 +83,8 @@ class Router
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->dynamicRoutes as $route) {
 | 
			
		||||
            // PHPStorm doesn't know that $parameters are always available,
 | 
			
		||||
            // (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) {
 | 
			
		||||
@@ -101,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) {
 | 
			
		||||
@@ -116,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,29 +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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Controller;
 | 
			
		||||
 | 
			
		||||
use App\Service\Template;
 | 
			
		||||
 | 
			
		||||
class AddressBook
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(private readonly Template $template)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function main(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $this->template->render(templateName: 'index.tpl');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            die($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    public function admin(string $command = '')
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $this->template->render(templateName: 'admin/index.tpl');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            die($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function login()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $this->template->render(templateName: 'admin/index.tpl');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            die($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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,63 +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\Entity;
 | 
			
		||||
 | 
			
		||||
class AddressBookEntry
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private int $userid,
 | 
			
		||||
        private string $first,
 | 
			
		||||
        private string $last,
 | 
			
		||||
        private string $nick,
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        // empty body
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUserid(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->userid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUserid(int $userid): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->userid = $userid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFirst(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setFirst(string $first): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->first = $first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getLast(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->last;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setLast(string $last): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->last = $last;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getNick(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->nick;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setNick(string $nick): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->nick = $nick;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,5 +0,0 @@
 | 
			
		||||
<html>
 | 
			
		||||
    <head>
 | 
			
		||||
        <title>Address Book</title>
 | 
			
		||||
        <link rel="stylesheet" href="/assets/styles/main.css">
 | 
			
		||||
    </head>
 | 
			
		||||
@@ -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,6 +0,0 @@
 | 
			
		||||
<?php include '_header.html.php' ?>
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
Welcome to Address Book
 | 
			
		||||
 | 
			
		||||
<?php include '_footer.html.php' ?>
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
{% include '_header.tpl' %}
 | 
			
		||||
 | 
			
		||||
<h1>Address Book</h1>
 | 
			
		||||
<a href="/">🏠 Home</a>
 | 
			
		||||
<a href="/">⚙ Admin</a>
 | 
			
		||||
<a href="/">🚪 Login</a>
 | 
			
		||||
 | 
			
		||||
{% include '_footer.tpl' %}
 | 
			
		||||
@@ -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