Make the homepage an actual template.
This commit is contained in:
parent
d3271e43a0
commit
51595c13eb
@ -22,6 +22,15 @@ server {
|
|||||||
alias /var/www/html/public;
|
alias /var/www/html/public;
|
||||||
index index.php;
|
index index.php;
|
||||||
|
|
||||||
|
# Cache static files
|
||||||
|
# Note that I don't actually serve most of this (just js and css to start)
|
||||||
|
# but including them all will let caching work later if I add images or something
|
||||||
|
location ~* ^/tkr/.+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
# index.php is the entry point
|
# index.php is the entry point
|
||||||
# It needs to be sent to php-fpm
|
# It needs to be sent to php-fpm
|
||||||
# But if someone tries to directly access index.php, that file will throw a 404
|
# But if someone tries to directly access index.php, that file will throw a 404
|
||||||
@ -60,15 +69,6 @@ server {
|
|||||||
fastcgi_param QUERY_STRING $query_string;
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Cache static files
|
|
||||||
# Note that I don't actually serve most of this (just js and css to start)
|
|
||||||
# but including them all will let caching work later if I add images or something
|
|
||||||
location ~* ^/tkr/.+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Deny access to sensitive directories
|
# Deny access to sensitive directories
|
||||||
location ~ ^/tkr/(storage|lib|vendor|config) {
|
location ~ ^/tkr/(storage|lib|vendor|config) {
|
||||||
deny all;
|
deny all;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('APP_ROOT', dirname(__FILE__));
|
#define('APP_ROOT', dirname(__FILE__));
|
||||||
define('CLASSES_DIR', APP_ROOT . '/classes');
|
#define('CLASSES_DIR', APP_ROOT . '/classes');
|
||||||
define('LIB_DIR', APP_ROOT . '/lib');
|
#define('LIB_DIR', APP_ROOT . '/lib');
|
||||||
define('TICKS_DIR', APP_ROOT . '/storage/ticks');
|
#define('TICKS_DIR', APP_ROOT . '/storage/ticks');
|
||||||
define('DATA_DIR', APP_ROOT . '/storage/db');
|
#define('DATA_DIR', APP_ROOT . '/storage/db');
|
||||||
define('DB_FILE', DATA_DIR . '/tkr.sqlite');
|
#define('DB_FILE', DATA_DIR . '/tkr.sqlite');
|
||||||
|
|
||||||
function verify_data_dir(string $dir, bool $allow_create = false): void {
|
function verify_data_dir(string $dir, bool $allow_create = false): void {
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
// Made this a class so it could be more obvious where config settings are coming from.
|
// Made this a class so it could be more obvious where config settings are coming from.
|
||||||
// Felt too much like magic constants in other files before.
|
// Felt too much like magic constants in other files before.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
// Made this a class so it could be more obvious where config settings are coming from.
|
// Made this a class so it could be more obvious where config settings are coming from.
|
||||||
// Felt too much like magic constants in other files before.
|
// Felt too much like magic constants in other files before.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require_once CLASSES_DIR . '/User.php';
|
#require_once CLASSES_DIR . '/User.php';
|
||||||
|
|
||||||
require LIB_DIR . '/emoji.php';
|
#require LIB_DIR . '/emoji.php';
|
||||||
|
|
||||||
function save_mood(string $mood): void {
|
function save_mood(string $mood): void {
|
||||||
$config = Config::load();
|
$config = Config::load();
|
||||||
|
@ -3,8 +3,6 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
$isLoggedIn = isset($_SESSION['username']);
|
|
||||||
|
|
||||||
function generateCsrfToken() {
|
function generateCsrfToken() {
|
||||||
if (empty($_SESSION['csrf_token'])) {
|
if (empty($_SESSION['csrf_token'])) {
|
||||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
function save_tick(string $tick): void {
|
function save_tick(string $tick): void {
|
||||||
// build the tick path and filename from the current time
|
// build the tick path and filename from the current time
|
||||||
|
@ -38,3 +38,101 @@ function relative_time(string $tickTime): string {
|
|||||||
}
|
}
|
||||||
return $diff->s . ' second' . ($diff->s != 1 ? 's' : '') . ' ago';
|
return $diff->s . ' second' . ($diff->s != 1 ? 's' : '') . ' ago';
|
||||||
}
|
}
|
||||||
|
function verify_data_dir(string $dir, bool $allow_create = false): void {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
if ($allow_create) {
|
||||||
|
if (!mkdir($dir, 0770, true)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo "Failed to create required directory: $dir";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(500);
|
||||||
|
echo "Required directory does not exist: $dir";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($dir)) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo "Directory is not writable: $dir";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that setup is complete (i.e. the databse is populated).
|
||||||
|
// Redirect to setup.php if it isn't.
|
||||||
|
function confirm_setup(): void {
|
||||||
|
$db = get_db();
|
||||||
|
|
||||||
|
// Ensure required tables exist
|
||||||
|
$db->exec("CREATE TABLE IF NOT EXISTS user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
display_name TEXT NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
about TEXT NULL,
|
||||||
|
website TEXT NULL,
|
||||||
|
mood TEXT NULL
|
||||||
|
)");
|
||||||
|
|
||||||
|
$db->exec("CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
site_title TEXT NOT NULL,
|
||||||
|
site_description TEXT NULL,
|
||||||
|
base_path TEXT NOT NULL,
|
||||||
|
items_per_page INTEGER NOT NULL
|
||||||
|
)");
|
||||||
|
|
||||||
|
// See if there's any data in the tables
|
||||||
|
$user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
|
||||||
|
$settings_count = (int) $db->query("SELECT COUNT(*) FROM settings")->fetchColumn();
|
||||||
|
|
||||||
|
// If either table has no records and we aren't on setup.php, redirect to setup.php
|
||||||
|
if ($user_count === 0 || $settings_count === 0){
|
||||||
|
if (basename($_SERVER['PHP_SELF']) !== 'setup.php'){
|
||||||
|
header('Location: setup.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If setup is complete and we are on setup.php, redirect to index.php.
|
||||||
|
if (basename($_SERVER['PHP_SELF']) === 'setup.php'){
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_db(): PDO {
|
||||||
|
verify_data_dir(DATA_DIR, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = new PDO("sqlite:" . DB_FILE);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Database connection failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_template(string $templateFile, array $vars = []): string {
|
||||||
|
#$templatePath = TEMPLATES_DIR . '/' . ltrim($templateFile, '/');
|
||||||
|
|
||||||
|
if (!file_exists($templateFile)) {
|
||||||
|
throw new RuntimeException("Template not found: $templatePath");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract variables into local scope
|
||||||
|
extract($vars, EXTR_SKIP);
|
||||||
|
|
||||||
|
// Start output buffering
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Include the template (with extracted variables in scope)
|
||||||
|
include $templateFile;
|
||||||
|
|
||||||
|
// Return rendered output
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
@ -1,7 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
|
define('APP_ROOT', dirname(dirname(__FILE__)));
|
||||||
|
|
||||||
|
define('CLASSES_DIR', APP_ROOT . '/classes');
|
||||||
|
define('LIB_DIR', APP_ROOT . '/lib');
|
||||||
|
define('STORAGE_DIR', APP_ROOT . '/storage');
|
||||||
|
define('TEMPLATES_DIR', APP_ROOT . '/templates');
|
||||||
|
|
||||||
|
define('TICKS_DIR', STORAGE_DIR . '/ticks');
|
||||||
|
define('DATA_DIR', STORAGE_DIR . '/db');
|
||||||
|
define('DB_FILE', DATA_DIR . '/tkr.sqlite');
|
||||||
|
|
||||||
|
$include_dirs = [
|
||||||
|
LIB_DIR,
|
||||||
|
CLASSES_DIR,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($include_dirs as $include_dir){
|
||||||
|
foreach (glob($include_dir . '/*.php') as $file) {
|
||||||
|
require_once $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_setup();
|
||||||
|
|
||||||
|
$isLoggedIn = isset($_SESSION['user_id']);
|
||||||
|
$config = Config::load();
|
||||||
|
$user = User::load();
|
||||||
|
|
||||||
// Define your base path (subdirectory)
|
// Define your base path (subdirectory)
|
||||||
$basePath = '/tkr';
|
#$basePath = '/tkr';
|
||||||
|
|
||||||
// Get HTTP data
|
// Get HTTP data
|
||||||
$method = $_SERVER['REQUEST_METHOD'];
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
@ -11,8 +40,15 @@ $request = $_SERVER['REQUEST_URI'];
|
|||||||
// and strip the trailing slash from the resulting route
|
// and strip the trailing slash from the resulting route
|
||||||
$path = parse_url($request, PHP_URL_PATH);
|
$path = parse_url($request, PHP_URL_PATH);
|
||||||
|
|
||||||
if (strpos($path, $basePath) === 0) {
|
// return a 404 if s request for a .php file gets this far.
|
||||||
$path = substr($path, strlen($basePath));
|
if (preg_match('/\.php$/', $path)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo '<h1>404 Not Found</h1>';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($path, $config->basePath) === 0) {
|
||||||
|
$path = substr($path, strlen($config->basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = trim($path, '/');
|
$path = trim($path, '/');
|
||||||
@ -42,7 +78,21 @@ header('Content-Type: text/html; charset=utf-8');
|
|||||||
echo "Path: " . $path;
|
echo "Path: " . $path;
|
||||||
|
|
||||||
// Define your routes
|
// Define your routes
|
||||||
route('', function() {
|
route('', function() use ($isLoggedIn, $config, $user) {
|
||||||
echo '<h1>Home Page</h1>';
|
#include TEMPLATES_DIR . "/home.php";
|
||||||
echo '<p>Welcome to the home page!</p>';
|
#echo render_home_page($isLoggedIn, $config, $user);
|
||||||
|
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
|
||||||
|
$limit = $config->itemsPerPage;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
$ticks = iterator_to_array(stream_ticks($limit, $offset));
|
||||||
|
|
||||||
|
$vars = [
|
||||||
|
'isLoggedIn' => $isLoggedIn,
|
||||||
|
'config' => $config,
|
||||||
|
'user' => $user,
|
||||||
|
'ticks' => $ticks,
|
||||||
|
];
|
||||||
|
|
||||||
|
echo render_template(TEMPLATES_DIR . "/home.php", $vars);
|
||||||
});
|
});
|
||||||
|
//isset($_SESSION['user_id'])
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
confirm_setup();
|
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
|
||||||
require LIB_DIR . '/session.php';
|
|
||||||
|
|
||||||
$config = Config::load();
|
|
||||||
$_SESSION = [];
|
|
||||||
session_destroy();
|
|
||||||
|
|
||||||
header('Location: ' . $config->basePath);
|
|
||||||
exit;
|
|
@ -1,12 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require LIB_DIR . '/session.php';
|
#require LIB_DIR . '/session.php';
|
||||||
require LIB_DIR . '/ticks.php';
|
#require LIB_DIR . '/ticks.php';
|
||||||
require LIB_DIR . '/util.php';
|
#require LIB_DIR . '/util.php';
|
||||||
|
|
||||||
|
|
||||||
// ticks must be sent via POST
|
// ticks must be sent via POST
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require LIB_DIR . '/session.php';
|
#require LIB_DIR . '/session.php';
|
||||||
|
|
||||||
if (!$isLoggedIn){
|
if (!$isLoggedIn){
|
||||||
header('Location: ' . $config->basePath . 'login.php');
|
header('Location: ' . $config->basePath . 'login.php');
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../bootstrap.php';
|
#require_once __DIR__ . '/../../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require_once LIB_DIR . '/ticks.php';
|
#require_once LIB_DIR . '/ticks.php';
|
||||||
|
|
||||||
$config = Config::load();
|
$config = Config::load();
|
||||||
$ticks = iterator_to_array(stream_ticks($config->itemsPerPage));
|
$ticks = iterator_to_array(stream_ticks($config->itemsPerPage));
|
@ -1,31 +1,15 @@
|
|||||||
<?php
|
<?php /** @var bool $isLoggedIn */ ?>
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
<?php /** @var Config $config */ ?>
|
||||||
|
<?php /** @var User $user */ ?>
|
||||||
|
<?php /** @var array $ticks */ ?>
|
||||||
|
|
||||||
confirm_setup();
|
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
|
||||||
require_once CLASSES_DIR . '/User.php';
|
|
||||||
require LIB_DIR . '/session.php';
|
|
||||||
require LIB_DIR . '/ticks.php';
|
|
||||||
require LIB_DIR . '/util.php';
|
|
||||||
|
|
||||||
$config = Config::load();
|
|
||||||
// I can get away with this before login because there's only one user.
|
|
||||||
$user = User::load();
|
|
||||||
|
|
||||||
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
|
|
||||||
$limit = $config->itemsPerPage;
|
|
||||||
$offset = ($page - 1) * $limit;
|
|
||||||
|
|
||||||
$ticks = iterator_to_array(stream_ticks($limit, $offset));
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title><?= $config->siteTitle ?></title>
|
<title><?= $config->siteTitle ?></title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="<?= htmlspecialchars($config->basePath) ?>css/tkr.css?v=<?= time() ?>">
|
<link rel="stylesheet" href="<?= htmlspecialchars($config->basePath) ?>/css/tkr.css?v=<?= time() ?>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="home-navbar">
|
<div class="home-navbar">
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require LIB_DIR . '/session.php';
|
#require LIB_DIR . '/session.php';
|
||||||
|
|
||||||
$config = Config::load();
|
$config = Config::load();
|
||||||
|
|
14
src/templates/logout.php
Normal file
14
src/templates/logout.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
|
#confirm_setup();
|
||||||
|
|
||||||
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
|
#require LIB_DIR . '/session.php';
|
||||||
|
|
||||||
|
$config = Config::load();
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
header('Location: ' . $config->basePath);
|
||||||
|
exit;
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../bootstrap.php';
|
#require_once __DIR__ . '/../../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require_once LIB_DIR . '/ticks.php';
|
#require_once LIB_DIR . '/ticks.php';
|
||||||
|
|
||||||
$config = Config::load();
|
$config = Config::load();
|
||||||
$ticks = iterator_to_array(stream_ticks($config->itemsPerPage));
|
$ticks = iterator_to_array(stream_ticks($config->itemsPerPage));
|
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require_once CLASSES_DIR . '/Config.php';
|
#require_once CLASSES_DIR . '/Config.php';
|
||||||
require LIB_DIR . '/session.php';
|
#require LIB_DIR . '/session.php';
|
||||||
require LIB_DIR . '/mood.php';
|
#require LIB_DIR . '/mood.php';
|
||||||
|
|
||||||
|
|
||||||
// get the config
|
// get the config
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
// If we got past confirm_setup(), then setup isn't complete.
|
// If we got past confirm_setup(), then setup isn't complete.
|
||||||
$db = get_db();
|
$db = get_db();
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
#require_once __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
confirm_setup();
|
#confirm_setup();
|
||||||
|
|
||||||
require LIB_DIR . '/util.php';
|
#require LIB_DIR . '/util.php';
|
||||||
|
|
||||||
$path = $_GET['path'] ?? '';
|
$path = $_GET['path'] ?? '';
|
||||||
$parts = explode('/', $path);
|
$parts = explode('/', $path);
|
Loading…
x
Reference in New Issue
Block a user