Robert 3 hours ago
parent b1d1a09254
commit c17f89bc96
  1. 6
      protected/src/Classes/Logic/IndexAuthContainer.php
  2. 86
      protected/src/Classes/Logic/IndexLogin.php
  3. 51
      protected/src/Classes/Models/HomeLoginModel.php
  4. 4
      protected/src/Configs/on_HTML.php
  5. 45
      protected/src/Controllers/App/HomeController.php
  6. 6
      protected/src/Templates/main.php
  7. 2
      protected/src/Views/Common/App/Home/Index.php
  8. 28
      protected/src/Views/Common/App/Home/Login.php
  9. 152
      protected/src/Views/Common/App/Reg/Form.php
  10. 31
      public/assets/css/breadcrumbs.css
  11. 39
      public/assets/css/buttons.css
  12. 88
      public/assets/css/index.css
  13. 24
      public/assets/css/registration.css
  14. 43
      public/assets/js/registration.js
  15. 12
      public/index.php

@ -16,7 +16,7 @@ namespace Project\Classes\Logic;
*/ */
class IndexAuthContainer class IndexAuthContainer
{ {
public static function Logins(): string public static function Logins(object $local): void
{ {
$auth = '<div class="auth-container">'; $auth = '<div class="auth-container">';
$loggedin = $_SESSION['email'] ?? false; $loggedin = $_SESSION['email'] ?? false;
@ -27,6 +27,8 @@ class IndexAuthContainer
$auth .= '<a href="/App/Home/Logout.html" class="btn login-btn">Logout</a>'; $auth .= '<a href="/App/Home/Logout.html" class="btn login-btn">Logout</a>';
} }
$auth .= '</div>'; $auth .= '</div>';
return $auth;
$local->view->set("Auth", $auth);
$local->html->addCss("css/buttons.css");
} }
} }

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright (c) 2026, Robert Strutts
* @license MIT
*/
namespace Project\Classes\Logic;
use \IOcornerstone\Framework\Uuids\UuidV7;
use \IOcornerstone\Framework\Configure;
use \Project\Classes\Models\HomeLoginModel;
/**
* Description of IndexLogin
*
* @author Robert Strutts
*/
class IndexLogin
{
private static function allowLogin(string $dbHash): bool
{
$uuid = $_POST['token'] ?? false;
$suuid = $_SESSION['token'] ?? false;
$pwd = $_POST['pwd'] ?? false;
if ($pwd === false || empty($pwd) || $uuid === false || $suuid === false) {
return false;
}
$ts = UuidV7::getTimestampFromUuidV7($uuid);
$ts2 = UuidV7::getTimestampFromUuidV7($suuid);
$d = UuidV7::getFormatedDate($ts);
$d2 = UuidV7::getFormatedDate($ts2);
$diffInSeconds = abs(strtotime($d) - strtotime($d2));
if ($diffInSeconds > 600) { // 600 seconds = 10 minutes
return false;
}
// $dbHash = password_hash($pwd, PASSWORD_ARGON2ID);
$allow = password_verify($pwd, $dbHash);
if (!$allow) {
return false;
}
return true;
}
public static function doLogin(): bool
{
$login = $_POST['login'] ?? false;
if ($login === false) {
$token = UuidV7::generateUuidV7();
$_SESSION['token'] = $token;
return false; // IE Show Login
}
$pdo = Configure::get('db');
$model = new HomeLoginModel($pdo);
$dbHash = $model->getLogin($login);
$allow = self::allowLogin($dbHash);
if ($allow) {
$level = $_SESSION['accessLevel'] ?? 0;
$user = match ($level) {
1 => "User",
2 => "Moderator",
3 => "Admin",
default => "Error",
};
if ($user === "User") {
header("Location: /App/Home/Index.html");
}
if ($user === "Admin") {
header("Location: /Admin/Home/Index.html");
}
}
return $allow;
}
}

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright (c) 2026, Robert Strutts
* @license MIT
*/
namespace Project\Classes\Models;
/**
* Description of HomeLoginModel
* Do Login...
* @author Robert Strutts
*/
class HomeLoginModel
{
public function __construct(private \PDO $pdo)
{
}
public function getLogin(string $emailAddress): string
{
try {
$sqlEmail = "SELECT id, first_name FROM emails WHERE is_active=1 AND email=? LIMIT 1";
$stmtEmail = $this->pdo->prepare($sqlEmail);
$stmtEmail->execute([$emailAddress]);
$email_row = $stmtEmail->fetch(\PDO::FETCH_ASSOC);
$sqlUser = "SELECT pwd, access_level FROM users WHERE email_id=? LIMIT 1";
$stmtUser = $this->pdo->prepare($sqlEmail);
$stmtUser->execute([$emailAddress]);
$user_row = $stmtUser->fetch(\PDO::FETCH_ASSOC);
$accessLevel = $user_row['access_level'] ?? 0;
$_SESSION['accessLevel'] = $accessLevel;
$name = $email_row['first_name'] ?? "";
$_SESSION['first_name'] = $name;
return $user_row['pwd'];
} catch (\PDOException $e) {
echo $e->getMessage();
}
return "";
}
}

