first commit
This commit is contained in:
9
.htaccess
Normal file
9
.htaccess
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
RewriteBase /
|
||||||
|
|
||||||
|
# Nie przepisuj istniejących plików i katalogów
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
|
||||||
|
# Przepisz wszystko na index.php
|
||||||
|
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||||
17
.vscode/ftp-kr.json
vendored
Normal file
17
.vscode/ftp-kr.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"host": "host117523.hostido.net.pl",
|
||||||
|
"username": "www@pagedev.pl",
|
||||||
|
"password": "dP9frFL3nw469pXJJ5vG",
|
||||||
|
"remotePath": "/public_html/",
|
||||||
|
"protocol": "ftp",
|
||||||
|
"port": 21,
|
||||||
|
"fileNameEncoding": "utf8",
|
||||||
|
"autoUpload": true,
|
||||||
|
"autoDelete": false,
|
||||||
|
"autoDownload": false,
|
||||||
|
"ignoreRemoteModification": true,
|
||||||
|
"ignore": [
|
||||||
|
".git",
|
||||||
|
"/.vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
.vscode/ftp-kr.sync.cache.json
vendored
Normal file
16
.vscode/ftp-kr.sync.cache.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"ftp://host117523.hostido.net.pl:21@www@pagedev.pl": {
|
||||||
|
"public_html": {
|
||||||
|
"cgi-bin": {},
|
||||||
|
"models": {
|
||||||
|
"Database.php": {
|
||||||
|
"type": "-",
|
||||||
|
"size": 1094,
|
||||||
|
"lmtime": 1769712478164,
|
||||||
|
"modified": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$version": 1
|
||||||
|
}
|
||||||
12
.vscode/sftp.json
vendored
Normal file
12
.vscode/sftp.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "host117523.hostido.net.pl",
|
||||||
|
"host": "host117523.hostido.net.pl",
|
||||||
|
"protocol": "ftp",
|
||||||
|
"port": 21,
|
||||||
|
"username": "www@pagedev.pl",
|
||||||
|
"password": "dP9frFL3nw469pXJJ5vG",
|
||||||
|
"remotePath": "/public_html/",
|
||||||
|
"uploadOnSave": false,
|
||||||
|
"useTempFile": false,
|
||||||
|
"openSsh": false
|
||||||
|
}
|
||||||
66
PROJECT_LOG.md
Normal file
66
PROJECT_LOG.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Projekt: System 2FA + Notatnik (PHP MVC)
|
||||||
|
|
||||||
|
## Cel projektu
|
||||||
|
- Prosta aplikacja logowania 2FA w PHP z notatnikiem użytkownika.
|
||||||
|
- MVC (kontrolery, modele, widoki). Bootstrap UI.
|
||||||
|
|
||||||
|
## Najważniejsze funkcje (stan na dziś)
|
||||||
|
- Logowanie użytkownika + weryfikacja kodem 2FA (testowy kod w konsoli).
|
||||||
|
- Panel użytkownika po zalogowaniu.
|
||||||
|
- Notatnik: lista, dodawanie, edycja, usuwanie z ładnym modalem potwierdzenia.
|
||||||
|
- Kalendarz: widok miesięczny, dodawanie/edycja/usuwanie wydarzeń.
|
||||||
|
|
||||||
|
## Dane testowe
|
||||||
|
- Login: projectpro
|
||||||
|
- Hasło: testowehaslo
|
||||||
|
|
||||||
|
## Routing (przyjazne URL)
|
||||||
|
- /logowanie
|
||||||
|
- /uwierzytelnianie (POST)
|
||||||
|
- /weryfikacja
|
||||||
|
- /weryfikuj-kod (POST)
|
||||||
|
- /panel lub /pulpit
|
||||||
|
- /notatnik
|
||||||
|
- /notatka/nowa
|
||||||
|
- /notatka/edytuj?id=ID
|
||||||
|
- /notatka/zapisz (POST)
|
||||||
|
- /notatka/usun (POST)
|
||||||
|
- /kalendarz
|
||||||
|
- /wydarzenie/nowe
|
||||||
|
- /wydarzenie/edytuj?id=ID
|
||||||
|
- /wydarzenie/zapisz (POST)
|
||||||
|
- /wydarzenie/usun (POST)
|
||||||
|
- /wyloguj-sie
|
||||||
|
- /inicjalizacja
|
||||||
|
|
||||||
|
## Baza danych (SQLite)
|
||||||
|
- users: użytkownicy (hasła haszowane)
|
||||||
|
- verification_codes: kody 2FA z wygasaniem
|
||||||
|
- notes: notatki per user (created_at/updated_at)
|
||||||
|
- calendar_events: wydarzenia per user (event_date, created_at/updated_at)
|
||||||
|
|
||||||
|
## Struktura MVC
|
||||||
|
- controllers: InitController, LoginController, DashboardController, NotesController
|
||||||
|
- controllers: InitController, LoginController, DashboardController, NotesController, CalendarController
|
||||||
|
- models: Database, User, Notes, CalendarEvent
|
||||||
|
- views: login, verify, dashboard, notes/index, notes/form, calendar/index, calendar/form
|
||||||
|
- layout wspólny: views/layout.php
|
||||||
|
|
||||||
|
## Wygląd UI
|
||||||
|
- Bootstrap 5
|
||||||
|
- Dodatkowe style: public/css/style.css, public/css/notes.css, public/css/calendar.css
|
||||||
|
- JS: public/js/app.js
|
||||||
|
|
||||||
|
## Ważne uwagi techniczne
|
||||||
|
- Layout obsługuje: $pageTitle, $bodyClass, $extraHead, $extraScript, $content.
|
||||||
|
- Widoki korzystają z layoutu i generują zawartość przez output buffering.
|
||||||
|
- Notatnik używa modala Bootstrap do potwierdzenia usunięcia.
|
||||||
|
|
||||||
|
## Pliki kluczowe
|
||||||
|
- index.php: router i bootstrap aplikacji
|
||||||
|
- .htaccess: przepisywanie URL
|
||||||
|
- models/Notes.php: inicjalizacja tabeli notatek przy użyciu
|
||||||
|
|
||||||
|
## Do dalszego pilnowania
|
||||||
|
- Utrzymywać spójność URL z routerem.
|
||||||
|
- Aktualizować ten plik po kolejnych zmianach.
|
||||||
77
README.md
Normal file
77
README.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# System Logowania 2FA
|
||||||
|
|
||||||
|
Prosta aplikacja demonstracyjna logowania dwuskładnikowego w PHP.
|
||||||
|
|
||||||
|
## Wymagania
|
||||||
|
|
||||||
|
- PHP 7.4 lub nowszy
|
||||||
|
- SQLite3
|
||||||
|
- Serwer WWW (Apache/Nginx) lub wbudowany serwer PHP
|
||||||
|
|
||||||
|
## Instalacja
|
||||||
|
|
||||||
|
1. Skopiuj pliki na serwer WWW
|
||||||
|
2. Upewnij się, że katalog `database/` ma uprawnienia do zapisu
|
||||||
|
3. Przejdź do `/inicjalizacja` (lub `index.php?action=init`) aby zainicjalizować bazę danych
|
||||||
|
4. Zaloguj się używając testowego konta
|
||||||
|
|
||||||
|
## Przyjazne URLe
|
||||||
|
|
||||||
|
Aplikacja wspiera przyjazne URLe (friendly URLs):
|
||||||
|
|
||||||
|
- `/` lub `/logowanie` - ekran logowania
|
||||||
|
- `/uwierzytelnianie` - proces uwierzytelniania
|
||||||
|
- `/weryfikacja` - ekran weryfikacji 2FA
|
||||||
|
- `/weryfikuj-kod` - weryfikacja kodu
|
||||||
|
- `/panel` lub `/dashboard` - panel użytkownika
|
||||||
|
- `/wyloguj-sie` - wylogowanie
|
||||||
|
- `/inicjalizacja` - inicjalizacja bazy danych
|
||||||
|
|
||||||
|
Stare URLe z parametrem `?action=` nadal działają dla kompatybilności.
|
||||||
|
|
||||||
|
## Dane testowe
|
||||||
|
|
||||||
|
- **Login:** projectpro
|
||||||
|
- **Hasło:** testowehaslo
|
||||||
|
|
||||||
|
## Struktura projektu
|
||||||
|
|
||||||
|
```
|
||||||
|
projektphp/
|
||||||
|
├── controllers/ # Kontrolery MVC
|
||||||
|
│ ├── InitController.php
|
||||||
|
│ ├── LoginController.php
|
||||||
|
│ └── DashboardController.php
|
||||||
|
├── models/ # Modele danych
|
||||||
|
│ ├── Database.php
|
||||||
|
│ └── User.php
|
||||||
|
├── views/ # Widoki PHP
|
||||||
|
│ ├── layout.php
|
||||||
|
│ ├── login.php
|
||||||
|
│ ├── verify.php
|
||||||
|
│ └── dashboard.php
|
||||||
|
├── public/ # Publiczne zasoby
|
||||||
|
│ ├── css/
|
||||||
|
│ │ └── style.css
|
||||||
|
│ └── js/
|
||||||
|
│ └── app.js
|
||||||
|
├── database/ # Baza danych SQLite
|
||||||
|
│ └── database.db
|
||||||
|
├── .htaccess # Konfiguracja Apache
|
||||||
|
└── index.php # Punkt wejścia
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proces logowania
|
||||||
|
|
||||||
|
1. Użytkownik wprowadza login i hasło
|
||||||
|
2. System generuje 6-cyfrowy kod weryfikacyjny
|
||||||
|
3. Kod wyświetlany jest w konsoli przeglądarki (tryb testowy)
|
||||||
|
4. Po wprowadzeniu prawidłowego kodu użytkownik jest zalogowany
|
||||||
|
5. Kod wygasa po 15 minutach
|
||||||
|
|
||||||
|
## Uwagi
|
||||||
|
|
||||||
|
- W trybie testowym kod weryfikacyjny jest wyświetlany w konsoli przeglądarki (F12)
|
||||||
|
- W produkcji kod powinien być wysyłany emailem
|
||||||
|
- Hasła są hashowane przy użyciu `password_hash()`
|
||||||
|
- Sesje są bezpiecznie zarządzane
|
||||||
142
controllers/CalendarController.php
Normal file
142
controllers/CalendarController.php
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CalendarController
|
||||||
|
{
|
||||||
|
private $calendarModel;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->calendarModel = new CalendarEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
$month = $_GET['month'] ?? date('Y-m');
|
||||||
|
$monthDate = DateTime::createFromFormat('Y-m', $month);
|
||||||
|
if (!$monthDate) {
|
||||||
|
$monthDate = new DateTime('first day of this month');
|
||||||
|
$month = $monthDate->format('Y-m');
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstDay = (clone $monthDate)->modify('first day of this month');
|
||||||
|
$daysInMonth = (int)$firstDay->format('t');
|
||||||
|
$startWeekday = (int)$firstDay->format('N');
|
||||||
|
|
||||||
|
$prevMonth = (clone $monthDate)->modify('-1 month')->format('Y-m');
|
||||||
|
$nextMonth = (clone $monthDate)->modify('+1 month')->format('Y-m');
|
||||||
|
$monthLabel = $monthDate->format('F Y');
|
||||||
|
|
||||||
|
$selectedDate = $_GET['date'] ?? date('Y-m-d');
|
||||||
|
|
||||||
|
$events = $this->calendarModel->getByMonth($userId, $month);
|
||||||
|
$eventsByDate = [];
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$eventsByDate[$event['event_date']][] = $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
$eventsForSelected = $this->calendarModel->getByDate($userId, $selectedDate);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/calendar/index.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$event = null;
|
||||||
|
$defaultDate = $_GET['date'] ?? date('Y-m-d');
|
||||||
|
$returnMonth = $_GET['month'] ?? date('Y-m');
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/calendar/form.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit()
|
||||||
|
{
|
||||||
|
$eventId = $_GET['id'] ?? null;
|
||||||
|
if (!$eventId) {
|
||||||
|
$_SESSION['error'] = 'Nie podano ID wydarzenia';
|
||||||
|
header('Location: /kalendarz');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$event = $this->calendarModel->getById($eventId, $userId);
|
||||||
|
if (!$event) {
|
||||||
|
$_SESSION['error'] = 'Wydarzenie nie zostało znalezione';
|
||||||
|
header('Location: /kalendarz');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultDate = $event['event_date'];
|
||||||
|
$returnMonth = $_GET['month'] ?? date('Y-m', strtotime($event['event_date']));
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/calendar/form.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /kalendarz');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$eventId = $_POST['event_id'] ?? null;
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$content = trim($_POST['content'] ?? '');
|
||||||
|
$eventDate = $_POST['event_date'] ?? date('Y-m-d');
|
||||||
|
$returnMonth = $_POST['return_month'] ?? date('Y-m');
|
||||||
|
|
||||||
|
if ($title === '') {
|
||||||
|
$_SESSION['error'] = 'Tytuł wydarzenia jest wymagany';
|
||||||
|
$redirect = $eventId ? "/wydarzenie/edytuj?id=$eventId&month=$returnMonth" : "/wydarzenie/nowe?date=$eventDate&month=$returnMonth";
|
||||||
|
header('Location: ' . $redirect);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eventId) {
|
||||||
|
$this->calendarModel->update($eventId, $userId, $title, $content, $eventDate);
|
||||||
|
$_SESSION['success'] = 'Wydarzenie zostało zaktualizowane';
|
||||||
|
} else {
|
||||||
|
$this->calendarModel->create($userId, $title, $content, $eventDate);
|
||||||
|
$_SESSION['success'] = 'Wydarzenie zostało utworzone';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: /kalendarz?month=' . $returnMonth . '&date=' . $eventDate);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /kalendarz');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$eventId = $_POST['event_id'] ?? null;
|
||||||
|
$returnMonth = $_POST['return_month'] ?? date('Y-m');
|
||||||
|
$returnDate = $_POST['return_date'] ?? date('Y-m-d');
|
||||||
|
|
||||||
|
if (!$eventId) {
|
||||||
|
$_SESSION['error'] = 'Nie podano ID wydarzenia';
|
||||||
|
header('Location: /kalendarz');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$result = $this->calendarModel->delete($eventId, $userId);
|
||||||
|
if ($result) {
|
||||||
|
$_SESSION['success'] = 'Wydarzenie zostało usunięte';
|
||||||
|
} else {
|
||||||
|
$_SESSION['error'] = 'Nie udało się usunąć wydarzenia';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: /kalendarz?month=' . $returnMonth . '&date=' . $returnDate);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
controllers/DashboardController.php
Normal file
27
controllers/DashboardController.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class DashboardController
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userModel = new User();
|
||||||
|
$user = $userModel->getUserById($_SESSION['user_id']);
|
||||||
|
|
||||||
|
$calendarModel = new CalendarEvent();
|
||||||
|
$today = new DateTime('today');
|
||||||
|
$weekStart = (clone $today)->modify('monday this week');
|
||||||
|
$weekEnd = (clone $weekStart)->modify('+6 days');
|
||||||
|
$eventsThisWeek = $calendarModel->countByDateRange(
|
||||||
|
$_SESSION['user_id'],
|
||||||
|
$weekStart->format('Y-m-d'),
|
||||||
|
$weekEnd->format('Y-m-d')
|
||||||
|
);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/dashboard.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
55
controllers/InitController.php
Normal file
55
controllers/InitController.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class InitController
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = Database::getInstance();
|
||||||
|
$db->initDatabase();
|
||||||
|
|
||||||
|
echo "<!DOCTYPE html>
|
||||||
|
<html lang='pl'>
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8'>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||||
|
<title>Inicjalizacja bazy danych</title>
|
||||||
|
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
|
||||||
|
</head>
|
||||||
|
<body class='bg-light'>
|
||||||
|
<div class='container mt-5'>
|
||||||
|
<div class='alert alert-success' role='alert'>
|
||||||
|
<h4 class='alert-heading'>Sukces!</h4>
|
||||||
|
<p>Baza danych została pomyślnie zainicjalizowana.</p>
|
||||||
|
<hr>
|
||||||
|
<p class='mb-0'>Testowy użytkownik:<br>
|
||||||
|
Login: <strong>projectpro</strong><br>
|
||||||
|
Hasło: <strong>testowehaslo</strong></p>
|
||||||
|
</div>
|
||||||
|
<a href='/logowanie' class='btn btn-primary'>Przejdź do logowania</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "<!DOCTYPE html>
|
||||||
|
<html lang='pl'>
|
||||||
|
<head>
|
||||||
|
<meta charset='UTF-8'>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||||
|
<title>Błąd inicjalizacji</title>
|
||||||
|
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
|
||||||
|
</head>
|
||||||
|
<body class='bg-light'>
|
||||||
|
<div class='container mt-5'>
|
||||||
|
<div class='alert alert-danger' role='alert'>
|
||||||
|
<h4 class='alert-heading'>Błąd!</h4>
|
||||||
|
<p>Nie udało się zainicjalizować bazy danych.</p>
|
||||||
|
<hr>
|
||||||
|
<p class='mb-0'>Szczegóły: " . htmlspecialchars($e->getMessage()) . "</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
controllers/LoginController.php
Normal file
98
controllers/LoginController.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LoginController
|
||||||
|
{
|
||||||
|
private $userModel;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userModel = new User();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: /panel');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/login.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authenticate()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_POST['username'] ?? '';
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
$user = $this->userModel->authenticate($username, $password);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
// Generowanie kodu weryfikacyjnego
|
||||||
|
$code = $this->userModel->generateVerificationCode($user['id']);
|
||||||
|
|
||||||
|
// Zapisanie ID użytkownika w sesji tymczasowo
|
||||||
|
$_SESSION['pending_user_id'] = $user['id'];
|
||||||
|
$_SESSION['pending_username'] = $user['username'];
|
||||||
|
|
||||||
|
// W rzeczywistości tutaj wysłalibyśmy email
|
||||||
|
// Dla testów kod będzie wyświetlony w konsoli przeglądarki
|
||||||
|
$_SESSION['test_code'] = $code;
|
||||||
|
|
||||||
|
header('Location: /weryfikacja');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$_SESSION['error'] = 'Nieprawidłowy login lub hasło';
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verify()
|
||||||
|
{
|
||||||
|
if (!isset($_SESSION['pending_user_id'])) {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/verify.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verifyCode()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['pending_user_id'])) {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = $_POST['code'] ?? '';
|
||||||
|
$userId = $_SESSION['pending_user_id'];
|
||||||
|
|
||||||
|
if ($this->userModel->verifyCode($userId, $code)) {
|
||||||
|
// Zalogowanie użytkownika
|
||||||
|
$_SESSION['user_id'] = $userId;
|
||||||
|
$_SESSION['username'] = $_SESSION['pending_username'];
|
||||||
|
|
||||||
|
// Czyszczenie danych tymczasowych
|
||||||
|
unset($_SESSION['pending_user_id']);
|
||||||
|
unset($_SESSION['pending_username']);
|
||||||
|
unset($_SESSION['test_code']);
|
||||||
|
|
||||||
|
header('Location: /panel');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$_SESSION['error'] = 'Nieprawidłowy kod weryfikacyjny lub kod wygasł';
|
||||||
|
header('Location: /weryfikacja');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
controllers/NotesController.php
Normal file
114
controllers/NotesController.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class NotesController
|
||||||
|
{
|
||||||
|
private $notesModel;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->notesModel = new Notes();
|
||||||
|
|
||||||
|
// Sprawdzenie czy użytkownik jest zalogowany
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$notes = $this->notesModel->getAllByUser($userId);
|
||||||
|
$notesCount = $this->notesModel->getCount($userId);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/notes/index.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$note = null; // Pusty formularz dla nowej notatki
|
||||||
|
require_once __DIR__ . '/../views/notes/form.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit()
|
||||||
|
{
|
||||||
|
$noteId = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$noteId) {
|
||||||
|
$_SESSION['error'] = 'Nie podano ID notatki';
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$note = $this->notesModel->getById($noteId, $userId);
|
||||||
|
|
||||||
|
if (!$note) {
|
||||||
|
$_SESSION['error'] = 'Notatka nie została znaleziona';
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../views/notes/form.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$noteId = $_POST['note_id'] ?? null;
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$content = trim($_POST['content'] ?? '');
|
||||||
|
|
||||||
|
if (empty($title)) {
|
||||||
|
$_SESSION['error'] = 'Tytuł notatki jest wymagany';
|
||||||
|
header('Location: ' . ($noteId ? "/notatka/edytuj?id=$noteId" : '/notatka/nowa'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($noteId) {
|
||||||
|
// Aktualizacja istniejącej notatki
|
||||||
|
$result = $this->notesModel->update($noteId, $userId, $title, $content);
|
||||||
|
$_SESSION['success'] = 'Notatka została zaktualizowana';
|
||||||
|
} else {
|
||||||
|
// Tworzenie nowej notatki
|
||||||
|
$result = $this->notesModel->create($userId, $title, $content);
|
||||||
|
$_SESSION['success'] = 'Notatka została utworzona';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$noteId = $_POST['note_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$noteId) {
|
||||||
|
$_SESSION['error'] = 'Nie podano ID notatki';
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$result = $this->notesModel->delete($noteId, $userId);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
$_SESSION['success'] = 'Notatka została usunięta';
|
||||||
|
} else {
|
||||||
|
$_SESSION['error'] = 'Nie udało się usunąć notatki';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: /notatnik');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
157
index.php
Normal file
157
index.php
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Autoloader dla klas
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
$paths = [
|
||||||
|
__DIR__ . '/controllers/' . $class . '.php',
|
||||||
|
__DIR__ . '/models/' . $class . '.php'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (file_exists($path)) {
|
||||||
|
require_once $path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pobieranie ścieżki z URL (odporne na root i podkatalog)
|
||||||
|
$requestUri = $_SERVER['REQUEST_URI'];
|
||||||
|
$uriPath = parse_url($requestUri, PHP_URL_PATH) ?? '/';
|
||||||
|
$basePath = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
|
||||||
|
|
||||||
|
if ($basePath && $basePath !== '.' && strpos($uriPath, $basePath) === 0) {
|
||||||
|
$uriPath = substr($uriPath, strlen($basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = trim($uriPath, '/');
|
||||||
|
|
||||||
|
// Debug - usuń po naprawie
|
||||||
|
// error_log("REQUEST_URI: " . $requestUri);
|
||||||
|
// error_log("Base Path: " . $basePath);
|
||||||
|
// error_log("Path: " . $path);
|
||||||
|
|
||||||
|
// Mapowanie przyjaznych URLi na akcje
|
||||||
|
$routes = [
|
||||||
|
'' => 'login',
|
||||||
|
'logowanie' => 'login',
|
||||||
|
'uwierzytelnianie' => 'authenticate',
|
||||||
|
'weryfikacja' => 'verify',
|
||||||
|
'weryfikuj-kod' => 'verify-code',
|
||||||
|
'panel' => 'dashboard',
|
||||||
|
'dashboard' => 'dashboard',
|
||||||
|
'pulpit' => 'dashboard',
|
||||||
|
'wyloguj-sie' => 'logout',
|
||||||
|
'inicjalizacja' => 'init',
|
||||||
|
'notatnik' => 'notes',
|
||||||
|
'notatki' => 'notes',
|
||||||
|
'notatka/nowa' => 'note-create',
|
||||||
|
'notatka/edytuj' => 'note-edit',
|
||||||
|
'notatka/zapisz' => 'note-save',
|
||||||
|
'notatka/usun' => 'note-delete',
|
||||||
|
'kalendarz' => 'calendar',
|
||||||
|
'wydarzenie/nowe' => 'event-create',
|
||||||
|
'wydarzenie/edytuj' => 'event-edit',
|
||||||
|
'wydarzenie/zapisz' => 'event-save',
|
||||||
|
'wydarzenie/usun' => 'event-delete',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Obsługa starych URLi z parametrem action dla kompatybilności
|
||||||
|
if (isset($_GET['action'])) {
|
||||||
|
$action = $_GET['action'];
|
||||||
|
} else {
|
||||||
|
$action = $routes[$path] ?? 'login';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case 'init':
|
||||||
|
$controller = new InitController();
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'login':
|
||||||
|
$controller = new LoginController();
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'authenticate':
|
||||||
|
$controller = new LoginController();
|
||||||
|
$controller->authenticate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'verify':
|
||||||
|
$controller = new LoginController();
|
||||||
|
$controller->verify();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'verify-code':
|
||||||
|
$controller = new LoginController();
|
||||||
|
$controller->verifyCode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'dashboard':
|
||||||
|
$controller = new DashboardController();
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'notes':
|
||||||
|
$controller = new NotesController();
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'note-create':
|
||||||
|
$controller = new NotesController();
|
||||||
|
$controller->create();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'note-edit':
|
||||||
|
$controller = new NotesController();
|
||||||
|
$controller->edit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'note-save':
|
||||||
|
$controller = new NotesController();
|
||||||
|
$controller->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'note-delete':
|
||||||
|
$controller = new NotesController();
|
||||||
|
$controller->delete();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'calendar':
|
||||||
|
$controller = new CalendarController();
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'event-create':
|
||||||
|
$controller = new CalendarController();
|
||||||
|
$controller->create();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'event-edit':
|
||||||
|
$controller = new CalendarController();
|
||||||
|
$controller->edit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'event-save':
|
||||||
|
$controller = new CalendarController();
|
||||||
|
$controller->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'event-delete':
|
||||||
|
$controller = new CalendarController();
|
||||||
|
$controller->delete();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'logout':
|
||||||
|
session_destroy();
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
header('Location: /logowanie');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
104
models/CalendarEvent.php
Normal file
104
models/CalendarEvent.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CalendarEvent
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->db = Database::getInstance()->getConnection();
|
||||||
|
$this->initTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initTable()
|
||||||
|
{
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS calendar_events (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT,
|
||||||
|
event_date DATE NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
)";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->db->exec($sql);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Tabela już istnieje
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByMonth($userId, $month)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM calendar_events
|
||||||
|
WHERE user_id = ? AND strftime('%Y-%m', event_date) = ?
|
||||||
|
ORDER BY event_date ASC, id ASC
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId, $month]);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByDate($userId, $date)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM calendar_events
|
||||||
|
WHERE user_id = ? AND event_date = ?
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId, $date]);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getById($id, $userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM calendar_events
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$id, $userId]);
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create($userId, $title, $content, $eventDate)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
INSERT INTO calendar_events (user_id, title, content, event_date)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
");
|
||||||
|
return $stmt->execute([$userId, $title, $content, $eventDate]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($id, $userId, $title, $content, $eventDate)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
UPDATE calendar_events
|
||||||
|
SET title = ?, content = ?, event_date = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
return $stmt->execute([$title, $content, $eventDate, $id, $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($id, $userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
DELETE FROM calendar_events
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
return $stmt->execute([$id, $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countByDateRange($userId, $startDate, $endDate)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT COUNT(*) AS count
|
||||||
|
FROM calendar_events
|
||||||
|
WHERE user_id = ? AND event_date BETWEEN ? AND ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId, $startDate, $endDate]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return (int)$result['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
73
models/Database.php
Normal file
73
models/Database.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Database
|
||||||
|
{
|
||||||
|
private static $instance = null;
|
||||||
|
private $connection;
|
||||||
|
private $dbPath;
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
$this->dbPath = __DIR__ . '/../database/database.db';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
if ($this->connection === null) {
|
||||||
|
try {
|
||||||
|
$this->connection = new PDO('sqlite:' . $this->dbPath);
|
||||||
|
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die('Connection failed: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initDatabase()
|
||||||
|
{
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
// Tworzenie tabeli użytkowników
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)";
|
||||||
|
$db->exec($sql);
|
||||||
|
|
||||||
|
// Tworzenie tabeli kodów weryfikacyjnych
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS verification_codes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
code TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
expires_at DATETIME NOT NULL,
|
||||||
|
used INTEGER DEFAULT 0,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
)";
|
||||||
|
$db->exec($sql);
|
||||||
|
|
||||||
|
// Dodawanie testowego użytkownika
|
||||||
|
$username = 'projectpro';
|
||||||
|
$password = password_hash('testowehaslo', PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$username, $password]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Użytkownik już istnieje
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
models/Notes.php
Normal file
92
models/Notes.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Notes
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->db = Database::getInstance()->getConnection();
|
||||||
|
$this->initTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initTable()
|
||||||
|
{
|
||||||
|
// Tworzenie tabeli notatek jeśli nie istnieje
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS notes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
)";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->db->exec($sql);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Tabela już istnieje
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByUser($userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM notes
|
||||||
|
WHERE user_id = ?
|
||||||
|
ORDER BY updated_at DESC
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId]);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getById($id, $userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM notes
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$id, $userId]);
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create($userId, $title, $content)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
INSERT INTO notes (user_id, title, content)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
");
|
||||||
|
return $stmt->execute([$userId, $title, $content]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($id, $userId, $title, $content)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
UPDATE notes
|
||||||
|
SET title = ?, content = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
return $stmt->execute([$title, $content, $id, $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($id, $userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
DELETE FROM notes
|
||||||
|
WHERE id = ? AND user_id = ?
|
||||||
|
");
|
||||||
|
return $stmt->execute([$id, $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCount($userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT COUNT(*) as count FROM notes
|
||||||
|
WHERE user_id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return $result['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
70
models/User.php
Normal file
70
models/User.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->db = Database::getInstance()->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authenticate($username, $password)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateVerificationCode($userId)
|
||||||
|
{
|
||||||
|
// Generowanie 6-cyfrowego kodu
|
||||||
|
$code = sprintf('%06d', random_int(0, 999999));
|
||||||
|
|
||||||
|
// Ustawienie czasu wygaśnięcia (15 minut)
|
||||||
|
$expiresAt = date('Y-m-d H:i:s', strtotime('+15 minutes'));
|
||||||
|
|
||||||
|
// Usuwanie starych nieużytych kodów dla tego użytkownika
|
||||||
|
$stmt = $this->db->prepare("DELETE FROM verification_codes WHERE user_id = ? AND used = 0");
|
||||||
|
$stmt->execute([$userId]);
|
||||||
|
|
||||||
|
// Zapisywanie nowego kodu
|
||||||
|
$stmt = $this->db->prepare("INSERT INTO verification_codes (user_id, code, expires_at) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$userId, $code, $expiresAt]);
|
||||||
|
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verifyCode($userId, $code)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("
|
||||||
|
SELECT * FROM verification_codes
|
||||||
|
WHERE user_id = ? AND code = ? AND used = 0 AND expires_at > datetime('now')
|
||||||
|
ORDER BY created_at DESC LIMIT 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId, $code]);
|
||||||
|
$verification = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($verification) {
|
||||||
|
// Oznaczenie kodu jako użyty
|
||||||
|
$stmt = $this->db->prepare("UPDATE verification_codes SET used = 1 WHERE id = ?");
|
||||||
|
$stmt->execute([$verification['id']]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserById($userId)
|
||||||
|
{
|
||||||
|
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$userId]);
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
public/css/calendar.css
Normal file
81
public/css/calendar.css
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* Calendar styles */
|
||||||
|
|
||||||
|
.calendar-title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekday {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6c757d;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cell {
|
||||||
|
min-height: 110px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cell--disabled {
|
||||||
|
background: #f8f9fa;
|
||||||
|
color: #adb5bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cell--selected {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #212529;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-events {
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
color: #0d6efd;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event i {
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event--more {
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.calendar-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekday {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
137
public/css/notes.css
Normal file
137
public/css/notes.css
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/* Notes styles */
|
||||||
|
|
||||||
|
.note-card {
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-content {
|
||||||
|
min-height: 100px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-meta {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gradient backgrounds */
|
||||||
|
.bg-gradient-info {
|
||||||
|
background: linear-gradient(135deg, #36d1dc 0%, #5b86e5 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient-success {
|
||||||
|
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-shadow {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-shadow:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal animations */
|
||||||
|
.modal.fade .modal-dialog {
|
||||||
|
transition: transform 0.3s ease-out;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.show .modal-dialog {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar improvements */
|
||||||
|
.navbar {
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link.active {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card improvements */
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button improvements */
|
||||||
|
.btn {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty state */
|
||||||
|
.fa-inbox {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive textarea */
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete modal styling */
|
||||||
|
#deleteModal .modal-header {
|
||||||
|
border-top-left-radius: 0.75rem;
|
||||||
|
border-top-right-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#deleteModal .modal-content {
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert animations */
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: translateY(-20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note title truncation on cards */
|
||||||
|
.note-card .card-title {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
115
public/css/style.css
Normal file
115
public/css/style.css
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/* Gradient Background */
|
||||||
|
.bg-gradient-primary {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login/Verify Card Styles */
|
||||||
|
.card {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Styles */
|
||||||
|
.form-control-user {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-radius: 10rem;
|
||||||
|
padding: 1.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-user {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-radius: 10rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background Images for Login/Verify Cards */
|
||||||
|
.bg-login-image {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
border-radius: 1rem 0 0 1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-login-image::before {
|
||||||
|
content: "🔐";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 6rem;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-verify-image {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
border-radius: 1rem 0 0 1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-verify-image::before {
|
||||||
|
content: "🔑";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 6rem;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar Styles */
|
||||||
|
.navbar-dark {
|
||||||
|
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: 700;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert Styles */
|
||||||
|
.alert {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Animations */
|
||||||
|
.card {
|
||||||
|
animation: fadeIn 0.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code Input Special Style */
|
||||||
|
input[name="code"] {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
letter-spacing: 0.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dashboard Cards */
|
||||||
|
.bg-light {
|
||||||
|
background-color: #f8f9fc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.bg-login-image,
|
||||||
|
.bg-verify-image {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
public/js/app.js
Normal file
50
public/js/app.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Simple app.js for additional functionality
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Auto-dismiss alerts after 5 seconds
|
||||||
|
const alerts = document.querySelectorAll('.alert:not(.alert-info)');
|
||||||
|
alerts.forEach(function(alert) {
|
||||||
|
setTimeout(function() {
|
||||||
|
const bsAlert = new bootstrap.Alert(alert);
|
||||||
|
bsAlert.close();
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add loading state to forms
|
||||||
|
const forms = document.querySelectorAll('form');
|
||||||
|
forms.forEach(function(form) {
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
const submitBtn = form.querySelector('button[type="submit"]');
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Przetwarzanie...';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Code input - accept only numbers
|
||||||
|
const codeInput = document.getElementById('code');
|
||||||
|
if (codeInput) {
|
||||||
|
codeInput.addEventListener('keypress', function(e) {
|
||||||
|
// Allow only numbers
|
||||||
|
if (e.key < '0' || e.key > '9') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-submit when 6 digits entered
|
||||||
|
codeInput.addEventListener('input', function(e) {
|
||||||
|
if (this.value.length === 6) {
|
||||||
|
// Optional: auto-submit after 6 digits
|
||||||
|
// this.form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add smooth scroll behavior
|
||||||
|
document.documentElement.style.scrollBehavior = 'smooth';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Console styling for verification code (if shown)
|
||||||
|
console.log('%cSystem Logowania 2FA', 'color: #667eea; font-size: 24px; font-weight: bold;');
|
||||||
|
console.log('%cAplikacja demonstracyjna', 'color: #666; font-size: 12px;');
|
||||||
152
views/calendar/form.php
Normal file
152
views/calendar/form.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = (isset($event) ? 'Edycja' : 'Nowe') . ' wydarzenie - System 2FA';
|
||||||
|
$bodyClass = 'bg-light';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">'
|
||||||
|
. '<link rel="stylesheet" href="/public/css/calendar.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/panel">
|
||||||
|
<i class="fas fa-shield-alt"></i> System 2FA
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/pulpit">
|
||||||
|
<i class="fas fa-home"></i> Pulpit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/notatnik">
|
||||||
|
<i class="fas fa-sticky-note"></i> Notatnik
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/kalendarz">
|
||||||
|
<i class="fas fa-calendar-alt"></i> Kalendarz
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<span class="nav-link text-white">
|
||||||
|
<i class="fas fa-user"></i> <?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/wyloguj-sie">
|
||||||
|
<i class="fas fa-sign-out-alt"></i> Wyloguj
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h4 class="mb-0">
|
||||||
|
<i class="fas fa-calendar-plus"></i> <?php echo isset($event) ? 'Edycja wydarzenia' : 'Nowe wydarzenie'; ?>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" action="/wydarzenie/zapisz">
|
||||||
|
<?php if (isset($event)): ?>
|
||||||
|
<input type="hidden" name="event_id" value="<?php echo $event['id']; ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<input type="hidden" name="return_month" value="<?php echo htmlspecialchars($returnMonth); ?>">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="title" class="form-label">
|
||||||
|
<i class="fas fa-heading"></i> Tytuł wydarzenia *
|
||||||
|
</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control form-control-lg"
|
||||||
|
id="title"
|
||||||
|
name="title"
|
||||||
|
placeholder="Wpisz tytuł wydarzenia..."
|
||||||
|
value="<?php echo isset($event) ? htmlspecialchars($event['title']) : ''; ?>"
|
||||||
|
required
|
||||||
|
autofocus>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="event_date" class="form-label">
|
||||||
|
<i class="fas fa-calendar-day"></i> Data wydarzenia *
|
||||||
|
</label>
|
||||||
|
<input type="date"
|
||||||
|
class="form-control"
|
||||||
|
id="event_date"
|
||||||
|
name="event_date"
|
||||||
|
value="<?php echo htmlspecialchars($defaultDate); ?>"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="content" class="form-label">
|
||||||
|
<i class="fas fa-align-left"></i> Treść wydarzenia
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control"
|
||||||
|
id="content"
|
||||||
|
name="content"
|
||||||
|
rows="8"
|
||||||
|
placeholder="Wpisz treść wydarzenia..."><?php echo isset($event) ? htmlspecialchars($event['content']) : ''; ?></textarea>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
<i class="fas fa-info-circle"></i> Pole opcjonalne
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<a href="/kalendarz?month=<?php echo htmlspecialchars($returnMonth); ?>&date=<?php echo htmlspecialchars($defaultDate); ?>" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left"></i> Powrót do kalendarza
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">
|
||||||
|
<i class="fas fa-save"></i> Zapisz wydarzenie
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php if (isset($event)): ?>
|
||||||
|
<div class="card-footer text-muted small">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<i class="fas fa-calendar-plus"></i> Utworzono:
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($event['created_at']);
|
||||||
|
echo $date->format('d.m.Y H:i');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<i class="fas fa-calendar-check"></i> Zaktualizowano:
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($event['updated_at']);
|
||||||
|
echo $date->format('d.m.Y H:i');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
require __DIR__ . '/../layout.php';
|
||||||
|
?>
|
||||||
221
views/calendar/index.php
Normal file
221
views/calendar/index.php
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = 'Kalendarz - System 2FA';
|
||||||
|
$bodyClass = 'bg-light';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">'
|
||||||
|
. '<link rel="stylesheet" href="/public/css/calendar.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/panel">
|
||||||
|
<i class="fas fa-shield-alt"></i> System 2FA
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/pulpit">
|
||||||
|
<i class="fas fa-home"></i> Pulpit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/notatnik">
|
||||||
|
<i class="fas fa-sticky-note"></i> Notatnik
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/kalendarz">
|
||||||
|
<i class="fas fa-calendar-alt"></i> Kalendarz
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<span class="nav-link text-white">
|
||||||
|
<i class="fas fa-user"></i> <?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/wyloguj-sie">
|
||||||
|
<i class="fas fa-sign-out-alt"></i> Wyloguj
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<?php if (isset($_SESSION['success'])): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-check-circle"></i> <?php echo htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<a class="btn btn-outline-secondary" href="/kalendarz?month=<?php echo $prevMonth; ?>">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</a>
|
||||||
|
<h3 class="mb-0 calendar-title"><?php echo htmlspecialchars($monthLabel); ?></h3>
|
||||||
|
<a class="btn btn-outline-secondary" href="/kalendarz?month=<?php echo $nextMonth; ?>">
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a href="/wydarzenie/nowe?date=<?php echo htmlspecialchars($selectedDate); ?>&month=<?php echo $month; ?>" class="btn btn-primary">
|
||||||
|
<i class="fas fa-plus"></i> Nowe wydarzenie
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calendar-grid">
|
||||||
|
<div class="calendar-weekday">Pon</div>
|
||||||
|
<div class="calendar-weekday">Wto</div>
|
||||||
|
<div class="calendar-weekday">Śro</div>
|
||||||
|
<div class="calendar-weekday">Czw</div>
|
||||||
|
<div class="calendar-weekday">Pią</div>
|
||||||
|
<div class="calendar-weekday">Sob</div>
|
||||||
|
<div class="calendar-weekday">Ndz</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$dayCounter = 1;
|
||||||
|
$totalCells = 42; // 6 tygodni
|
||||||
|
|
||||||
|
for ($cell = 1; $cell <= $totalCells; $cell++):
|
||||||
|
$cellDay = $cell - $startWeekday + 1;
|
||||||
|
$isCurrentMonth = $cellDay >= 1 && $cellDay <= $daysInMonth;
|
||||||
|
$dateStr = $isCurrentMonth ? $monthDate->format('Y-m') . '-' . str_pad($cellDay, 2, '0', STR_PAD_LEFT) : '';
|
||||||
|
$isSelected = $isCurrentMonth && $dateStr === $selectedDate;
|
||||||
|
$eventsForDay = $isCurrentMonth && isset($eventsByDate[$dateStr]) ? $eventsByDate[$dateStr] : [];
|
||||||
|
?>
|
||||||
|
<div class="calendar-cell <?php echo $isCurrentMonth ? '' : 'calendar-cell--disabled'; ?> <?php echo $isSelected ? 'calendar-cell--selected' : ''; ?>">
|
||||||
|
<?php if ($isCurrentMonth): ?>
|
||||||
|
<a class="calendar-day" href="/kalendarz?month=<?php echo $month; ?>&date=<?php echo $dateStr; ?>">
|
||||||
|
<?php echo $cellDay; ?>
|
||||||
|
</a>
|
||||||
|
<?php if (!empty($eventsForDay)): ?>
|
||||||
|
<div class="calendar-events">
|
||||||
|
<?php foreach (array_slice($eventsForDay, 0, 2) as $event): ?>
|
||||||
|
<div class="calendar-event">
|
||||||
|
<i class="fas fa-circle"></i>
|
||||||
|
<?php echo htmlspecialchars($event['title']); ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (count($eventsForDay) > 2): ?>
|
||||||
|
<div class="calendar-event calendar-event--more">+<?php echo count($eventsForDay) - 2; ?> więcej</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<i class="fas fa-list"></i> Wydarzenia: <?php echo htmlspecialchars($selectedDate); ?>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (empty($eventsForSelected)): ?>
|
||||||
|
<p class="text-muted mb-0">Brak wydarzeń dla wybranego dnia.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="list-group">
|
||||||
|
<?php foreach ($eventsForSelected as $event): ?>
|
||||||
|
<div class="list-group-item list-group-item-action d-flex justify-content-between align-items-start">
|
||||||
|
<div class="me-3">
|
||||||
|
<h6 class="mb-1">
|
||||||
|
<i class="fas fa-calendar-check text-primary"></i>
|
||||||
|
<?php echo htmlspecialchars($event['title']); ?>
|
||||||
|
</h6>
|
||||||
|
<p class="mb-1 small text-muted">
|
||||||
|
<?php echo nl2br(htmlspecialchars($event['content'] ?? '')); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-sm btn-outline-primary" href="/wydarzenie/edytuj?id=<?php echo $event['id']; ?>&month=<?php echo $month; ?>">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-sm btn-outline-danger" type="button"
|
||||||
|
onclick="confirmDeleteEvent(<?php echo $event['id']; ?>, '<?php echo htmlspecialchars(addslashes($event['title'])); ?>')">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 mt-3 mt-lg-0">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-light">
|
||||||
|
<i class="fas fa-info-circle"></i> Szybkie akcje
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="mb-2">Dodaj wydarzenie dla wybranego dnia:</p>
|
||||||
|
<a class="btn btn-primary" href="/wydarzenie/nowe?date=<?php echo htmlspecialchars($selectedDate); ?>&month=<?php echo $month; ?>">
|
||||||
|
<i class="fas fa-plus"></i> Dodaj wydarzenie
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="deleteEventModal" tabindex="-1" aria-labelledby="deleteEventModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-white">
|
||||||
|
<h5 class="modal-title" id="deleteEventModalLabel">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i> Potwierdź usunięcie
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Czy na pewno chcesz usunąć wydarzenie:</p>
|
||||||
|
<p class="fw-bold" id="eventTitle"></p>
|
||||||
|
<p class="text-muted small">Tej operacji nie można cofnąć.</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||||
|
<i class="fas fa-times"></i> Anuluj
|
||||||
|
</button>
|
||||||
|
<form method="POST" action="/wydarzenie/usun">
|
||||||
|
<input type="hidden" name="event_id" id="deleteEventId">
|
||||||
|
<input type="hidden" name="return_month" value="<?php echo $month; ?>">
|
||||||
|
<input type="hidden" name="return_date" value="<?php echo htmlspecialchars($selectedDate); ?>">
|
||||||
|
<button type="submit" class="btn btn-danger">
|
||||||
|
<i class="fas fa-trash"></i> Usuń wydarzenie
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
$extraScript = '<script>
|
||||||
|
function confirmDeleteEvent(eventId, eventTitle) {
|
||||||
|
document.getElementById("deleteEventId").value = eventId;
|
||||||
|
document.getElementById("eventTitle").textContent = eventTitle;
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById("deleteEventModal"));
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
require __DIR__ . '/../layout.php';
|
||||||
|
?>
|
||||||
144
views/dashboard.php
Normal file
144
views/dashboard.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = 'Panel - System 2FA';
|
||||||
|
$bodyClass = 'bg-light';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/panel">
|
||||||
|
<i class="fas fa-shield-alt"></i> System 2FA
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/pulpit">
|
||||||
|
<i class="fas fa-home"></i> Pulpit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/notatnik">
|
||||||
|
<i class="fas fa-sticky-note"></i> Notatnik
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/kalendarz">
|
||||||
|
<i class="fas fa-calendar-alt"></i> Kalendarz
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<span class="nav-link text-white">
|
||||||
|
<i class="fas fa-user"></i> <?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/wyloguj-sie">
|
||||||
|
<i class="fas fa-sign-out-alt"></i> Wyloguj
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h4 class="mb-0">
|
||||||
|
<i class="fas fa-home"></i> Panel główny
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
<h4 class="alert-heading">
|
||||||
|
<i class="fas fa-user-check"></i> Witaj, <?php echo htmlspecialchars($user['username']); ?>! 🎉
|
||||||
|
</h4>
|
||||||
|
<p>Pomyślnie zalogowałeś się do systemu z uwierzytelnianiem dwuskładnikowym.</p>
|
||||||
|
<hr>
|
||||||
|
<p class="mb-0">
|
||||||
|
<i class="fas fa-calendar-alt"></i> <strong>Data rejestracji:</strong>
|
||||||
|
<?php echo date('d.m.Y H:i', strtotime($user['created_at'])); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="mt-4 mb-3"><i class="fas fa-th-large"></i> Szybki dostęp</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<a href="/notatnik" class="text-decoration-none">
|
||||||
|
<div class="card bg-gradient-info text-white h-100 hover-shadow">
|
||||||
|
<div class="card-body text-center py-4">
|
||||||
|
<i class="fas fa-sticky-note fa-3x mb-3"></i>
|
||||||
|
<h5 class="card-title">Notatnik</h5>
|
||||||
|
<p class="card-text">Zarządzaj swoimi notatkami</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<a href="/pulpit" class="text-decoration-none">
|
||||||
|
<div class="card bg-gradient-success text-white h-100 hover-shadow">
|
||||||
|
<div class="card-body text-center py-4">
|
||||||
|
<i class="fas fa-home fa-3x mb-3"></i>
|
||||||
|
<h5 class="card-title">Pulpit</h5>
|
||||||
|
<p class="card-text">Strona główna panelu</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<a href="/kalendarz" class="text-decoration-none">
|
||||||
|
<div class="card bg-primary text-white h-100 hover-shadow">
|
||||||
|
<div class="card-body text-center py-4">
|
||||||
|
<i class="fas fa-calendar-alt fa-3x mb-3"></i>
|
||||||
|
<h5 class="card-title">Kalendarz</h5>
|
||||||
|
<p class="card-text">Wydarzenia w tym tygodniu: <strong><?php echo (int)$eventsThisWeek; ?></strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="mt-4 mb-3"><i class="fas fa-info-circle"></i> Bezpieczeństwo</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div class="card bg-light h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-title">
|
||||||
|
<i class="fas fa-check-circle text-success"></i> Bezpieczne logowanie
|
||||||
|
</h6>
|
||||||
|
<p class="card-text small">Twoje konto jest chronione uwierzytelnianiem dwuskładnikowym</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div class="card bg-light h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-title">
|
||||||
|
<i class="fas fa-lock text-primary"></i> Sesja aktywna
|
||||||
|
</h6>
|
||||||
|
<p class="card-text small">Twoja sesja jest bezpiecznie zarządzana przez system</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted text-center">
|
||||||
|
<small>System logowania dwuskładnikowego - Demo</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
require __DIR__ . '/layout.php';
|
||||||
|
?>
|
||||||
22
views/layout.php
Normal file
22
views/layout.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo $pageTitle ?? 'Aplikacja'; ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="/public/css/style.css">
|
||||||
|
<?php if (isset($extraHead)): ?>
|
||||||
|
<?php echo $extraHead; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</head>
|
||||||
|
<body class="<?php echo $bodyClass ?? 'bg-light'; ?>">
|
||||||
|
<?php echo $content ?? ''; ?>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="/public/js/app.js"></script>
|
||||||
|
<?php if (isset($extraScript)): ?>
|
||||||
|
<?php echo $extraScript; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
63
views/login.php
Normal file
63
views/login.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = 'Logowanie - System 2FA';
|
||||||
|
$bodyClass = 'bg-gradient-primary';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center align-items-center min-vh-100">
|
||||||
|
<div class="col-xl-10 col-lg-12 col-md-9">
|
||||||
|
<div class="card o-hidden border-0 shadow-lg my-5">
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 d-none d-lg-block bg-login-image"></div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="p-5">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="h4 text-gray-900 mb-4">System Logowania 2FA</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<?php
|
||||||
|
echo htmlspecialchars($_SESSION['error']);
|
||||||
|
unset($_SESSION['error']);
|
||||||
|
?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form class="user" method="POST" action="/uwierzytelnianie">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<input type="text" class="form-control form-control-user"
|
||||||
|
id="username" name="username"
|
||||||
|
placeholder="Login" required autofocus>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<input type="password" class="form-control form-control-user"
|
||||||
|
id="password" name="password"
|
||||||
|
placeholder="Hasło" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-user btn-block w-100">
|
||||||
|
Zaloguj się
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="text-center">
|
||||||
|
<small class="text-muted">
|
||||||
|
Testowy użytkownik: <strong>projectpro</strong> / <strong>testowehaslo</strong>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
require __DIR__ . '/layout.php';
|
||||||
|
?>
|
||||||
147
views/notes/form.php
Normal file
147
views/notes/form.php
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = (isset($note) ? 'Edycja' : 'Nowa') . ' notatka - System 2FA';
|
||||||
|
$bodyClass = 'bg-light';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">'
|
||||||
|
. '<link rel="stylesheet" href="/public/css/notes.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/panel">
|
||||||
|
<i class="fas fa-shield-alt"></i> System 2FA
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/pulpit">
|
||||||
|
<i class="fas fa-home"></i> Pulpit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/notatnik">
|
||||||
|
<i class="fas fa-sticky-note"></i> Notatnik
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/kalendarz">
|
||||||
|
<i class="fas fa-calendar-alt"></i> Kalendarz
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<span class="nav-link text-white">
|
||||||
|
<i class="fas fa-user"></i> <?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/wyloguj-sie">
|
||||||
|
<i class="fas fa-sign-out-alt"></i> Wyloguj
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h4 class="mb-0">
|
||||||
|
<i class="fas fa-<?php echo isset($note) ? 'edit' : 'plus'; ?>"></i>
|
||||||
|
<?php echo isset($note) ? 'Edycja notatki' : 'Nowa notatka'; ?>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" action="/notatka/zapisz">
|
||||||
|
<?php if (isset($note)): ?>
|
||||||
|
<input type="hidden" name="note_id" value="<?php echo $note['id']; ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="title" class="form-label">
|
||||||
|
<i class="fas fa-heading"></i> Tytuł notatki *
|
||||||
|
</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control form-control-lg"
|
||||||
|
id="title"
|
||||||
|
name="title"
|
||||||
|
placeholder="Wpisz tytuł notatki..."
|
||||||
|
value="<?php echo isset($note) ? htmlspecialchars($note['title']) : ''; ?>"
|
||||||
|
required
|
||||||
|
autofocus>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="content" class="form-label">
|
||||||
|
<i class="fas fa-align-left"></i> Treść notatki
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control"
|
||||||
|
id="content"
|
||||||
|
name="content"
|
||||||
|
rows="12"
|
||||||
|
placeholder="Wpisz treść notatki..."><?php echo isset($note) ? htmlspecialchars($note['content']) : ''; ?></textarea>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
<i class="fas fa-info-circle"></i> Pole opcjonalne
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<a href="/notatnik" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left"></i> Powrót do listy
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">
|
||||||
|
<i class="fas fa-save"></i> Zapisz notatkę
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php if (isset($note)): ?>
|
||||||
|
<div class="card-footer text-muted small">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<i class="fas fa-calendar-plus"></i> Utworzono:
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($note['created_at']);
|
||||||
|
echo $date->format('d.m.Y H:i');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<i class="fas fa-calendar-check"></i> Zaktualizowano:
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($note['updated_at']);
|
||||||
|
echo $date->format('d.m.Y H:i');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
$extraScript = '<script>
|
||||||
|
const textarea = document.getElementById("content");
|
||||||
|
textarea.addEventListener("input", function() {
|
||||||
|
this.style.height = "auto";
|
||||||
|
this.style.height = this.scrollHeight + "px";
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
require __DIR__ . '/../layout.php';
|
||||||
|
?>
|
||||||
170
views/notes/index.php
Normal file
170
views/notes/index.php
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = 'Notatnik - System 2FA';
|
||||||
|
$bodyClass = 'bg-light';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">'
|
||||||
|
. '<link rel="stylesheet" href="/public/css/notes.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/panel">
|
||||||
|
<i class="fas fa-shield-alt"></i> System 2FA
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/pulpit">
|
||||||
|
<i class="fas fa-home"></i> Pulpit
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/notatnik">
|
||||||
|
<i class="fas fa-sticky-note"></i> Notatnik
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/kalendarz">
|
||||||
|
<i class="fas fa-calendar-alt"></i> Kalendarz
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<span class="nav-link text-white">
|
||||||
|
<i class="fas fa-user"></i> <?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/wyloguj-sie">
|
||||||
|
<i class="fas fa-sign-out-alt"></i> Wyloguj
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<?php if (isset($_SESSION['success'])): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-check-circle"></i> <?php echo htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h2><i class="fas fa-sticky-note text-primary"></i> Moje Notatki</h2>
|
||||||
|
<a href="/notatka/nowa" class="btn btn-primary">
|
||||||
|
<i class="fas fa-plus"></i> Nowa notatka
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($notes)): ?>
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="fas fa-inbox fa-5x text-muted mb-3"></i>
|
||||||
|
<h4 class="text-muted">Brak notatek</h4>
|
||||||
|
<p class="text-muted">Stwórz swoją pierwszą notatkę, aby zacząć!</p>
|
||||||
|
<a href="/notatka/nowa" class="btn btn-primary btn-lg mt-3">
|
||||||
|
<i class="fas fa-plus"></i> Dodaj pierwszą notatkę
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach ($notes as $note): ?>
|
||||||
|
<div class="col-md-6 col-lg-4 mb-4">
|
||||||
|
<div class="card note-card h-100 shadow-sm">
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h5 class="card-title">
|
||||||
|
<i class="fas fa-file-alt text-primary"></i>
|
||||||
|
<?php echo htmlspecialchars($note['title']); ?>
|
||||||
|
</h5>
|
||||||
|
<p class="card-text flex-grow-1 note-content">
|
||||||
|
<?php
|
||||||
|
$content = htmlspecialchars($note['content'] ?? '');
|
||||||
|
echo nl2br(mb_substr($content, 0, 150)) . (mb_strlen($content) > 150 ? '...' : '');
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<div class="note-meta text-muted small mb-3">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($note['updated_at']);
|
||||||
|
echo $date->format('d.m.Y H:i');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<a href="/notatka/edytuj?id=<?php echo $note['id']; ?>"
|
||||||
|
class="btn btn-sm btn-outline-primary">
|
||||||
|
<i class="fas fa-edit"></i> Edytuj
|
||||||
|
</a>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-danger"
|
||||||
|
onclick="confirmDelete(<?php echo $note['id']; ?>, '<?php echo htmlspecialchars(addslashes($note['title'])); ?>')">
|
||||||
|
<i class="fas fa-trash"></i> Usuń
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 text-center text-muted">
|
||||||
|
<p>Łącznie notatek: <strong><?php echo $notesCount; ?></strong></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal potwierdzenia usunięcia -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-danger text-white">
|
||||||
|
<h5 class="modal-title" id="deleteModalLabel">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i> Potwierdź usunięcie
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Czy na pewno chcesz usunąć notatkę:</p>
|
||||||
|
<p class="fw-bold" id="noteTitle"></p>
|
||||||
|
<p class="text-muted small">Tej operacji nie można cofnąć.</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||||
|
<i class="fas fa-times"></i> Anuluj
|
||||||
|
</button>
|
||||||
|
<form method="POST" action="/notatka/usun" id="deleteForm">
|
||||||
|
<input type="hidden" name="note_id" id="deleteNoteId">
|
||||||
|
<button type="submit" class="btn btn-danger">
|
||||||
|
<i class="fas fa-trash"></i> Usuń notatkę
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
$extraScript = '<script>
|
||||||
|
function confirmDelete(noteId, noteTitle) {
|
||||||
|
document.getElementById("deleteNoteId").value = noteId;
|
||||||
|
document.getElementById("noteTitle").textContent = noteTitle;
|
||||||
|
|
||||||
|
const deleteModal = new bootstrap.Modal(document.getElementById("deleteModal"));
|
||||||
|
deleteModal.show();
|
||||||
|
}
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
require __DIR__ . '/../layout.php';
|
||||||
|
?>
|
||||||
78
views/verify.php
Normal file
78
views/verify.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
$pageTitle = 'Weryfikacja 2FA - System 2FA';
|
||||||
|
$bodyClass = 'bg-gradient-primary';
|
||||||
|
$extraHead = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">';
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center align-items-center min-vh-100">
|
||||||
|
<div class="col-xl-10 col-lg-12 col-md-9">
|
||||||
|
<div class="card o-hidden border-0 shadow-lg my-5">
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 d-none d-lg-block bg-verify-image"></div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="p-5">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="h4 text-gray-900 mb-2">Weryfikacja dwuskładnikowa</h1>
|
||||||
|
<p class="mb-4 text-muted">Wprowadź kod weryfikacyjny</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['error'])): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<?php
|
||||||
|
echo htmlspecialchars($_SESSION['error']);
|
||||||
|
unset($_SESSION['error']);
|
||||||
|
?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<strong>Tryb testowy:</strong> Kod weryfikacyjny znajduje się w konsoli przeglądarki (F12).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="user" method="POST" action="/weryfikuj-kod">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<input type="text" class="form-control form-control-user text-center"
|
||||||
|
id="code" name="code"
|
||||||
|
placeholder="000000"
|
||||||
|
maxlength="6"
|
||||||
|
pattern="[0-9]{6}"
|
||||||
|
required autofocus>
|
||||||
|
<small class="form-text text-muted">Wprowadź 6-cyfrowy kod</small>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-user btn-block w-100">
|
||||||
|
Zweryfikuj
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="/logowanie" class="small">Powrót do logowania</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
$extraScript = '<script>
|
||||||
|
// Wyświetlanie kodu weryfikacyjnego w konsoli (tylko dla testów)
|
||||||
|
' . (isset($_SESSION['test_code']) ? "console.log('%c🔐 KOD WERYFIKACYJNY: " . $_SESSION['test_code'] . "', 'background: #222; color: #bada55; font-size: 20px; padding: 10px;');\n console.log('W produkcji kod zostałby wysłany emailem.');" : '') . '
|
||||||
|
|
||||||
|
// Auto-focus i formatowanie pola kodu
|
||||||
|
document.getElementById("code").addEventListener("input", function() {
|
||||||
|
this.value = this.value.replace(/[^0-9]/g, "");
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
require __DIR__ . '/layout.php';
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user