Remove a bunch of trailing whitespace.
This commit is contained in:
parent
93bfe14d4f
commit
a3a6471ced
@ -39,7 +39,7 @@ RewriteCond %{REQUEST_URI} !^/css/custom/
|
||||
RewriteRule ^css/tkr\.css$ public/css/tkr.css [L]
|
||||
|
||||
# 404 all other static files (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L]
|
||||
|
||||
|
@ -19,7 +19,7 @@ A lightweight, HTML-only status feed for self-hosted personal websites. Written
|
||||
* PHP 8.2+ with the PDO and PDO_SQLITE extensions
|
||||
* The PDO and PDO_SQLITE extensions are usually included by default
|
||||
* This might work with earlier PHP versions, but I've only tested 8.2
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the latest tkr archive from https://subcultureofone.org/files/tkr/tkr.0.6.0.zip
|
||||
|
@ -18,12 +18,12 @@ define('DB_FILE', DATA_DIR . '/tkr.sqlite');
|
||||
// Define an exception for validation errors
|
||||
class SetupException extends Exception {
|
||||
private $setupIssue;
|
||||
|
||||
|
||||
public function __construct(string $message, string $setupIssue = '', int $code = 0, Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->setupIssue = $setupIssue;
|
||||
}
|
||||
|
||||
|
||||
public function getSetupIssue(): string {
|
||||
return $this->setupIssue;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ RewriteCond %{REQUEST_URI} !^/css/custom/
|
||||
RewriteRule ^css/tkr\.css$ public/css/tkr.css [L]
|
||||
|
||||
# 404 all other static files (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L]
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
# Replace localhost with your subdomain, e.g. tkr.my-domain.com
|
||||
ServerName localhost
|
||||
DocumentRoot /var/www/tkr/public
|
||||
|
||||
|
||||
# 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"
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
|
||||
|
||||
# Block access to sensitive directories
|
||||
<Directory "/var/www/tkr/storage">
|
||||
Require all denied
|
||||
@ -44,7 +44,7 @@
|
||||
Alias /css/tkr.css /var/www/tkr/public/css/tkr.css
|
||||
|
||||
# 404 all non-css static files (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
<LocationMatch "\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
|
||||
<RequireAll>
|
||||
@ -69,7 +69,7 @@
|
||||
RewriteCond %{REQUEST_URI} !^/css/custom/
|
||||
RewriteRule ^css/tkr\.css$ css/tkr.css [L]
|
||||
|
||||
# Everything else to front controller
|
||||
# Everything else to front controller
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
|
@ -24,13 +24,13 @@
|
||||
# Replace with the actual paths to your cert and key
|
||||
SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem
|
||||
|
||||
|
||||
# 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"
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
|
||||
|
||||
# Block access to sensitive directories
|
||||
<Directory "/var/www/tkr/storage">
|
||||
Require all denied
|
||||
@ -59,7 +59,7 @@
|
||||
Alias /css/tkr.css /var/www/tkr/public/css/tkr.css
|
||||
|
||||
# 404 all non-css static files (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
<LocationMatch "\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
|
||||
<RequireAll>
|
||||
@ -84,7 +84,7 @@
|
||||
RewriteCond %{REQUEST_URI} !^/css/custom/
|
||||
RewriteRule ^css/tkr\.css$ css/tkr.css [L]
|
||||
|
||||
# Everything else to front controller
|
||||
# Everything else to front controller
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
|
@ -9,18 +9,18 @@
|
||||
# Replace localhost with your subdomain, e.g. tkr.my-domain.com
|
||||
ServerName localhost
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
|
||||
# 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"
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
|
||||
|
||||
# tkr Application at /tkr
|
||||
# NOTE: If you change the directory name,
|
||||
# remember to update all instances of /var/www/tkr in this file to match
|
||||
# remember to update all instances of /var/www/tkr in this file to match
|
||||
Alias /tkr /var/www/tkr/public
|
||||
|
||||
|
||||
# Block access to sensitive TKR directories
|
||||
<Directory "/var/www/tkr/storage">
|
||||
Require all denied
|
||||
@ -34,28 +34,28 @@
|
||||
<Directory "/var/www/tkr/config">
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
|
||||
# 404 all non-css static files in /tkr (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
<LocationMatch "^/tkr/.*\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
|
||||
<RequireAll>
|
||||
Require all denied
|
||||
</RequireAll>
|
||||
</LocationMatch>
|
||||
|
||||
|
||||
# tkr application directory
|
||||
<Directory "/var/www/tkr/public">
|
||||
Options -Indexes
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
|
||||
# Block direct PHP access
|
||||
RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
|
||||
RewriteRule ^.*$ - [R=404,L]
|
||||
|
||||
|
||||
# Serve the one static file that exists: css/tkr.css
|
||||
# (Pass requests to css/custom/ through to the PHP app)
|
||||
RewriteCond %{REQUEST_URI} !^/tkr/css/custom/
|
||||
@ -66,7 +66,7 @@
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
</Directory>
|
||||
|
||||
|
||||
# Error and access logs
|
||||
ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
|
||||
|
@ -17,7 +17,7 @@
|
||||
# Replace localhost with your subdomain, e.g. tkr.my-domain.com
|
||||
ServerName localhost
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
|
||||
# SSL Configuration
|
||||
SSLEngine on
|
||||
|
||||
@ -25,18 +25,18 @@
|
||||
# Replace with the actual paths to your cert and key
|
||||
SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem
|
||||
|
||||
|
||||
# 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"
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
|
||||
|
||||
# tkr Application at /tkr
|
||||
# NOTE: If you change the directory name,
|
||||
# remember to update all instances of /var/www/tkr in this file to match
|
||||
# remember to update all instances of /var/www/tkr in this file to match
|
||||
Alias /tkr /var/www/tkr/public
|
||||
|
||||
|
||||
# Block access to sensitive TKR directories
|
||||
<Directory "/var/www/tkr/storage">
|
||||
Require all denied
|
||||
@ -50,28 +50,28 @@
|
||||
<Directory "/var/www/tkr/config">
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
|
||||
# 404 all non-css static files in /tkr (images, js, fonts, etc.)
|
||||
# so those requests don't hit the PHP app
|
||||
# so those requests don't hit the PHP app
|
||||
# (this is to reduce load on the PHP app from bots and scanners)
|
||||
<LocationMatch "^/tkr/.*\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
|
||||
<RequireAll>
|
||||
Require all denied
|
||||
</RequireAll>
|
||||
</LocationMatch>
|
||||
|
||||
|
||||
# tkr application directory
|
||||
<Directory "/var/www/tkr/public">
|
||||
Options -Indexes
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
|
||||
# Block direct PHP access
|
||||
RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
|
||||
RewriteRule ^.*$ - [R=404,L]
|
||||
|
||||
|
||||
# Serve the one static file that exists: css/tkr.css
|
||||
# (Pass requests to css/custom/ through to the PHP app)
|
||||
RewriteCond %{REQUEST_URI} !^/tkr/css/custom/
|
||||
@ -82,7 +82,7 @@
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [L]
|
||||
</Directory>
|
||||
|
||||
|
||||
# Error and access logs
|
||||
ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
|
||||
|
@ -10,7 +10,7 @@ server {
|
||||
# replace localhost with your subdomain
|
||||
# e.g. tkr.my-domain.com
|
||||
server_name localhost;
|
||||
|
||||
|
||||
root /var/www/tkr/public;
|
||||
index index.php;
|
||||
|
||||
@ -63,7 +63,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
@ -73,7 +73,7 @@ server {
|
||||
# (these are bots and scanners)
|
||||
location ~ ^/.+\.php$ {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
# forward other requests to the fallback block,
|
||||
# which sends them to php-fpm for handling
|
||||
@ -88,7 +88,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
|
@ -17,7 +17,7 @@ server {
|
||||
# Replace with the actual paths to your cert and key
|
||||
ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem;
|
||||
|
||||
|
||||
root /var/www/tkr/public;
|
||||
index index.php;
|
||||
|
||||
@ -61,7 +61,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
@ -71,7 +71,7 @@ server {
|
||||
# (these are bots and scanners)
|
||||
location ~ ^/.+\.php$ {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
# forward other requests to the fallback block,
|
||||
# which sends them to php-fpm for handling
|
||||
@ -86,7 +86,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
|
@ -12,7 +12,7 @@ server {
|
||||
# replace localhost with your subdomain
|
||||
# e.g. tkr.my-domain.com
|
||||
server_name localhost;
|
||||
|
||||
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
|
||||
@ -65,7 +65,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
@ -75,7 +75,7 @@ server {
|
||||
# (these are bots and scanners)
|
||||
location ~ ^/tkr/.+\.php$ {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
# forward other requests to the fallback block,
|
||||
# which sends them to php-fpm for handling
|
||||
@ -87,7 +87,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
|
@ -8,11 +8,11 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
|
||||
|
||||
# Replace localhost with your domain
|
||||
# e.g. my-domain.com
|
||||
server_name localhost;
|
||||
|
||||
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
|
||||
@ -56,7 +56,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
@ -66,7 +66,7 @@ server {
|
||||
# (these are bots and scanners)
|
||||
location ~ ^/tkr/.+\.php$ {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
# forward other requests to the fallback block,
|
||||
# which sends them to php-fpm for handling
|
||||
@ -78,7 +78,7 @@ server {
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
|
||||
include fastcgi_params;
|
||||
|
||||
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
|
@ -8,7 +8,7 @@
|
||||
--color-primary-light: #bae6fd;
|
||||
--color-primary-lighter: #ebf8ff;
|
||||
--color-primary-lightest: #f0f9ff;
|
||||
|
||||
|
||||
/* Text colors */
|
||||
--color-text-primary: #374151;
|
||||
--color-text-secondary: #1e40af;
|
||||
@ -16,25 +16,25 @@
|
||||
--color-text-muted: gray;
|
||||
--color-text-black: black;
|
||||
--color-text-dark: #333;
|
||||
|
||||
|
||||
/* Background colors */
|
||||
--color-bg-body: whitesmoke;
|
||||
--color-bg-white: white;
|
||||
--color-bg-light: #fefefe;
|
||||
--color-bg-file: #f8fafc;
|
||||
|
||||
|
||||
/* Border colors */
|
||||
--color-border-light: #e5e7eb;
|
||||
--color-border-medium: #d1d5db;
|
||||
--color-border-file: #cbd5e0;
|
||||
|
||||
|
||||
/* State colors */
|
||||
--color-required: #dc2626;
|
||||
--color-hover-light: #dbeafe;
|
||||
--color-hover-medium: #bfdbfe;
|
||||
--color-emoji-bg: #ddeeff;
|
||||
--color-emoji-border: #339;
|
||||
|
||||
|
||||
/* Shadow colors */
|
||||
--shadow-primary: rgba(66, 153, 225, 0.1);
|
||||
--shadow-primary-strong: rgba(66, 153, 225, 0.3);
|
||||
@ -108,9 +108,9 @@ h1.site-description {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
@ -124,9 +124,9 @@ select {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="password"]:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
@ -205,14 +205,14 @@ label.description {
|
||||
|
||||
.navbar {
|
||||
overflow: visible;
|
||||
background: var(--color-bg-white);
|
||||
background: var(--color-bg-white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navbar a {
|
||||
font-size: 16px;
|
||||
color: var(--color-text-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
@ -229,9 +229,9 @@ label.description {
|
||||
box-shadow: 0 0 0 2px var(--shadow-primary);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
Using details/summary tags to build dropdowm nmenus.
|
||||
They have to be clicked open and closed, but they allow
|
||||
They have to be clicked open and closed, but they allow
|
||||
me to have semantically appropriate pure HTML dropdowms
|
||||
that work on mobile devices.
|
||||
*/
|
||||
@ -241,7 +241,7 @@ label.description {
|
||||
}
|
||||
|
||||
.dropdown summary.dropbtn {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
outline: none;
|
||||
@ -424,13 +424,13 @@ label.description {
|
||||
|
||||
/* Styling for flash messages */
|
||||
.flash-messages {
|
||||
background: var(--color-bg-white);
|
||||
background: var(--color-bg-white);
|
||||
margin-top: 10px;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px var(--shadow-primary);
|
||||
}
|
||||
|
||||
|
||||
.flash-message {
|
||||
padding: 12px 16px;
|
||||
margin: 5px 0;
|
||||
@ -462,7 +462,7 @@ label.description {
|
||||
border-left-color: var(--color-flash-info-border-left);
|
||||
color: var(--color-flash-info);
|
||||
}
|
||||
|
||||
|
||||
.fieldset-items {
|
||||
margin-bottom: 14px;
|
||||
display: grid;
|
||||
@ -532,7 +532,7 @@ label.description {
|
||||
}
|
||||
|
||||
.tick-time {
|
||||
color: var(--color-text-muted);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.8em;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
@ -543,9 +543,9 @@ label.description {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tick-pagination a {
|
||||
margin: 0 5px;
|
||||
text-decoration: none;
|
||||
.tick-pagination a {
|
||||
margin: 0 5px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.emoji-option input {
|
||||
@ -616,7 +616,7 @@ label.description {
|
||||
box-shadow: 0 0 0 2px var(--shadow-primary);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
Responsive layout - adjusts from 1 to 2 columns based on screen width
|
||||
- min-width makes the mobile (stacked) view the default
|
||||
- 600px covers most mobile devices in portrait mode
|
||||
@ -633,7 +633,7 @@ label.description {
|
||||
padding-top: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
.home-container {
|
||||
grid-template-columns: 1fr 2fr;
|
||||
grid-gap: 2em;
|
||||
@ -646,7 +646,7 @@ label.description {
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
|
||||
.file-info {
|
||||
grid-column: 2;
|
||||
}
|
||||
@ -654,7 +654,7 @@ label.description {
|
||||
.navbar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
.dropdown-menu {
|
||||
position: fixed;
|
||||
left: 1em;
|
||||
|
@ -51,13 +51,13 @@ class AdminController extends Controller {
|
||||
// handle form submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors = [];
|
||||
|
||||
|
||||
// UserModel profile
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$displayName = trim($_POST['display_name'] ?? '');
|
||||
$about = trim($_POST['about'] ?? '');
|
||||
$website = trim($_POST['website'] ?? '');
|
||||
|
||||
|
||||
// Site settings
|
||||
$siteTitle = trim($_POST['site_title']) ?? '';
|
||||
$siteDescription = trim($_POST['site_description']) ?? '';
|
||||
@ -68,7 +68,7 @@ class AdminController extends Controller {
|
||||
// Password
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirmPassword = $_POST['confirm_password'] ?? '';
|
||||
|
||||
|
||||
// Validate user profile
|
||||
if (!$username) {
|
||||
$errors[] = "Username is required.";
|
||||
@ -87,7 +87,7 @@ class AdminController extends Controller {
|
||||
$errors[] = "URL must start with http:// or https://.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate site settings
|
||||
if (!$siteTitle) {
|
||||
$errors[] = "Site title is required.";
|
||||
@ -98,12 +98,12 @@ class AdminController extends Controller {
|
||||
if ($itemsPerPage < 1 || $itemsPerPage > 50) {
|
||||
$errors[] = "Items per page must be a number between 1 and 50.";
|
||||
}
|
||||
|
||||
|
||||
// If a password was sent, make sure it matches the confirmation
|
||||
if ($password && !($password === $confirmPassword)){
|
||||
$errors[] = "Passwords do not match";
|
||||
}
|
||||
|
||||
|
||||
// Validation complete
|
||||
if (empty($errors)) {
|
||||
// Update site settings
|
||||
@ -112,21 +112,21 @@ class AdminController extends Controller {
|
||||
$config->baseUrl = $baseUrl;
|
||||
$config->basePath = $basePath;
|
||||
$config->itemsPerPage = $itemsPerPage;
|
||||
|
||||
|
||||
// Save site settings and reload config from database
|
||||
// TODO - raise and handle exception on failure
|
||||
$config = $config->save();
|
||||
|
||||
|
||||
// Update user profile
|
||||
$user->username = $username;
|
||||
$user->displayName = $displayName;
|
||||
$user->about = $about;
|
||||
$user->website = $website;
|
||||
|
||||
|
||||
// Save user profile and reload user from database
|
||||
// TODO - raise and handle exception on failure
|
||||
$user = $user->save();
|
||||
|
||||
|
||||
// Update the password if one was sent
|
||||
// TODO - raise and handle exception on failure
|
||||
if($password){
|
||||
|
@ -19,13 +19,13 @@ class AuthController extends Controller {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
|
||||
// TODO: move into user model
|
||||
global $db;
|
||||
$stmt = $db->prepare("SELECT id, username, password_hash FROM user WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
|
||||
if ($user && password_verify($password, $user['password_hash'])) {
|
||||
session_regenerate_id(true);
|
||||
// TODO: move into session.php
|
||||
|
@ -24,5 +24,5 @@ class Controller {
|
||||
|
||||
extract($vars, EXTR_SKIP);
|
||||
include $templatePath;
|
||||
}
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@ class CssController extends Controller {
|
||||
http_response_code(400);
|
||||
exit("Cannot delete default theme");
|
||||
}
|
||||
|
||||
|
||||
// Get the data for the selected CSS file
|
||||
$cssId = $_POST['selectCssFile'];
|
||||
$cssModel = new CssModel();
|
||||
@ -87,7 +87,7 @@ class CssController extends Controller {
|
||||
http_response_code(400);
|
||||
exit("No entry found for css id $cssId");
|
||||
}
|
||||
|
||||
|
||||
// get the filename
|
||||
$cssFilename = $cssRow["filename"];
|
||||
|
||||
@ -151,7 +151,7 @@ class CssController extends Controller {
|
||||
// Validate file extension
|
||||
$filename = $file['name'];
|
||||
$fileExtension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
|
||||
if ($fileExtension !== 'css') {
|
||||
throw new Exception('File must have a .css extension');
|
||||
}
|
||||
@ -186,7 +186,7 @@ class CssController extends Controller {
|
||||
// Add upload to database
|
||||
$cssModel = new CssModel();
|
||||
$cssModel->save($safeFilename, $description);
|
||||
|
||||
|
||||
// Set success flash message
|
||||
Session::setFlashMessage('success', 'Theme uploaded as ' . $safeFilename);
|
||||
|
||||
@ -200,11 +200,11 @@ class CssController extends Controller {
|
||||
private function validateCssContent($content) {
|
||||
// Remove comments
|
||||
$content = preg_replace('/\/\*.*?\*\//s', '', $content);
|
||||
|
||||
|
||||
// Basic CSS validation - check for balanced braces
|
||||
$openBraces = substr_count($content, '{');
|
||||
$closeBraces = substr_count($content, '}');
|
||||
|
||||
|
||||
if ($openBraces !== $closeBraces) {
|
||||
throw new Exception('Invalid CSS: Unbalanced braces detected');
|
||||
}
|
||||
@ -261,7 +261,7 @@ class CssController extends Controller {
|
||||
// Remove path information and dangerous characters
|
||||
$fileName = basename($originalName);
|
||||
$fileName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $fileName);
|
||||
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
'config' => $config,
|
||||
'moodPicker' => $moodPicker,
|
||||
];
|
||||
|
||||
|
||||
$this->render("mood.php", $vars);
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
// set or clear the mood
|
||||
$user->mood = $mood;
|
||||
$user = $user->save();
|
||||
|
||||
|
||||
// go back to the index and show the updated mood
|
||||
header('Location: ' . $config->basePath);
|
||||
exit;
|
||||
|
@ -4,7 +4,7 @@ class TickController extends Controller{
|
||||
// every tick is identified by its timestamp
|
||||
public function index(string $year, string $month, string $day, string $hour, string $minute, string $second){
|
||||
$model = new TickModel();
|
||||
$tick = $model->get($year, $month, $day, $hour, $minute, $second);
|
||||
$tick = $model->get($year, $month, $day, $hour, $minute, $second);
|
||||
$this->render('tick.php', $tick);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class Util {
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// For relative time display, compare the stored time to the current time
|
||||
// and display it as "X seconds/minutes/hours/days etc." ago
|
||||
@ -59,6 +59,6 @@ class Util {
|
||||
[$year, $month, $day] = $dateParts;
|
||||
[$hour, $minute, $second] = $timeParts;
|
||||
|
||||
return "$year/$month/$day/$hour/$minute/$second";
|
||||
return "$year/$month/$day/$hour/$minute/$second";
|
||||
}
|
||||
}
|
@ -36,11 +36,11 @@ class ConfigModel {
|
||||
if (empty($this->cssId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Fetch filename from css table using cssId
|
||||
$cssModel = new CssModel();
|
||||
$cssRecord = $cssModel->getById($this->cssId);
|
||||
|
||||
|
||||
return $cssRecord ? $cssRecord['filename'] : null;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class TickModel {
|
||||
// split the path to the current file into the date components
|
||||
$pathParts = explode('/', str_replace('\\', '/', $file));
|
||||
|
||||
// assign the different components to the appropriate part of the date
|
||||
// assign the different components to the appropriate part of the date
|
||||
$year = $pathParts[count($pathParts) - 3];
|
||||
$month = $pathParts[count($pathParts) - 2];
|
||||
$day = pathinfo($pathParts[count($pathParts) - 1], PATHINFO_FILENAME);
|
||||
@ -75,18 +75,18 @@ class TickModel {
|
||||
$content = $time . "|" . $tick . "\n";
|
||||
file_put_contents($filename, $content, FILE_APPEND);
|
||||
}
|
||||
|
||||
|
||||
public static function get(string $y, string $m, string $d, string $H, string $i, string $s): array{
|
||||
$tickTime = new DateTime("$y-$m-$d $H:$i:$s");
|
||||
$timestamp = "$H:$i:$s";
|
||||
$file = TICKS_DIR . "/$y/$m/$d.txt";
|
||||
|
||||
|
||||
if (!file_exists($file)) {
|
||||
http_response_code(404);
|
||||
echo "Tick not found: $file.";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (str_starts_with($line, $timestamp)) {
|
||||
|
@ -46,7 +46,7 @@ class UserModel {
|
||||
// loading the password into memory
|
||||
public function set_password(string $password): void {
|
||||
global $db;
|
||||
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("UPDATE user SET password_hash=? WHERE id=1");
|
||||
$stmt->execute([$hash]);
|
||||
|
@ -12,7 +12,7 @@ echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
<channel>
|
||||
<title><?php echo Util::escape_xml($config->siteTitle . 'RSS Feed') ?></title>
|
||||
<link><?php echo Util::escape_xml($config->baseUrl . $config->basePath)?></link>
|
||||
<atom:link href="<?php echo Util::escape_xml($config->baseUrl . $config->basePath. 'feed/rss')?>"
|
||||
<atom:link href="<?php echo Util::escape_xml($config->baseUrl . $config->basePath. 'feed/rss')?>"
|
||||
rel="self"
|
||||
type="application/rss+xml" />
|
||||
<description><?php echo Util::escape_xml($config->siteDescription) ?></description>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<link rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="<?php echo Util::escape_html($config->siteTitle) ?> RSS Feed"
|
||||
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/rss/">
|
||||
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/rss/">
|
||||
<link rel="alternate"
|
||||
type="application/atom+xml"
|
||||
title="<?php echo Util::escape_html($config->siteTitle) ?> Atom Feed"
|
||||
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/atom/">
|
||||
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/atom/">
|
||||
</head>
|
||||
<body>
|
||||
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<h1><?php if ($isSetup): ?>Setup<?php else: ?>Admin<?php endif; ?></h1>
|
||||
<div>
|
||||
<form
|
||||
action="<?php echo $config->basePath . ($isSetup ? 'setup' : 'admin') ?>"
|
||||
action="<?php echo $config->basePath . ($isSetup ? 'setup' : 'admin') ?>"
|
||||
method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
|
||||
<fieldset>
|
||||
@ -16,7 +16,7 @@
|
||||
value="<?= Util::escape_html($user->username) ?>"
|
||||
required>
|
||||
<label>Display name <span class=required>*</span></label>
|
||||
<input type="text"
|
||||
<input type="text"
|
||||
name="display_name"
|
||||
value="<?= Util::escape_html($user->displayName) ?>"
|
||||
required>
|
||||
@ -36,7 +36,7 @@
|
||||
<label>Title <span class=required>*</span></label>
|
||||
<input type="text"
|
||||
name="site_title"
|
||||
value="<?= Util::escape_html($config->siteTitle) ?>"
|
||||
value="<?= Util::escape_html($config->siteTitle) ?>"
|
||||
required>
|
||||
<label>Description <span class=required>*</span></label>
|
||||
<input type="text"
|
||||
@ -47,7 +47,7 @@
|
||||
name="base_url"
|
||||
value="<?= Util::escape_html($config->baseUrl) ?>"
|
||||
required>
|
||||
<label>Base path <span class=required>*</span></label>
|
||||
<label>Base path <span class=required>*</span></label>
|
||||
<input type="text"
|
||||
name="base_path"
|
||||
value="<?= Util::escape_html($config->basePath) ?>"
|
||||
@ -62,7 +62,7 @@
|
||||
<fieldset>
|
||||
<legend>Change password</legend>
|
||||
<div class="fieldset-items">
|
||||
<label>New password
|
||||
<label>New password
|
||||
<?php if($isSetup): ?><span class=required>*</span><?php endif; ?>
|
||||
</label>
|
||||
<input type="password"
|
||||
|
@ -11,14 +11,14 @@
|
||||
<select id="selectCssFile" name="selectCssFile" value=<?= $config->cssId ?>>
|
||||
<option value="">Default</option>
|
||||
<?php foreach ($customCss as $cssFile): ?>
|
||||
<?php
|
||||
<?php
|
||||
if ($cssFile['id'] == $config->cssId){
|
||||
$cssDescription = $cssFile['description'];
|
||||
$selected = "selected";
|
||||
}
|
||||
?>
|
||||
|
||||
<option value=<?= $cssFile['id'] ?>
|
||||
<option value=<?= $cssFile['id'] ?>
|
||||
<?= isset($selected) ? $selected : ""?>>
|
||||
<?=Util::escape_html($cssFile['filename'])?>
|
||||
</option>
|
||||
@ -40,9 +40,9 @@
|
||||
<div class="fieldset-items">
|
||||
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
|
||||
<label for="uploadCssFile">Select File to Upload</label>
|
||||
<input type="file"
|
||||
id="uploadCssFile"
|
||||
name="uploadCssFile"
|
||||
<input type="file"
|
||||
id="uploadCssFile"
|
||||
name="uploadCssFile"
|
||||
accept=".css">
|
||||
<div class="file-info">
|
||||
<strong>File Requirements:</strong><br>
|
||||
@ -51,8 +51,8 @@
|
||||
• Will be scanned for malicious content
|
||||
</div>
|
||||
<label for="description">Description (optional)</label>
|
||||
<textarea id="description"
|
||||
name="description"
|
||||
<textarea id="description"
|
||||
name="description"
|
||||
placeholder="Describe this CSS file..."></textarea>
|
||||
<div></div>
|
||||
<button type="submit" name="action" value="upload">Upload CSS File</button>
|
||||
|
@ -31,9 +31,9 @@
|
||||
<div class="fieldset-items">
|
||||
<?php foreach ($emojiList as $emojiItem): ?>
|
||||
<div class="emoji-checkbox-item">
|
||||
<input type="checkbox"
|
||||
id="delete_emoji_<?= Util::escape_html($emojiItem['id']) ?>"
|
||||
name="delete_emoji_ids[]"
|
||||
<input type="checkbox"
|
||||
id="delete_emoji_<?= Util::escape_html($emojiItem['id']) ?>"
|
||||
name="delete_emoji_ids[]"
|
||||
value="<?= Util::escape_html($emojiItem['id']) ?>">
|
||||
<label for="delete_emoji_<?= Util::escape_html($emojiItem['id']) ?>">
|
||||
<span class="emoji-display"><?= Util::escape_html($emojiItem['emoji']) ?></span>
|
||||
|
@ -34,7 +34,7 @@
|
||||
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
|
||||
<textarea name="tick"
|
||||
placeholder="What's ticking?"
|
||||
minlength="1"
|
||||
minlength="1"
|
||||
maxlength="200"
|
||||
rows="3"></textarea>
|
||||
<button type="submit" class="submit-btn">Tick</button>
|
||||
|
Loading…
x
Reference in New Issue
Block a user