initial code commit
This commit is contained in:
21
Model/Bill.php
Normal file
21
Model/Bill.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Model;
|
||||
|
||||
class Bill {
|
||||
public int $id;
|
||||
public string $dueDate; // Format: 'YYYY-MM-DD'
|
||||
public float $amount;
|
||||
public string $description;
|
||||
public bool $isPaid;
|
||||
|
||||
public function __construct(int $id, string $dueDate, float $amount, string $description, bool $isPaid) {
|
||||
$this->id = $id;
|
||||
$this->dueDate = $dueDate;
|
||||
$this->amount = $amount;
|
||||
$this->description = $description;
|
||||
$this->isPaid = $isPaid;
|
||||
}
|
||||
|
||||
// Additional methods as needed, such as getters and setters
|
||||
}
|
||||
20
Model/User.php
Normal file
20
Model/User.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Model;
|
||||
|
||||
class User {
|
||||
public int $id;
|
||||
public string $username;
|
||||
private string $passwordHash;
|
||||
|
||||
public function __construct(int $id, string $username, string $passwordHash) {
|
||||
$this->id = $id;
|
||||
$this->username = $username;
|
||||
$this->passwordHash = $passwordHash;
|
||||
}
|
||||
|
||||
// Add methods for password verification, etc.
|
||||
public function verifyPassword(string $password): bool {
|
||||
return password_verify($password, $this->passwordHash);
|
||||
}
|
||||
}
|
||||
69
Service/BillManager.php
Normal file
69
Service/BillManager.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Service;
|
||||
|
||||
use Hpz937\BillReminder\Database\DatabaseInterface;
|
||||
use Hpz937\BillReminder\Model\Bill;
|
||||
|
||||
class BillManager {
|
||||
private DatabaseInterface $db;
|
||||
|
||||
public function __construct(DatabaseInterface $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function createBill(Bill $bill): bool {
|
||||
$sql = "INSERT INTO bills (dueDate, amount, description, isPaid) VALUES (:dueDate, :amount, :description, :isPaid)";
|
||||
$this->db->prepare($sql);
|
||||
$this->db->execute([
|
||||
':dueDate' => $bill->dueDate,
|
||||
':amount' => $bill->amount,
|
||||
':description' => $bill->description,
|
||||
':isPaid' => $bill->isPaid ? 1 : 0,
|
||||
]);
|
||||
return $this->db->lastInsertId() > 0;
|
||||
}
|
||||
|
||||
public function getBill(int $id): ?Bill {
|
||||
$sql = "SELECT * FROM bills WHERE id = :id";
|
||||
$this->db->prepare($sql);
|
||||
$this->db->execute([':id' => $id]);
|
||||
$result = $this->db->fetch();
|
||||
if ($result) {
|
||||
return new Bill($result['id'], $result['dueDate'], $result['amount'], $result['description'], $result['isPaid'] == 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function updateBill(Bill $bill): bool {
|
||||
$sql = "UPDATE bills SET dueDate = :dueDate, amount = :amount, description = :description, isPaid = :isPaid WHERE id = :id";
|
||||
$this->db->prepare($sql);
|
||||
return $this->db->execute([
|
||||
':id' => $bill->id,
|
||||
':dueDate' => $bill->dueDate,
|
||||
':amount' => $bill->amount,
|
||||
':description' => $bill->description,
|
||||
':isPaid' => $bill->isPaid ? 1 : 0,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function deleteBill(int $id): bool {
|
||||
$sql = "DELETE FROM bills WHERE id = :id";
|
||||
$this->db->prepare($sql);
|
||||
return $this->db->execute([':id' => $id]);
|
||||
}
|
||||
|
||||
|
||||
public function listBills(): array {
|
||||
$sql = "SELECT * FROM bills";
|
||||
$this->db->prepare($sql);
|
||||
$this->db->execute();
|
||||
$bills = [];
|
||||
while ($row = $this->db->fetch()) {
|
||||
$bills[] = new Bill($row['id'], $row['dueDate'], $row['amount'], $row['description'], $row['isPaid'] == 1);
|
||||
}
|
||||
return $bills;
|
||||
}
|
||||
}
|
||||
42
Service/UserManager.php
Normal file
42
Service/UserManager.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Hpz937\BillReminder\Service;
|
||||
|
||||
use Hpz937\BillReminder\Database\DatabaseInterface;
|
||||
use Hpz937\BillReminder\Model\User;
|
||||
|
||||
class UserManager {
|
||||
private DatabaseInterface $db;
|
||||
|
||||
public function __construct(DatabaseInterface $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function register(string $username, string $password): bool {
|
||||
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$sql = "INSERT INTO users (username, passwordHash) VALUES (:username, :passwordHash)";
|
||||
$this->db->prepare($sql);
|
||||
return $this->db->execute([
|
||||
':username' => $username,
|
||||
':passwordHash' => $passwordHash,
|
||||
]);
|
||||
}
|
||||
|
||||
public function login(string $username, string $password): ?User {
|
||||
$sql = "SELECT * FROM users WHERE username = :username";
|
||||
$this->db->prepare($sql);
|
||||
$this->db->execute([':username' => $username]);
|
||||
$result = $this->db->fetch();
|
||||
|
||||
if ($result && (new User($result['id'], $result['username'], $result['passwordHash']))->verifyPassword($password)) {
|
||||
// Start or regenerate the session
|
||||
session_regenerate_id();
|
||||
$_SESSION['user_id'] = $result['id'];
|
||||
return new User($result['id'], $result['username'], $result['passwordHash']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Additional methods as needed, e.g., for password change, user details update, etc.
|
||||
}
|
||||
32
View/PageRenderer.php
Normal file
32
View/PageRenderer.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Hpz937\BillReminder\View;
|
||||
|
||||
class PageRenderer {
|
||||
public static function renderHeader($title = 'Bill Reminder') {
|
||||
echo <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>$title</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<!-- Additional stylesheet links or other <head> elements -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
HTML;
|
||||
}
|
||||
|
||||
public static function renderFooter() {
|
||||
echo <<<HTML
|
||||
</div> <!-- Closing container div -->
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,12 @@
|
||||
"email": "hpz937+code@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {}
|
||||
"repositories": [{
|
||||
"type": "composer",
|
||||
"url": "https://git.hpz.pw/api/packages/hpz937/composer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"hpz937/restclient": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
27
index.php
Normal file
27
index.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
// index.php
|
||||
session_start();
|
||||
|
||||
require 'vendor/autoload.php'; // Assuming you're using Composer
|
||||
|
||||
// Define a simple router
|
||||
$request = $_SERVER['REQUEST_URI'];
|
||||
|
||||
switch ($request) {
|
||||
case '/' :
|
||||
case '' :
|
||||
require __DIR__ . '/views/home.php';
|
||||
break;
|
||||
case '/login' :
|
||||
require __DIR__ . '/views/login.php';
|
||||
break;
|
||||
case '/register' :
|
||||
require __DIR__ . '/views/register.php';
|
||||
break;
|
||||
// Add more routes as needed
|
||||
default:
|
||||
http_response_code(404);
|
||||
require __DIR__ . '/views/404.php';
|
||||
break;
|
||||
}
|
||||
14
setup.php
Normal file
14
setup.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
use Hpz937\BillReminder\Database\DatabaseSetup;
|
||||
use Hpz937\BillReminder\Database\SQLite3Database;
|
||||
|
||||
// Assuming you have a SQLite database file path
|
||||
$databaseFile = 'path/to/your/database/file.sqlite';
|
||||
|
||||
$db = new SQLite3Database($databaseFile);
|
||||
$db->connect();
|
||||
|
||||
$databaseSetup = new DatabaseSetup($db);
|
||||
$databaseSetup->setup();
|
||||
|
||||
// Continue with the rest of your application setup
|
||||
14
src/Database/DatabaseInterface.php
Normal file
14
src/Database/DatabaseInterface.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Database;
|
||||
|
||||
interface DatabaseInterface {
|
||||
public function connect();
|
||||
public function query(string $query);
|
||||
public function prepare(string $sql);
|
||||
public function execute(array $parameters = []);
|
||||
public function fetch();
|
||||
public function fetchAll();
|
||||
public function close();
|
||||
}
|
||||
|
||||
44
src/Database/DatabaseSetup.php
Normal file
44
src/Database/DatabaseSetup.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Database;
|
||||
|
||||
class DatabaseSetup {
|
||||
private DatabaseInterface $db;
|
||||
|
||||
public function __construct(DatabaseInterface $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function setup(): void {
|
||||
$this->createBillsTable();
|
||||
$this->createUsersTable();
|
||||
}
|
||||
|
||||
private function createBillsTable(): void {
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS bills (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dueDate TEXT NOT NULL,
|
||||
amount REAL NOT NULL,
|
||||
description TEXT,
|
||||
isPaid INTEGER NOT NULL
|
||||
);
|
||||
SQL;
|
||||
|
||||
$this->db->query($sql);
|
||||
}
|
||||
|
||||
private function createUsersTable(): void {
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
passwordHash TEXT NOT NULL
|
||||
);
|
||||
SQL;
|
||||
|
||||
$this->db->query($sql);
|
||||
}
|
||||
|
||||
// Additional methods to setup other tables as needed
|
||||
}
|
||||
83
src/Database/SQLite3Database.php
Normal file
83
src/Database/SQLite3Database.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Database;
|
||||
|
||||
use SQLite3;
|
||||
use Exception;
|
||||
|
||||
class SQLite3Database implements DatabaseInterface {
|
||||
/**
|
||||
* @var SQLite3
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var string The path to the SQLite database file.
|
||||
*/
|
||||
private $databaseFile;
|
||||
|
||||
/**
|
||||
* @var \SQLite3Stmt
|
||||
*/
|
||||
private $statement;
|
||||
|
||||
public function __construct(string $databaseFile) {
|
||||
$this->databaseFile = $databaseFile;
|
||||
}
|
||||
|
||||
public function connect() {
|
||||
try {
|
||||
$this->connection = new SQLite3($this->databaseFile);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Unable to connect to SQLite database: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function query(string $query) {
|
||||
if (!$this->connection) {
|
||||
throw new Exception("No database connection");
|
||||
}
|
||||
return $this->connection->query($query);
|
||||
}
|
||||
|
||||
public function prepare(string $sql) {
|
||||
if (!$this->connection) {
|
||||
throw new Exception("No database connection");
|
||||
}
|
||||
$this->statement = $this->connection->prepare($sql);
|
||||
if (!$this->statement) {
|
||||
throw new Exception("Failed to prepare SQL statement");
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(array $parameters = []) {
|
||||
if (!$this->statement) {
|
||||
throw new Exception("No prepared statement");
|
||||
}
|
||||
foreach ($parameters as $param => $value) {
|
||||
$this->statement->bindValue($param, $value);
|
||||
}
|
||||
return $this->statement->execute();
|
||||
}
|
||||
|
||||
public function fetch() {
|
||||
if (!$this->statement) {
|
||||
throw new Exception("No prepared statement");
|
||||
}
|
||||
return $this->statement->fetchArray(SQLITE3_ASSOC);
|
||||
}
|
||||
|
||||
public function fetchAll() {
|
||||
$data = [];
|
||||
while ($row = $this->fetch()) {
|
||||
$data[] = $row;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function close() {
|
||||
if ($this->connection) {
|
||||
$this->connection->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/Notification/NftyNotification.php
Normal file
44
src/Notification/NftyNotification.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Notification;
|
||||
|
||||
use Hpz937\Restclient\RestClient;
|
||||
use Exception;
|
||||
|
||||
class NftyNotification implements NotificationInterface {
|
||||
private $restClient;
|
||||
|
||||
public function __construct(RestClient $restClient) {
|
||||
$this->restClient = $restClient;
|
||||
}
|
||||
|
||||
public function send(string $to, string $message, array $options = []): bool {
|
||||
try {
|
||||
// ntfy.sh uses simple text/plain content type for the message body
|
||||
$headers = ["Content-Type: text/plain"];
|
||||
if (isset($options['title'])) {
|
||||
$headers[] = "Title: {$options['title']}";
|
||||
}
|
||||
if (isset($options['priority'])) {
|
||||
$headers[] = "Priority: {$options['priority']}";
|
||||
}
|
||||
if (isset($options['tags'])) {
|
||||
$headers[] = "Tags: {$options['tags']}";
|
||||
}
|
||||
|
||||
$this->restClient->setHeaders($headers);
|
||||
|
||||
// Use the RestClient's custom request method to mimic the file_get_contents approach
|
||||
$this->restClient->post("https://ntfy.sh/{$this->to}", $message);
|
||||
|
||||
// Since ntfy.sh returns a simple response, we might not need to decode JSON
|
||||
$response = $this->restClient->getResponse();
|
||||
|
||||
// Check the response or HTTP status code as needed to determine success
|
||||
return true; // Assuming the request was successful, adjust based on actual response handling
|
||||
} catch (Exception $e) {
|
||||
// Error handling logic
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/Notification/NotificationInterface.php
Normal file
15
src/Notification/NotificationInterface.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Hpz937\BillReminder\Notification;
|
||||
|
||||
interface NotificationInterface {
|
||||
/**
|
||||
* Sends a notification with optional parameters such as title and priority.
|
||||
*
|
||||
* @param string $to The recipient of the notification.
|
||||
* @param string $message The message content of the notification.
|
||||
* @param array $options Optional parameters for the notification, such as title, priority, and tags.
|
||||
* @return bool Returns true on success, false on failure.
|
||||
*/
|
||||
public function send(string $to, string $message, array $options = []): bool;
|
||||
}
|
||||
0
views/404.php
Normal file
0
views/404.php
Normal file
50
views/dashboard.php
Normal file
50
views/dashboard.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Hpz937\BillReminder\View\PageRenderer;
|
||||
|
||||
session_start();
|
||||
|
||||
// Redirect to login if not logged in
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: /login');
|
||||
exit;
|
||||
}
|
||||
|
||||
PageRenderer::renderHeader('My Bills Dashboard');
|
||||
|
||||
// Fetch user's bills from the database
|
||||
// $bills = fetchBillsForUser($_SESSION['user_id']);
|
||||
|
||||
?>
|
||||
|
||||
<h2>My Bills</h2>
|
||||
<a href="/add-bill" class="btn btn-success">Add New Bill</a>
|
||||
<div class="mt-3">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Due Date</th>
|
||||
<th>Amount</th>
|
||||
<th>Description</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($bills as $bill): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($bill->dueDate); ?></td>
|
||||
<td><?php echo htmlspecialchars($bill->amount); ?></td>
|
||||
<td><?php echo htmlspecialchars($bill->description); ?></td>
|
||||
<td><?php echo $bill->isPaid ? 'Paid' : 'Pending'; ?></td>
|
||||
<td>
|
||||
<a href="/edit-bill/<?php echo $bill->id; ?>" class="btn btn-primary btn-sm">Edit</a>
|
||||
<a href="/delete-bill/<?php echo $bill->id; ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php PageRenderer::renderFooter(); ?>
|
||||
30
views/home.php
Normal file
30
views/home.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Hpz937\BillReminder\View\PageRenderer;
|
||||
|
||||
session_start();
|
||||
|
||||
PageRenderer::renderHeader('Bill Reminder Home');
|
||||
|
||||
// views/home.php
|
||||
|
||||
session_start(); // Ensure session is started to access session variables
|
||||
|
||||
// Check if user is logged in
|
||||
$loggedIn = isset($_SESSION['user_id']);
|
||||
|
||||
?>
|
||||
|
||||
<h1>Welcome to Bill Reminder</h1>
|
||||
<p>Manage your bills efficiently and never miss a payment.</p>
|
||||
|
||||
<?php if ($loggedIn): ?>
|
||||
<p>Hello, <?php echo htmlspecialchars($_SESSION['username']); ?>!</p>
|
||||
<a href="/dashboard" class="btn btn-primary">Go to Dashboard</a>
|
||||
<a href="/logout" class="btn btn-secondary">Logout</a>
|
||||
<?php else: ?>
|
||||
<a href="/login" class="btn btn-primary">Login</a>
|
||||
<a href="/register" class="btn btn-secondary">Register</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php PageRenderer::renderFooter(); ?>
|
||||
35
views/login.php
Normal file
35
views/login.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// views/login.php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Handle login logic here
|
||||
// Use UserManager to verify credentials
|
||||
// Redirect to home page or dashboard on success
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Login</title>
|
||||
<link rel="stylesheet" href="path/to/bootstrap/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h2>Login</h2>
|
||||
<form method="POST" action="/login">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
35
views/register.php
Normal file
35
views/register.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// views/register.php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Handle registration logic here
|
||||
// Use UserManager to create a new user
|
||||
// Redirect to login page on success
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Register</title>
|
||||
<link rel="stylesheet" href="path/to/bootstrap/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h2>Register</h2>
|
||||
<form method="POST" action="/register">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user