-
-
-
= Util::escape_html(Util::relative_time($tick['timestamp'])) ?>= Util::linkify(Util::escape_html($tick['tick'])) ?> -
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..c57d4fa
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,49 @@
+# Example Apache VirtualHost
+# for serving tkr as a subdirectory path
+# on shared hosting via .htaccess
+#
+# e.g. http://www.my-domain.com/tkr
+#
+# This should work without modification if you extract the app
+# to /tkr from your web document root
+
+# Enable mod_rewrite
+RewriteEngine On
+
+# Security headers
+Header always set X-Frame-Options "SAMEORIGIN"
+Header always set X-XSS-Protection "1; mode=block"
+Header always set X-Content-Type-Options "nosniff"
+
+# Directory index
+DirectoryIndex public/index.php
+
+# Security: Block direct access to .php files (except through rewrites)
+RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
+RewriteRule ^.*$ - [R=404,L]
+
+# Security: Block access to sensitive directories
+RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
+
+# Security: Block access to hidden files
+RewriteRule ^\..*$ - [F,L]
+
+# Cache CSS files for 1 hour
+
" . htmlspecialchars($setupError['message']) . "
"; + echo "" . Util::escape_html($setupError['message']) . "
"; exit; case 'table_contents': // Recoverable error. diff --git a/examples/.htaccess b/examples/.htaccess new file mode 100644 index 0000000..f86ed55 --- /dev/null +++ b/examples/.htaccess @@ -0,0 +1,6 @@ +# Deny all access to this directory +Require all denied + +# Fallback for Apache 2.2 +Order deny,allow +Deny from all \ No newline at end of file diff --git a/public/css/tkr.css b/public/css/tkr.css index 76da718..9a81245 100644 --- a/public/css/tkr.css +++ b/public/css/tkr.css @@ -95,7 +95,7 @@ fieldset.emoji-group { } h1.site-description { - font-size: 1.3em; + font-size: 1.5em; } .delete-emoji-fieldset .fieldset-items { @@ -310,19 +310,85 @@ label.description { } .home-sidebar{ + padding-top: 1em; padding-bottom: 1em; } .site-description { font-size: 1.2rem; color: var(--color-text-dark); - margin-bottom: 0.5rem; + margin-bottom: 1.2rem; } -.profile-row { +.profile-data { + display: grid; + gap: 1rem; + margin: 0; + margin-bottom: 1rem; +} + +/* Description list: description */ +.profile-data dd { + margin: 0; +} + +/* Description list: term */ +/* Hidden from visual display - screen reader only class */ +.profile-data dt { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* + Left-justify the greeting text, + right-justify the Change Mood link +*/ + +/* greeting text */ +.profile-greeting { display: flex; - width: 100%; - gap: 0.5em; + justify-content: space-between; + align-items: center; + gap: 0.5rem; +} + +/* add a small gap between the greeting and the mood emoji */ +.greeting-content { + display: flex; + align-items: baseline; + gap: 0.4em; +} + +/* define the profile "greeting" style */ +.greeting-text { + font-weight: 600; + font-size: 1.1em; + color: var(--color-text-primary); +} + +/* Adjust emoji positioning */ +.greeting-emoji { + vertical-align: middle; +} + +/* Style the Change Mood link */ +.change-mood { + font-size: 0.9em; + white-space: nowrap; +} + +/* define the profile "about" style */ +.profile-about { + font-style: italic; + font-size: 0.95em; + color: var(--color-text-muted); } .tick-form { @@ -332,20 +398,13 @@ label.description { gap: 0.5em; } -.mood-bar { - display: flex; - width: 100%; - justify-content: space-between; - align-items: center; - gap: 0.5em; -} - +/* Styling for flash messages */ .flash-messages { - background: white; + background: var(--color-bg-white); margin-top: 10px; padding: 15px; border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); + box-shadow: 0 2px 10px var(--shadow-primary); } .flash-message { @@ -436,18 +495,28 @@ label.description { color: var(--color-required); } +.tick-feed { + list-style: none; + padding: 0; + margin: 0; + margin-top: 0.5em; +} + .tick { margin-bottom: 1em; + padding-left: 0.5em; } .tick-time { color: var(--color-text-muted); font-size: 0.8em; + margin-bottom: 0.4em; } .tick-text { color: var(--color-text-black); font-size: 1.0em; + display: block; } .tick-pagination a { diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 0000000..f86ed55 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1,6 @@ +# Deny all access to this directory +Require all denied + +# Fallback for Apache 2.2 +Order deny,allow +Deny from all \ No newline at end of file diff --git a/src/Controller/AuthController/AuthController.php b/src/Controller/AuthController/AuthController.php index 15bc901..4efbfcd 100644 --- a/src/Controller/AuthController/AuthController.php +++ b/src/Controller/AuthController/AuthController.php @@ -16,8 +16,6 @@ class AuthController extends Controller { function handleLogin(){ global $config; - $error = ''; - if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = $_POST['username'] ?? ''; $password = $_POST['password'] ?? ''; @@ -37,7 +35,10 @@ class AuthController extends Controller { header('Location: ' . $config->basePath); exit; } else { - $error = 'Invalid username or password'; + // Set a flash message and reload the login page + Session::setFlashMessage('error', 'Invalid username or password'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; } } } diff --git a/src/View/FlashView/FlashView.php b/src/View/FlashView/FlashView.php index cf7e9a0..e657d55 100644 --- a/src/View/FlashView/FlashView.php +++ b/src/View/FlashView/FlashView.php @@ -9,7 +9,7 @@ class FlashView { $messages): ?> diff --git a/src/View/HomeView/HomeView.php b/src/View/HomeView/HomeView.php index da17826..d49921b 100644 --- a/src/View/HomeView/HomeView.php +++ b/src/View/HomeView/HomeView.php @@ -4,15 +4,14 @@ class HomeView { ob_start(); ?> -