@ -12,9 +12,9 @@ use IOcornerstone\Framework\Configure;
Configure::set('html', array( Configure::set('html', array(
'author' => 'Robert Strutts', 'author' => 'Robert Strutts',
'title' => 'StickingToGoal.com', 'title' => 'StickingToGoals.com',
// 'javascript' => ['js/analytics.js'=>'project''], // 'javascript' => ['js/analytics.js'=>'project''],
'css' => ['css/index.css'=>'project'], 'css' => ['css/breadcrumbs.css'=>'project'],
'robots' => 'noindex', 'robots' => 'noindex',
'keywords' => 'goal, setting', 'keywords' => 'goal, setting',
'description' => 'Goals site', 'description' => 'Goals site',

@ -14,6 +14,7 @@ use Project\Classes\{
BaseController, BaseController,
Logic\HomeSearch, Logic\HomeSearch,
Logic\IndexAuthContainer, Logic\IndexAuthContainer,
Logic\IndexLogin,
Models\HomeFetchModel Models\HomeFetchModel
}; };
use IOcornerstone\Framework\{ use IOcornerstone\Framework\{
@ -35,6 +36,9 @@ class HomeController extends BaseController
{ {
Security::initSessions(); Security::initSessions();
$this->html->setActiveCrumb("Main Page");
$this->html->addCss("css/index.css");
$this->html->addToJavascript("function filterTag(tag){ \r\n window.location='?tag='+encodeURIComponent(tag); \r\n }"); $this->html->addToJavascript("function filterTag(tag){ \r\n window.location='?tag='+encodeURIComponent(tag); \r\n }");
$pdo = Configure::get('db'); $pdo = Configure::get('db');
@ -50,8 +54,7 @@ class HomeController extends BaseController
$goals = $model->GetGoals($inputs); $goals = $model->GetGoals($inputs);
$this->view->set("Goals", $goals); $this->view->set("Goals", $goals);
$auth = IndexAuthContainer::Logins(); IndexAuthContainer::Logins($this);
$this->view->set("Auth", $auth);
$uid = HomeSearch::MyUUID(); $uid = HomeSearch::MyUUID();
$this->view->set("Uid", $uid); $this->view->set("Uid", $uid);
@ -66,22 +69,51 @@ class HomeController extends BaseController
public function Register(): ResponseInterface public function Register(): ResponseInterface
{ {
$this->html->setActiveCrumb("Registion");
$this->html->setBreadcrumbs(['/App/Home/Index.html'=>"Main Page"]);
$this->html->addCss("css/registration.css");
$this->html->addJS("js/registration.js");
$this->html->setTitleAndHeader("Register");
$this->view->set('html', $this->html);
$this->view->setPhpTemplate('main');
$this->view->setView("App/Reg/Form"); $this->view->setView("App/Reg/Form");
$this->view->setView("App/Home/TOS"); $this->view->setView("App/Home/TOS");
$myView = $this->view->fetch($this); $myView = $this->view->fetch($this);
$myView .= "</body>" . PHP_EOL . "</html>"; $myView .= "</body>" . PHP_EOL . "</html>";
return $this->returnResponse($myView); return $this->returnResponse($myView);
} }
public function Login(): ResponseInterface public function Login(): ResponseInterface
{ {
$this->html->setActiveCrumb("LogIn");
$this->html->setBreadcrumbs(['/App/Home/Index.html'=>"Main Page"]);
Security::initSessions(); Security::initSessions();
return $this->returnResponse(""); $login = IndexLogin::doLogin();
if ($login === false) {
$this->view->set('html', $this->html);
$this->view->setPhpTemplate('main');
$this->view->set("token", $_SESSION['token']);
$this->view->setView("App/Home/Login");
$myView = $this->view->fetch($this);
} else {
$myView = "";
}
return $this->returnResponse($myView);
} }
public function Logout(): ResponseInterface public function Logout(): ResponseInterface
{ {
$this->html->setActiveCrumb("LogOut");
$this->html->setBreadcrumbs(['/App/Home/Index.html'=>"Main Page"]);
$this->html->setTitleAndHeader("Logged Out");
Security::initSessions(); Security::initSessions();
$_SESSION = []; $_SESSION = [];
@ -89,8 +121,11 @@ class HomeController extends BaseController
session_destroy(); session_destroy();
$html = '<html><head><title>Logged Out</title></head><body><h1>Logged Out!</h1></body></html>'; $this->view->set('html', $this->html);
return $this->returnResponse($html); $this->view->setPhpTemplate('main');
$myView = $this->view->fetch($this);
return $this->returnResponse($myView);
} }
} }

@ -12,7 +12,7 @@ declare(strict_types=1);
<html lang="en-US"> <html lang="en-US">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<base href="<?= PROJECT_ASSETS_BASE_REF; ?>/"> <base href="<?= PROJECT_BASE_REF; ?>/">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="<?= $html->getKeywords(); ?>"> <meta name="keywords" content="<?= $html->getKeywords(); ?>">
<meta name="description" content="<?= $html->getDescription(); ?>"> <meta name="description" content="<?= $html->getDescription(); ?>">
@ -35,7 +35,9 @@ declare(strict_types=1);
</head> </head>
<body <?= $html->getBody(); ?>> <body <?= $html->getBody(); ?>>
<?= $local->pageOutput ?> <?= $html->getBreadcrumbsAuto(); ?>
<?= $local->pageOutput; ?>
<?= $html->getFooter(); ?> <?= $html->getFooter(); ?>

@ -17,7 +17,7 @@ function end_of_the_line(): void
<?= $Auth ?> <?= $Auth ?>
<div class="container"> <div class="container">
<h1>🎯 StickingToGoals.com</h1> <h1>🎯<?= $html->getTitle(); ?></h1>
<form method="GET"> <form method="GET">
<input name="search" placeholder="Search"> <input name="search" placeholder="Search">
<button>Search</button> <button>Search</button>

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* @author Robert Strutts
* @copyright (c) 2026, Robert Strutts
* @license MIT
*/
?>
<form method="POST">
<input type="hidden" name="token" value="<?= $token ?>">
<center>
<fieldset style="width: 300px;"><legend><?= $html->getTitle() ?> - Log In</legend>
<table border="0">
<tr>
<th><label for="login">Email:</label></th>
<td><input type="text" name="login"></td>
</tr><tr>
<th><label for="pwd">Password:</label></th>
<td><input type="password" name="pwd"></td>
</tr>
</table>
<input type="submit" value="Login">
</fieldset>
</center>
</form>

@ -8,119 +8,39 @@ declare(strict_types=1);
*/ */
?> ?>
<!DOCTYPE html> <div class="container">
<html lang="en"> <h2><?= $html->getHeader(); ?></h2>
<head>
<meta charset="UTF-8"> <div id="success" class="hidden mysuccess">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <p>Registration successful! Check your email to verify.</p>
<title>Register</title> <p>This is not automated, so it may take a while. If it'd been a few business days, email share&#64;StickingToGoals.com to let us know you did not get a registration email.</p>
<style> </div>
body {
font-family: Arial; <p class="error" id="errors"></p>
background:#f5f5f5;
} <form id="myForm">
.container { <input type="email" id="email" name="email" placeholder="Email" required size="25" maxlength="250"><br>
width: 230px;
margin: 50px auto; <input type="text" id="first_name" name="first_name" placeholder="First Name" required maxlength="32"><br>
padding: 20px;
background: white; <input type="text" id="last_initial" name="last_initial" placeholder="Last Initial" maxlength="1" required><br>
border-radius: 8px;
} <input type="text" id="last_name" name="last_name" placeholder="Last Name (optional)" maxlength="60"><br>
input, button { <table>
margin: 8px 0; <tr>
padding: 10px; <td><input type="checkbox" id="adult" name="is_adult" required></td>
} <th align="left"><label for="adult">I am 18+ years old</label></th>
.hidden { </tr><tr>
display:none; <td><input type="checkbox" id="anon" name="be_anon"></td>
} <th align="left"><label for="anon">Stay anonymous</label></th>
.error { </tr><tr>
color: red; <td><input type="checkbox" id="sub" name="send_newsletter"></td>
} <th align="left"><label for="sub">Subscribe to newsletter</label></th>
.mysuccess { </tr><tr>
color: green; <td><input type="checkbox" id="tos" name="tos" required></td>
} <th align="left"><label for="tos">Accept Terms of Service</label></th>
</style> </tr>
</head> </table>
<body> <button type="submit" id="reg">Register</button>
</form>
<div class="container"> </div>
<h2>Register</h2>
<div id="success" class="hidden mysuccess">
<p>Registration successful! Check your email to verify.</p>
<p>This is not automated, so it may take a while. If it'd been a few business days, email share&#64;StickingToGoals.com to let us know you did not get a registration email.</p>
</div>
<p class="error" id="errors"></p>
<form id="myForm">
<input type="email" id="email" name="email" placeholder="Email" required size="25" maxlength="250"><br>
<input type="text" id="first_name" name="first_name" placeholder="First Name" required maxlength="32"><br>
<input type="text" id="last_initial" name="last_initial" placeholder="Last Initial" maxlength="1" required><br>
<input type="text" id="last_name" name="last_name" placeholder="Last Name (optional)" maxlength="60"><br>
<table>
<tr>
<td><input type="checkbox" id="adult" name="is_adult" required></td>
<th align="left"><label for="adult">I am 18+ years old</label></th>
</tr><tr>
<td><input type="checkbox" id="anon" name="be_anon"></td>
<th align="left"><label for="anon">Stay anonymous</label></th>
</tr><tr>
<td><input type="checkbox" id="sub" name="send_newsletter"></td>
<th align="left"><label for="sub">Subscribe to newsletter</label></th>
</tr><tr>
<td><input type="checkbox" id="tos" name="tos" required></td>
<th align="left"><label for="tos">Accept Terms of Service</label></th>
</tr>
</table>
<button type="submit" id="reg">Register</button>
</form>
</div>
<script>
document.getElementById("myForm").addEventListener('submit', async function (e) {
e.preventDefault();
const formData = new FormData(e.target);
/* Log all form data
for (let [key, value] of formData.entries()) {
console.log(key + ': ' + value);
}
*/
const formObject = Object.fromEntries(formData.entries());
// console.log('Form object:', formObject);
try {
const response = await fetch('/Data/RegPost/Save.html', {
method: 'POST',
headers: {"Content-Type": "application/json"},
body: JSON.stringify(formObject)
});
const result = await response.json();
// Multiple ways to check success
if (response.status === 200 && result.success === true) {
console.log('Success:', result);
document.getElementById("success").classList.remove("hidden");
document.getElementById("errors").textContent = "";
document.getElementById("reg").disabled = true;
//} else if (result.success) {
// console.log('Success:', result);
// } else if (result.error === false) {
// console.log('Success:', result);
} else {
document.getElementById("errors").textContent = result.errors;
console.error('Failed:', result);
}
} catch (error) {
document.getElementById("errors").textContent = error;
console.error('Error:', error);
}
});
</script>

@ -0,0 +1,31 @@
/* Style the list */
ul.breadcrumb {
padding: 10px 16px;
list-style: none;
background-color: #eee;
}
/* Display list items side by side */
ul.breadcrumb li {
display: inline;
font-size: 18px;
}
/* Add a slash symbol (/) before/behind each list item */
ul.breadcrumb li+li:before {
padding: 8px;
color: black;
content: "/\00a0";
}
/* Add a color to all links inside the list */
ul.breadcrumb li a {
color: #0275d8;
text-decoration: none;
}
/* Add a color on mouse-over */
ul.breadcrumb li a:hover {
color: #01447e;
text-decoration: underline;
}

@ -0,0 +1,39 @@
button{
background:#4CAF50;
color:#fff;
border:none;
padding:10px;
border-radius:6px;
cursor:pointer
}
button:hover{
background:#45a049
}
.btn {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
color: white;
}
.login-btn {
background: #3498db;
}
.login-btn:hover {
background: #2980b9;
transform: translateY(-2px);
}
.register-btn {
background: #2ecc71;
}
.register-btn:hover {
background: #27ae60;
transform: translateY(-2px);
}

@ -1,44 +1,58 @@
body {font-family:Arial;background:#f4f6f9;margin:0} body {
.container {max-width:900px;margin:30px auto;background:#fff;padding:20px;border-radius:10px;box-shadow:0 4px 12px rgba(0,0,0,.1)} font-family:Arial;
input,textarea{width:100%;padding:10px;margin:8px 0;border-radius:6px;border:1px solid #ccc} background:#f4f6f9;
button{background:#4CAF50;color:#fff;border:none;padding:10px;border-radius:6px;cursor:pointer} margin:0
button:hover{background:#45a049}
.goal-item{padding:10px;border-bottom:1px solid #eee}
.advice-box{border:1px solid #ddd;border-radius:8px;padding:15px;margin-top:15px;background:#fafafa}
.vote{background:#eee;padding:5px 10px;border-radius:6px;display:inline-block}
.comment{margin-left:20px;border-left:2px solid #ccc;padding-left:8px;margin-top:5px}
.tag{display:inline-block;background:#3498db;color:#fff;padding:3px 8px;border-radius:5px;margin:2px;font-size:12px;cursor:pointer}
.auth-container {
display: flex;
gap: 20px;
} }
.container {
.btn { max-width:900px;
padding: 12px 24px; margin:30px auto;
font-size: 16px; background:#fff;
border: none; padding:20px;
border-radius: 6px; border-radius:10px;
cursor: pointer; box-shadow:0 4px 12px rgba(0,0,0,.1)
transition: all 0.3s ease;
text-decoration: none;
color: white;
} }
input,textarea{
.login-btn { width:100%;
background: #3498db; padding:10px;
margin:8px 0;
border-radius:6px;
border:1px solid #ccc
} }
.goal-item{
.login-btn:hover { padding:10px;
background: #2980b9; border-bottom:1px solid #eee
transform: translateY(-2px);
} }
.advice-box{
.register-btn { border:1px solid #ddd;
background: #2ecc71; border-radius:8px;
padding:15px;
margin-top:15px;
background:#fafafa
}
.vote{
background:#eee;
padding:5px 10px;
border-radius:6px;
display:inline-block
}
.comment{
margin-left:20px;
border-left:2px solid #ccc;
padding-left:8px;
margin-top:5px
}
.tag{
display:inline-block;
background:#3498db;
color:#fff;
padding:3px 8px;
border-radius:5px;
margin:2px;
font-size:12px;
cursor:pointer
} }
.register-btn:hover { .auth-container {
background: #27ae60; display: flex;
transform: translateY(-2px); gap: 20px;
} }

@ -0,0 +1,24 @@
body {
font-family: Arial;
background:#f5f5f5;
}
.container {
width: 230px;
margin: 50px auto;
padding: 20px;
background: white;
border-radius: 8px;
}
input, button {
margin: 8px 0;
padding: 10px;
}
.hidden {
display:none;
}
.error {
color: red;
}
.mysuccess {
color: green;
}

@ -0,0 +1,43 @@
document.getElementById("myForm").addEventListener('submit', async function (e) {
e.preventDefault();
const formData = new FormData(e.target);
/* Log all form data
for (let [key, value] of formData.entries()) {
console.log(key + ': ' + value);
}
*/
const formObject = Object.fromEntries(formData.entries());
// console.log('Form object:', formObject);
try {
const response = await fetch('/Data/RegPost/Save.html', {
method: 'POST',
headers: {"Content-Type": "application/json"},
body: JSON.stringify(formObject)
});
const result = await response.json();
// Multiple ways to check success
if (response.status === 200 && result.success === true) {
console.log('Success:', result);
document.getElementById("success").classList.remove("hidden");
document.getElementById("errors").textContent = "";
document.getElementById("reg").disabled = true;
//} else if (result.success) {
// console.log('Success:', result);
// } else if (result.error === false) {
// console.log('Success:', result);
} else {
document.getElementById("errors").textContent = result.errors;
console.error('Failed:', result);
}
} catch (error) {
document.getElementById("errors").textContent = error;
console.error('Error:', error);
}
});

@ -3,18 +3,6 @@
declare(strict_types=1); declare(strict_types=1);
define("BaseDir", dirname(__DIR__)); // Project DIR define("BaseDir", dirname(__DIR__)); // Project DIR
define("PROJECT_ASSETS_DIR", BaseDir . DIRECTORY_SEPARATOR . "public" . DIRECTORY_SEPARATOR . "assets" . DIRECTORY_SEPARATOR);
$server_port = $_SERVER['SERVER_PORT'] ?? 80;
$secure_port_on = $_SERVER['HTTPS'] ?? "off";
$use_secure = ($server_port == "443" || $secure_port_on == "on");
$protocol = ($use_secure) ? "https://" : "http://";
$domain_name = $_SERVER['HTTP_HOST'] ?? "";
define("HTTP_PROT", $protocol);
define("PROJECT_ASSETS_BASE_REF", $protocol . $domain_name ."/assets");
define("ASSETS_DIR", PROJECT_ASSETS_DIR);
define('ASSETS_BASE_REF', $protocol . $domain_name . "/assets");
const IO_CORNERSTONE_PROJECT = BaseDir . DIRECTORY_SEPARATOR . "protected". DIRECTORY_SEPARATOR. "src" . DIRECTORY_SEPARATOR; const IO_CORNERSTONE_PROJECT = BaseDir . DIRECTORY_SEPARATOR . "protected". DIRECTORY_SEPARATOR. "src" . DIRECTORY_SEPARATOR;

Loading…
Cancel
Save