Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
344786ee95 | |||
892110703b | |||
2f76c1ae35 | |||
4aebef12c8 | |||
2021ada52b | |||
caca552cae | |||
8f462953b7 | |||
20d01d0d4c | |||
7b02960b46 | |||
bedf615ad3 | |||
5d2281b713 | |||
7e877465a6 | |||
7360c279ae | |||
641fdb17c5 | |||
45e10dcacd | |||
28513d367d | |||
0c41ca9b65 | |||
d183c4c1e0 | |||
f76bbfb27c | |||
aca652c22e | |||
0d4b62ffb9 | |||
7cd3071905 | |||
77b60d34f6 | |||
b858af3b55 | |||
937e4581ba | |||
f19f59d53a | |||
0c96ba6c90 | |||
713a44ad98 | |||
ea40fc8e38 | |||
8190b5ed71 | |||
dca2556bca | |||
90fd87d042 | |||
9447e76815 | |||
e64fbc0678 |
29
README.md
29
README.md
@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Novaconium PHP: A PHP Framework Built from the Past
|
# Novaconium PHP: A PHP Framework Built from the Past
|
||||||
|
|
||||||
@ -6,15 +6,30 @@ NovaconiumPHP is a high-performance PHP framework designed with inspiration from
|
|||||||
|
|
||||||
Pronounced: Noh-vah-koh-nee-um
|
Pronounced: Noh-vah-koh-nee-um
|
||||||
|
|
||||||
|
Packagist: https://packagist.org/packages/4lt/novaconium
|
||||||
|
Master Repo: https://git.4lt.ca/4lt/novaconium
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Installation
|
Novaconium is heavly influenced by docker, but you can use composer outside of docker.
|
||||||
|
You can [learn more about how novaconium works with composer](https://git.4lt.ca/4lt/novaconium/src/branch/master/docs/Install-Composer-On-Debian.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir project_name;
|
PROJECTNAME=novaproject;
|
||||||
cd project_name;
|
mkdir -p $PROJECTNAME/novaconium;
|
||||||
mkdir -p App/controllers App/templates App/views public
|
cd $PROJECTNAME;
|
||||||
touch App/controllers/404.php App/controllers/index.php App/views/index.html.twig public/index.php public/.htaccess App/routes.php
|
|
||||||
|
|
||||||
composer require 4lt/novaconium
|
docker run --rm --interactive --tty --volume ./novaconium/:/app composer:latest require 4lt/novaconium;
|
||||||
|
|
||||||
|
cp -R novaconium/vendor/4lt/novaconium/skeleton/. .;
|
||||||
|
|
||||||
|
# Edit .env
|
||||||
|
# Edit novaconium/App/config.php
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
* [Novaconiumm Official Repo](https://git.4lt.ca/4lt/novaconium)
|
||||||
|
* [CORXN Apache and PHP Container for Novaconium](https://git.4lt.ca/4lt/CORXN)
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
<svg width="100%" height="200" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="100%" height="100%" fill="black"/>
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="star-gradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
|
|
||||||
<stop offset="0%" style="stop-color: white; stop-opacity: 1" />
|
|
||||||
<stop offset="100%" style="stop-color: white; stop-opacity: 0" />
|
|
||||||
</radialGradient>
|
|
||||||
<g id="star">
|
|
||||||
<circle cx="0" cy="0" r="2" fill="white" />
|
|
||||||
</g>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Lots of stars moving outward slower -->
|
|
||||||
<g>
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-300,-150) scale(1)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(300,-150) scale(1)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-300,150) scale(1)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(300,150) scale(1)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-150,-75) scale(0.5)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(150,-75) scale(0.5)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-150,75) scale(0.5)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(150,75) scale(0.5)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-75,-37) scale(0.7)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(75,-37) scale(0.7)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-75,37) scale(0.7)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(75,37) scale(0.7)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-350,-175) scale(0.4)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(350,-175) scale(0.4)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(-350,175) scale(0.4)" />
|
|
||||||
<use href="#star" x="50%" y="50%" transform="translate(350,175) scale(0.4)" />
|
|
||||||
<animateTransform attributeName="transform" type="scale" from="1" to="10" dur="2s" repeatCount="indefinite"/>
|
|
||||||
<animate attributeName="opacity" from="1" to="0" dur="2s" repeatCount="indefinite" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Centered Title -->
|
|
||||||
<text x="50%" y="50%" fill="white" font-size="40" text-anchor="middle" font-family="Arial" dy=".3em">
|
|
||||||
Novaconium PHP
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.2 KiB |
BIN
_assets/novaconium-logo.png
Normal file
BIN
_assets/novaconium-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "4lt/novaconium",
|
"name": "4lt/novaconium",
|
||||||
"description": "A high-performance PHP framework built from the past.",
|
"description": "A high-performance PHP framework built from the past.",
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
@ -17,7 +16,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"twig/twig": "*"
|
"twig/twig": "*",
|
||||||
|
"nickyeoman/php-validation-class": "^5.0"
|
||||||
},
|
},
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"extra": {
|
"extra": {
|
||||||
@ -26,4 +26,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
config/routes.php
Normal file
44
config/routes.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
$framework_routes = [
|
||||||
|
'/novaconium' => [
|
||||||
|
'get' => 'NOVACONIUM/init'
|
||||||
|
],
|
||||||
|
'/novaconium/create_admin' => [
|
||||||
|
'post' => 'NOVACONIUM/create_admin'
|
||||||
|
],
|
||||||
|
'/novaconium/login' => [
|
||||||
|
'post' => 'NOVACONIUM/authenticate',
|
||||||
|
'get' => 'NOVACONIUM/login'
|
||||||
|
],
|
||||||
|
'/novaconium/dashboard' => [
|
||||||
|
'get' => 'NOVACONIUM/dashboard'
|
||||||
|
],
|
||||||
|
'/novaconium/pages' => [
|
||||||
|
'get' => 'NOVACONIUM/pages'
|
||||||
|
],
|
||||||
|
'/novaconium/page/edit/{id}' => [
|
||||||
|
'get' => 'NOVACONIUM/editpage'
|
||||||
|
],
|
||||||
|
'/novaconium/page/create' => [
|
||||||
|
'get' => 'NOVACONIUM/editpage'
|
||||||
|
],
|
||||||
|
'/novaconium/savePage' => [
|
||||||
|
'post' => 'NOVACONIUM/savepage'
|
||||||
|
],
|
||||||
|
'/novaconium/messages' => [
|
||||||
|
'get' => 'NOVACONIUM/messages'
|
||||||
|
],
|
||||||
|
'/novaconium/messages/delete/{id}' => [
|
||||||
|
'get' => 'NOVACONIUM/message_delete'
|
||||||
|
],
|
||||||
|
'/novaconium/messages/edit/{id}' => [
|
||||||
|
'get' => 'NOVACONIUM/message_edit'
|
||||||
|
],
|
||||||
|
'/novaconium/message_save' => [
|
||||||
|
'post' => 'NOVACONIUM/message_save'
|
||||||
|
],
|
||||||
|
'/novaconium/logout' => [
|
||||||
|
'post' => 'NOVACONIUM/logout',
|
||||||
|
'get' => 'NOVACONIUM/logout'
|
||||||
|
]
|
||||||
|
];
|
70
controllers/authenticate.php
Normal file
70
controllers/authenticate.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
|
$url_success = '/novaconium/dashboard';
|
||||||
|
$url_fail = '/novaconium/login';
|
||||||
|
|
||||||
|
|
||||||
|
// Don't go further if already logged in
|
||||||
|
if ( !empty($session->get('username')) ) {
|
||||||
|
$redirect->url($url_success);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure Session Token is correct
|
||||||
|
if ($session->get('token') != $post->get('token')) {
|
||||||
|
$messages->addMessage('error', "Invalid Session.");
|
||||||
|
$log->error("Login Authentication - Invalid Session Token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Username
|
||||||
|
$rawUsername = $post->get('username', null);
|
||||||
|
$cleanUsername = $v->clean($rawUsername); // Clean the input
|
||||||
|
$username = strtolower($cleanUsername); // Convert to lowercase
|
||||||
|
if (!$username) {
|
||||||
|
$messages->addMessage('error', "No Username given.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Password
|
||||||
|
$password = $v->clean($post->get('password', null));
|
||||||
|
if ( empty($password) ) {
|
||||||
|
$messages->addMessage('error', "Password Empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************************************************
|
||||||
|
* Query Database
|
||||||
|
************************************************************************************************************/
|
||||||
|
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
$query = "SELECT id, username, email, password, blocked FROM users WHERE username = ? OR email = ?";
|
||||||
|
$matched = $db->getRow($query, [$username, $username]);
|
||||||
|
if (empty($matched)) {
|
||||||
|
$messages->addMessage('error', "User or Password incorrect.");
|
||||||
|
$log->warning("Login Authentication - Login Error, user doesn't exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
// Re-apply pepper
|
||||||
|
$peppered = hash_hmac('sha3-512', $password, $config['secure_key']);
|
||||||
|
|
||||||
|
// Verify hashed password
|
||||||
|
if (!password_verify($peppered, $matched['password'])) {
|
||||||
|
$messages->addMessage('error', "User or Password incorrect.");
|
||||||
|
$log->warning("Login Authentication - Login Error, password wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Login or Redirect
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
$query = "SELECT groupName FROM user_groups WHERE user_id = ?";
|
||||||
|
$groups = $db->getRow($query, [$matched['id']]);
|
||||||
|
$session->set('username', $cleanUsername);
|
||||||
|
$session->set('group', $groups['groupName']);
|
||||||
|
$redirect->url($url_success);
|
||||||
|
$log->info("Login Authentication - Login Success");
|
||||||
|
} else {
|
||||||
|
$redirect->url($url_fail);
|
||||||
|
}
|
58
controllers/create_admin.php
Normal file
58
controllers/create_admin.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
|
||||||
|
$validate = new Validation\Validate();
|
||||||
|
$valid = true;
|
||||||
|
$p = $post->all();
|
||||||
|
|
||||||
|
// Check secure key
|
||||||
|
if (empty($p['secure_key']) || $p['secure_key'] !== $config['secure_key']) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Username
|
||||||
|
$name = $validate->clean($p['username']);
|
||||||
|
if (!$validate->minLength($name, 1)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email
|
||||||
|
if (empty($p['email'])) {
|
||||||
|
$valid = false;
|
||||||
|
} elseif (!$validate->isEmail($p['email'])) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password
|
||||||
|
if (empty($p['password'])) {
|
||||||
|
$valid = false;
|
||||||
|
} else {
|
||||||
|
// Use pepper + Argon2id
|
||||||
|
$peppered = hash_hmac('sha3-512', $p['password'], $config['secure_key']);
|
||||||
|
$hashed_password = password_hash($peppered, PASSWORD_ARGON2ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
// Insert user
|
||||||
|
$query = <<<EOSQL
|
||||||
|
INSERT INTO `users`
|
||||||
|
(`username`, `password`, `email`, `validate`, `confirmationToken`, `reset`, `created`, `updated`, `confirmed`, `blocked`)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, NULL, NULL, NULL, NOW(), NOW(), 1, 0);
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$params = [$name, $hashed_password, $p['email']];
|
||||||
|
$db->query($query, $params);
|
||||||
|
$userid = $db->lastid();
|
||||||
|
|
||||||
|
// Assign admin group
|
||||||
|
$groupInsertQuery = <<<EOSQL
|
||||||
|
INSERT INTO `user_groups` (`user_id`, `groupName`) VALUES (?, ?);
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($groupInsertQuery, [$userid, 'admin']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always redirect at end
|
||||||
|
$redirect->url('/novaconium');
|
13
controllers/dashboard.php
Normal file
13
controllers/dashboard.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'title' => 'Novaconium Dashboard Page',
|
||||||
|
'pageclass' => 'novaconium'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
view('@novacore/dashboard', $data);
|
73
controllers/editpage.php
Normal file
73
controllers/editpage.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'title' => 'Novaconium Edit Page',
|
||||||
|
'pageclass' => 'novaconium'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check if logged in
|
||||||
|
if (empty($session->get('username'))) {
|
||||||
|
$messages->error('You are not logged in');
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get page ID from router parameters
|
||||||
|
$pageid = $router->parameters['id'] ?? null;
|
||||||
|
|
||||||
|
if (!empty($pageid)) {
|
||||||
|
// Existing page: fetch from database
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
heading,
|
||||||
|
description,
|
||||||
|
keywords,
|
||||||
|
author,
|
||||||
|
slug,
|
||||||
|
path,
|
||||||
|
intro,
|
||||||
|
body,
|
||||||
|
notes,
|
||||||
|
draft,
|
||||||
|
changefreq,
|
||||||
|
priority,
|
||||||
|
created,
|
||||||
|
updated
|
||||||
|
FROM pages
|
||||||
|
WHERE id = ?
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$data['rows'] = $db->getRow($query, [$pageid]);
|
||||||
|
|
||||||
|
// If no row is found, treat as new page
|
||||||
|
if (!$data['rows']) {
|
||||||
|
$pageid = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($pageid)) {
|
||||||
|
// New page: set default values for all fields
|
||||||
|
$data['rows'] = [
|
||||||
|
'id' => '',
|
||||||
|
'title' => '',
|
||||||
|
'heading' => '',
|
||||||
|
'description' => '',
|
||||||
|
'keywords' => '',
|
||||||
|
'author' => $session->get('username') ?? '',
|
||||||
|
'slug' => '',
|
||||||
|
'path' => '',
|
||||||
|
'intro' => '',
|
||||||
|
'body' => '',
|
||||||
|
'notes' => '',
|
||||||
|
'draft' => 0,
|
||||||
|
'changefreq' => 'monthly',
|
||||||
|
'priority' => 0.0,
|
||||||
|
'created' => date('Y-m-d H:i:s'),
|
||||||
|
'updated' => date('Y-m-d H:i:s')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the edit page view
|
||||||
|
view('@novacore/editpage', $data);
|
128
controllers/init.php
Normal file
128
controllers/init.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'secure_key' => false,
|
||||||
|
'gen_key' => NULL,
|
||||||
|
'users_created' => false,
|
||||||
|
'empty_users' => false,
|
||||||
|
'show_login' => false,
|
||||||
|
'token' => $session->get('token'),
|
||||||
|
'title' => 'Novaconium Admin'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check if SECURE KEY is Set in
|
||||||
|
if ($config['secure_key'] !== null && strlen($config['secure_key']) === 64) {
|
||||||
|
$data['secure_key'] = true;
|
||||||
|
} else {
|
||||||
|
$data['gen_key'] = substr(bin2hex(random_bytes(32)), 0, 64);
|
||||||
|
$log->warn('secure_key not detected');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user table exists
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'users';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(30) NOT NULL,
|
||||||
|
`password` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
`validate` varchar(32) DEFAULT NULL,
|
||||||
|
`confirmationToken` varchar(255) DEFAULT NULL,
|
||||||
|
`reset` varchar(32) DEFAULT NULL,
|
||||||
|
`created` datetime NOT NULL,
|
||||||
|
`updated` datetime DEFAULT NULL,
|
||||||
|
`confirmed` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`blocked` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$data['users_created'] = true;
|
||||||
|
$log->info('Users Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Usergroup
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'user_groups';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `user_groups` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`groupName` VARCHAR(40) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$log->info('User_groups Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Pages Table
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'pages';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `pages` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) NOT NULL,
|
||||||
|
`heading` varchar(255) NOT NULL,
|
||||||
|
`description` varchar(255) NOT NULL,
|
||||||
|
`keywords` varchar(255) NOT NULL,
|
||||||
|
`author` varchar(255) NOT NULL,
|
||||||
|
`slug` varchar(255) NOT NULL,
|
||||||
|
`path` varchar(255) DEFAULT NULL,
|
||||||
|
`intro` text DEFAULT NULL,
|
||||||
|
`body` text DEFAULT NULL,
|
||||||
|
`notes` text DEFAULT NULL,
|
||||||
|
`created` datetime NOT NULL,
|
||||||
|
`updated` datetime DEFAULT NULL,
|
||||||
|
`draft` tinyint(1) NOT NULL DEFAULT 1,
|
||||||
|
`changefreq` varchar(7) NOT NULL DEFAULT 'monthly',
|
||||||
|
`priority` float(4,1) NOT NULL DEFAULT 0.0
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$log->info('Pages Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a user exists
|
||||||
|
$result = $db->query("SELECT COUNT(*) as total FROM users");
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
|
|
||||||
|
if ($row['total'] < 1) {
|
||||||
|
$data['empty_users'] = true;
|
||||||
|
} else {
|
||||||
|
$log->info('Init Run complete, all sql tables exist with a user.');
|
||||||
|
// Everything is working, send them to login page
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
view('@novacore/init', $data);
|
9
controllers/login.php
Normal file
9
controllers/login.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
$data['title'] = 'Novaconium Login Page';
|
||||||
|
|
||||||
|
// Don't come here if logged in
|
||||||
|
if ($session->get('username')) {
|
||||||
|
$redirect->url('/novaconium/dashboard');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
view('@novacore/login');
|
5
controllers/logout.php
Normal file
5
controllers/logout.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
$session->kill();
|
||||||
|
$log->info("Logout - Logout Success - " . $_SERVER['REMOTE_ADDR']);
|
||||||
|
$redirect->url('/');
|
||||||
|
makeitso();
|
15
controllers/message_delete.php
Normal file
15
controllers/message_delete.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
$messageid = $router->parameters['id'];
|
||||||
|
$query="DELETE FROM contactForm WHERE `contactForm`.`id` = ?";
|
||||||
|
$db->query($query, [$messageid]);
|
||||||
|
|
||||||
|
$redirect->url('/novaconium/messages');
|
||||||
|
$messages->notice("Removed Message $messageid");
|
||||||
|
makeitso();
|
19
controllers/message_edit.php
Normal file
19
controllers/message_edit.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'title' => 'Novaconium Message Page',
|
||||||
|
'pageclass' => 'novaconium'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
$messageid = $router->parameters['id'];
|
||||||
|
$query = "SELECT id, name, email, message, created, unread FROM contactForm WHERE id = '$messageid'";
|
||||||
|
|
||||||
|
$data['themessage'] = $db->getRow($query);
|
||||||
|
|
||||||
|
view('@novacore/editmessage', $data);
|
57
controllers/message_save.php
Normal file
57
controllers/message_save.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
|
||||||
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
|
$url_success = '/novaconium/messages';
|
||||||
|
$url_error = '/novaconium/messages/edit/' . $post->get('id'); // Redirect back to the message edit form on error
|
||||||
|
|
||||||
|
// Check if logged in
|
||||||
|
if (empty($session->get('username'))) {
|
||||||
|
$messages->error('You are not logged in');
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check CSRF token
|
||||||
|
if ($session->get('token') != $post->get('token')) {
|
||||||
|
$messages->error('Invalid token');
|
||||||
|
$redirect->url($url_success);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get POST data
|
||||||
|
$id = $post->get('id');
|
||||||
|
$name = $post->get('name');
|
||||||
|
$email = $post->get('email');
|
||||||
|
$message = $post->get('message');
|
||||||
|
$unread = !empty($post->get('unread')) ? 1 : 0;
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (empty($id) || empty($message) || empty($email)) {
|
||||||
|
$messages->error('One of the required fields was empty.');
|
||||||
|
$redirect->url($url_error);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Prepare update query
|
||||||
|
$query = "UPDATE `contactForm`
|
||||||
|
SET `name` = ?, `email` = ?, `message` = ?, `unread` = ?
|
||||||
|
WHERE `id` = ?";
|
||||||
|
|
||||||
|
$params = [$name, $email, $message, $unread, $id];
|
||||||
|
|
||||||
|
$db->query($query, $params);
|
||||||
|
|
||||||
|
$messages->notice('Message updated successfully');
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$messages->error('Error updating message: ' . $e->getMessage());
|
||||||
|
$redirect->url($url_error);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to success page
|
||||||
|
$redirect->url($url_success);
|
21
controllers/messages.php
Normal file
21
controllers/messages.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'title' => 'Novaconium Messages',
|
||||||
|
'pageclass' => 'novaconium'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the pages
|
||||||
|
$query = "SELECT id, name, email, LEFT(message, 40) AS message, created, unread FROM contactForm";
|
||||||
|
|
||||||
|
$matched = $db->getRows($query);
|
||||||
|
|
||||||
|
$data['messages'] = $matched;
|
||||||
|
|
||||||
|
view('@novacore/messages', $data);
|
20
controllers/pages.php
Normal file
20
controllers/pages.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'title' => 'Novaconium Pages',
|
||||||
|
'pageclass' => 'novaconium'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the pages
|
||||||
|
$query = "SELECT id, title, created, updated, draft FROM pages";
|
||||||
|
$matched = $db->getRows($query);
|
||||||
|
|
||||||
|
$data['pages'] = $matched;
|
||||||
|
|
||||||
|
view('@novacore/pages', $data);
|
83
controllers/savepage.php
Normal file
83
controllers/savepage.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
|
$url_error = '/novaconium/page/edit/' . $post->get('id'); // fallback for errors
|
||||||
|
|
||||||
|
// Check login
|
||||||
|
if (empty($session->get('username'))) {
|
||||||
|
$messages->error('You are not logged in');
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check token
|
||||||
|
if ($session->get('token') != $post->get('token')) {
|
||||||
|
$messages->error('Invalid Token');
|
||||||
|
$redirect->url('/novaconium/pages');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather POST data
|
||||||
|
$id = $post->get('id');
|
||||||
|
$title = $_POST['title'] ?? '';
|
||||||
|
$heading = $_POST['heading'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$keywords = $_POST['keywords'] ?? '';
|
||||||
|
$author = $_POST['author'] ?? '';
|
||||||
|
$slug = $_POST['slug'] ?? '';
|
||||||
|
$path = $_POST['path'] ?? null;
|
||||||
|
$intro = $_POST['intro'] ?? '';
|
||||||
|
$body = $_POST['body'] ?? '';
|
||||||
|
$notes = $_POST['notes'] ?? '';
|
||||||
|
$draft = !empty($post->get('draft')) ? 1 : 0;
|
||||||
|
$changefreq = $_POST['changefreq'] ?? 'monthly';
|
||||||
|
$priority = $_POST['priority'] ?? 0.0;
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (empty($title) || empty($slug) || empty($body)) {
|
||||||
|
$messages->error('Title, Slug, and Body are required.');
|
||||||
|
$redirect->url($url_error);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!empty($id)) {
|
||||||
|
// Update existing page
|
||||||
|
$query = "UPDATE `pages` SET
|
||||||
|
`title` = ?, `heading` = ?, `description` = ?, `keywords` = ?, `author` = ?,
|
||||||
|
`slug` = ?, `path` = ?, `intro` = ?, `body` = ?, `notes` = ?,
|
||||||
|
`draft` = ?, `changefreq` = ?, `priority` = ?, `updated` = NOW()
|
||||||
|
WHERE `id` = ?";
|
||||||
|
$params = [
|
||||||
|
$title, $heading, $description, $keywords, $author,
|
||||||
|
$slug, $path, $intro, $body, $notes,
|
||||||
|
$draft, $changefreq, $priority, $id
|
||||||
|
];
|
||||||
|
$db->query($query, $params);
|
||||||
|
$messages->notice('Page Updated');
|
||||||
|
} else {
|
||||||
|
// Create new page
|
||||||
|
$query = "INSERT INTO `pages`
|
||||||
|
(`title`, `heading`, `description`, `keywords`, `author`,
|
||||||
|
`slug`, `path`, `intro`, `body`, `notes`,
|
||||||
|
`draft`, `changefreq`, `priority`, `created`)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
|
||||||
|
$params = [
|
||||||
|
$title, $heading, $description, $keywords, $author,
|
||||||
|
$slug, $path, $intro, $body, $notes,
|
||||||
|
$draft, $changefreq, $priority
|
||||||
|
];
|
||||||
|
$db->query($query, $params);
|
||||||
|
$id = $db->lastid; // Get new page ID
|
||||||
|
$messages->notice('Page Created');
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$messages->error($e->getMessage());
|
||||||
|
$redirect->url($url_error);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to edit page
|
||||||
|
$redirect->url('/novaconium/page/edit/' . $id);
|
18
docs/Composer.md
Normal file
18
docs/Composer.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# PHP Composer Cheatsheet
|
||||||
|
|
||||||
|
Install novaconium with composer: ```composer require 4lt/novaconium```
|
||||||
|
|
||||||
|
Install novaconium with composer in docker: ```docker run --rm --interactive --tty --volume $PWD:/app composer:latest require 4lt/novaconium```
|
||||||
|
|
||||||
|
Update novaconium with composer in docker: ```docker run --rm --interactive --tty --volume $PWD:/app composer:latest update```
|
||||||
|
|
||||||
|
## Install Composer natively on Debian
|
||||||
|
|
||||||
|
Assuming you have nala installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nala install curl php-cli php-mbstring git unzip
|
||||||
|
curl -sS https://getcomposer.org/installer -o composer-setup.php
|
||||||
|
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
|
||||||
|
rm composer-setup.php
|
||||||
|
```
|
19
docs/Logs.md
Normal file
19
docs/Logs.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Logging
|
||||||
|
|
||||||
|
You can use the logging class to output to a file.
|
||||||
|
|
||||||
|
|
||||||
|
use ```$log->info(The Message');```1
|
||||||
|
|
||||||
|
Logging levels are:
|
||||||
|
```
|
||||||
|
'DEBUG' => 0,
|
||||||
|
'INFO' => 1,
|
||||||
|
'WARNING' => 2,
|
||||||
|
'ERROR' => 3,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
It's recommended that production is set to ERROR.
|
||||||
|
You set the log level in /App/config.php under 'loglevel' => 'ERROR'
|
||||||
|
If you are using CORXN a health check is run every 30 seconds which would fill the log file with info.
|
3
docs/Messages.md
Normal file
3
docs/Messages.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Messages
|
||||||
|
|
||||||
|
Messages is $messages.
|
5
docs/Post.md
Normal file
5
docs/Post.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Post
|
||||||
|
|
||||||
|
There is a post class.
|
||||||
|
It cleans the post.
|
||||||
|
You can access it with $post.
|
6
docs/Redirect.md
Normal file
6
docs/Redirect.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Redirect
|
||||||
|
|
||||||
|
How to use redirect class.
|
||||||
|
|
||||||
|
$redirect->url;
|
||||||
|
it's called on every page, if you set it more than once the last one is used.
|
5
docs/Session.md
Normal file
5
docs/Session.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Sessions
|
||||||
|
|
||||||
|
There is a sessions handler built into Novaconium.
|
||||||
|
|
||||||
|
$session
|
7
docs/StyleSheets-sass.md
Normal file
7
docs/StyleSheets-sass.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Style Sheets
|
||||||
|
|
||||||
|
The idea is to use sass to generate only what you need for style sheets.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo docker run --rm -v $(pwd):/usr/src/app sass-container sass sass/project.sass public/css/main.css
|
||||||
|
```
|
21
docs/Twig-Views.md
Normal file
21
docs/Twig-Views.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Twig
|
||||||
|
|
||||||
|
## Overrides
|
||||||
|
|
||||||
|
You can override twig templates by creating the same file in the templates directory.
|
||||||
|
|
||||||
|
## Calling View
|
||||||
|
|
||||||
|
There is a $data that the system uses to store arrays for twig you can save to this array:
|
||||||
|
|
||||||
|
```
|
||||||
|
$data['newinfo'] = 'stuff';
|
||||||
|
view('templatename');
|
||||||
|
```
|
||||||
|
and that will automotically go to twig.
|
||||||
|
or you can create a new array and pass it in:
|
||||||
|
|
||||||
|
```
|
||||||
|
$anotherArr['newinfo'] = 'stuff';
|
||||||
|
view('templatename',$anotherArr);
|
||||||
|
```
|
9
docs/docker.md
Normal file
9
docs/docker.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Docker Cheatsheet (for Novaconium)
|
||||||
|
|
||||||
|
## Sample Docker Compose File
|
||||||
|
|
||||||
|
See the skeleton directory for an example docker setup.
|
||||||
|
|
||||||
|
## Start Docker
|
||||||
|
|
||||||
|
```docker compose up -d```
|
@ -1 +0,0 @@
|
|||||||
<h1>This is 404</h1>
|
|
@ -1,2 +0,0 @@
|
|||||||
<?php
|
|
||||||
echo $twig->render('index.html.twig');
|
|
@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
define('BASEPATH', dirname(__DIR__, 1));
|
|
||||||
require_once(BASEPATH . '/vendor/4lt/novaconium/src/bootstrap.php');
|
|
||||||
?>
|
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
$routes = [
|
|
||||||
'/about' => [
|
|
||||||
'file' => 'about'
|
|
||||||
],
|
|
||||||
'/' => [
|
|
||||||
'file' => 'index'
|
|
||||||
]
|
|
||||||
];
|
|
@ -1,7 +0,0 @@
|
|||||||
{% extends '@nytwig/master.html.twig' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>This is twig</h1>
|
|
||||||
<p>Content Here</p>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
9
services/Auth.php
Normal file
9
services/Auth.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
class Auth
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
2
skeleton/.env
Normal file
2
skeleton/.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
MYSQL_ROOT_PASSWORD=random
|
||||||
|
MYSQL_PASSWORD=random
|
3
skeleton/.gitignore
vendored
Normal file
3
skeleton/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
data/
|
||||||
|
novaconium/vendor/
|
||||||
|
novaconium/logs/
|
56
skeleton/docker-compose.yml
Normal file
56
skeleton/docker-compose.yml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Sample Docker Compose
|
||||||
|
services:
|
||||||
|
corxn:
|
||||||
|
image: 4lights/corxn:6.0.0
|
||||||
|
ports:
|
||||||
|
- "8000:80"
|
||||||
|
volumes:
|
||||||
|
- ./novaconium:/data
|
||||||
|
- ./data/logs:/var/log/apache2 # Optional Logs
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- proxy
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:latest
|
||||||
|
container_name: mariadb
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||||
|
MYSQL_DATABASE: novadb
|
||||||
|
MYSQL_USER: novaconium
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./data/db:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin/phpmyadmin:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8001:80"
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
environment:
|
||||||
|
- PMA_ARBITRARY=-1
|
||||||
|
- PMA_HOST=mariadb
|
||||||
|
- PMA_USER=root
|
||||||
|
- PMA_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||||
|
- UPLOAD_LIMIT=200M
|
||||||
|
volumes:
|
||||||
|
- "/etc/timezone:/etc/timezone:ro"
|
||||||
|
- "/etc/localtime:/etc/localtime:ro"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
14
skeleton/novaconium/App/config.php
Normal file
14
skeleton/novaconium/App/config.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
$config = [
|
||||||
|
'database' => [
|
||||||
|
'host' => 'mariadb',
|
||||||
|
'name' => 'novadb',
|
||||||
|
'user' => 'novaconium',
|
||||||
|
'pass' => '',
|
||||||
|
'port' => 3306
|
||||||
|
],
|
||||||
|
'base_url' => 'http://localhost:8000',
|
||||||
|
'secure_key' => '', //64 alphanumeric characters
|
||||||
|
'logfile' => '/logs/novaconium.log',
|
||||||
|
'loglevel' => 'ERROR' // 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'NONE'
|
||||||
|
];
|
15
skeleton/novaconium/App/controllers/404.php
Normal file
15
skeleton/novaconium/App/controllers/404.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Define our status code and message
|
||||||
|
$status_code = 404;
|
||||||
|
$status_message = 'The requested resource could not be found.';
|
||||||
|
|
||||||
|
// Set the HTTP response code and message
|
||||||
|
http_response_code($status_code);
|
||||||
|
header("Content-Type: text/html");
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>Error 404 Resource Not found</h1>
|
||||||
|
<p><?php echo $status_message; ?></p>
|
||||||
|
<p style="font-size:10px; margin-top:60px">Novaconium Default 404 page.</p>
|
||||||
|
|
2
skeleton/novaconium/App/controllers/index.php
Normal file
2
skeleton/novaconium/App/controllers/index.php
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?php
|
||||||
|
view('index');
|
6
skeleton/novaconium/App/routes.php
Normal file
6
skeleton/novaconium/App/routes.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
$routes = [
|
||||||
|
'/' => [
|
||||||
|
'get' => 'index'
|
||||||
|
]
|
||||||
|
];
|
1
skeleton/novaconium/App/templates/override.html.twig
Normal file
1
skeleton/novaconium/App/templates/override.html.twig
Normal file
@ -0,0 +1 @@
|
|||||||
|
{# Overrides go here #}
|
21
skeleton/novaconium/App/views/index.html.twig
Normal file
21
skeleton/novaconium/App/views/index.html.twig
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<img src="https://git.4lt.ca/4lt/novaconium/media/branch/master/_assets/novaconium-logo.png" aalt="Novaconium framework logo" />
|
||||||
|
<h2>Minimalist PHP framework</h2>
|
||||||
|
<p>
|
||||||
|
Edit <code>App/routes.php</code> and <code>App/controllers/index.php</code><br>
|
||||||
|
to customize this page.
|
||||||
|
</p>
|
||||||
|
<p>Sign in to the <a href="/novaconium">Administration</a></p>
|
||||||
|
|
||||||
|
<h2>Documentation</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://git.4lt.ca/4lt/novaconium/src/branch/master/docs/StyleSheets-sass.md">Style Sheets</a></li>
|
||||||
|
<li><a href="https://git.4lt.ca/4lt/novaconium/src/branch/master/docs/Twig-Views.md">Twig overrides</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Repository</h2>
|
||||||
|
<p class="small">Visit Source Control Repository for <a href="https://git.4lt.ca/4lt/novaconium">Novaconium</a></p>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,6 +1,4 @@
|
|||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php?_uri=$1 [QSA,L]
|
||||||
RewriteRule ^(.*)$ index.php?_uri=$1 [QSA,L]
|
|
114
skeleton/novaconium/public/css/novaconium.css
Normal file
114
skeleton/novaconium/public/css/novaconium.css
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
body {
|
||||||
|
background-color: #1b1f23;
|
||||||
|
width: 100%;
|
||||||
|
font-family: 'Fira Code', 'Source Code Pro', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page .container {
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
width: 900px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftnav {
|
||||||
|
width: 320px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, #leftnav {
|
||||||
|
border: 1px solid #3b444c;
|
||||||
|
background-color: #14171a;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#leftnav {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftnav li {
|
||||||
|
border-bottom: 1px solid #3b444c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftnav a {
|
||||||
|
display: block;
|
||||||
|
padding: 10px 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftnav a:hover {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftnav li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background-color: #0d1117;
|
||||||
|
color: #c9d1d9;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error, div#debug {
|
||||||
|
border: 1px solid red;
|
||||||
|
padding: 30px;
|
||||||
|
background-color: pink;
|
||||||
|
color: darkred;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.notice {
|
||||||
|
border: 1px solid rgb(31, 119, 13);
|
||||||
|
padding: 30px;
|
||||||
|
background-color: rgb(169, 218, 163);
|
||||||
|
color: rgb(20, 56, 13);
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#debug {
|
||||||
|
margin-top: 100px;
|
||||||
|
margin-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pages-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pages-table th,
|
||||||
|
.pages-table td {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pages-table th {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
6
skeleton/novaconium/public/index.php
Normal file
6
skeleton/novaconium/public/index.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
// error_reporting(E_ALL);
|
||||||
|
// ini_set('display_errors', 1);
|
||||||
|
define('BASEPATH', dirname(__DIR__, 1));
|
||||||
|
require_once(BASEPATH . '/vendor/4lt/novaconium/src/novaconium.php');
|
||||||
|
?>
|
126
src/Database.php
Normal file
126
src/Database.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
|
||||||
|
private $conn;
|
||||||
|
public $lastid;
|
||||||
|
|
||||||
|
public function __construct($dbinfo) {
|
||||||
|
$this->conn = new mysqli($dbinfo['host'], $dbinfo['user'], $dbinfo['pass'], $dbinfo['name']);
|
||||||
|
if ($this->conn->connect_error) {
|
||||||
|
die("Connection failed: " . $this->conn->connect_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query($query, $params = []) {
|
||||||
|
// Clean up pending results to avoid "commands out of sync"
|
||||||
|
while ($this->conn->more_results() && $this->conn->next_result()) {
|
||||||
|
if ($res = $this->conn->use_result()) {
|
||||||
|
$res->free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the SQL statement
|
||||||
|
$stmt = $this->conn->prepare($query);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Query preparation failed: " . $this->conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind parameters if needed
|
||||||
|
if (!empty($params)) {
|
||||||
|
$types = str_repeat('s', count($params)); // Use 's' for all types, or detect types dynamically
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the statement
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
$stmt->close();
|
||||||
|
throw new Exception("Query execution failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save last insert id if it's an INSERT query
|
||||||
|
if (preg_match('/^\s*INSERT/i', $query)) {
|
||||||
|
$this->lastid = $this->conn->insert_id;
|
||||||
|
} else {
|
||||||
|
$this->lastid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide what to return
|
||||||
|
// For SELECT/SHOW etc., return result set
|
||||||
|
if (preg_match('/^\s*(SELECT|SHOW|DESCRIBE|EXPLAIN)/i', $query)) {
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$stmt->close();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For INSERT/UPDATE/DELETE, return success status
|
||||||
|
$success = $stmt->affected_rows;
|
||||||
|
$stmt->close();
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lastid() {
|
||||||
|
return $this->lastid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRow($query, $params = []) {
|
||||||
|
try {
|
||||||
|
// Prepare the SQL statement
|
||||||
|
$stmt = $this->conn->prepare($query);
|
||||||
|
if (!$stmt) {
|
||||||
|
throw new Exception("Query preparation failed: " . $this->conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind parameters
|
||||||
|
if (!empty($params)) {
|
||||||
|
$types = str_repeat('s', count($params)); // You may improve this with actual type detection
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the statement
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
$stmt->close();
|
||||||
|
throw new Exception("Query execution failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get result
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
return $row;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "An error occurred: " . $e->getMessage();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRows($query, $params = []) {
|
||||||
|
$stmt = $this->conn->prepare($query);
|
||||||
|
if (!$stmt) {
|
||||||
|
die("Query preparation failed: " . $this->conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind parameters if provided
|
||||||
|
if (!empty($params)) {
|
||||||
|
$types = str_repeat('s', count($params)); // Assuming all are strings, adjust as needed
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result(); // Requires MySQL Native Driver (mysqlnd)
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
return $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->conn->close();
|
||||||
|
}
|
||||||
|
}
|
48
src/Logger.php
Normal file
48
src/Logger.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
class Logger {
|
||||||
|
protected string $logFile;
|
||||||
|
protected int $logLevelThreshold;
|
||||||
|
|
||||||
|
const LEVELS = [
|
||||||
|
'DEBUG' => 0,
|
||||||
|
'INFO' => 1,
|
||||||
|
'WARNING' => 2,
|
||||||
|
'ERROR' => 3,
|
||||||
|
'NONE' => 999
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(string $logFile, string $minLevel = 'DEBUG') {
|
||||||
|
$this->logFile = $logFile;
|
||||||
|
$minLevel = strtoupper($minLevel);
|
||||||
|
$this->logLevelThreshold = self::LEVELS[$minLevel] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function log(string $level, string $message): void {
|
||||||
|
$level = strtoupper($level);
|
||||||
|
if (!isset(self::LEVELS[$level]) || self::LEVELS[$level] < $this->logLevelThreshold) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = date('Y-m-d H:i:s');
|
||||||
|
$ip = $_SERVER['REMOTE_ADDR'];
|
||||||
|
$logEntry = "[$time] [$ip] [$level] $message" . PHP_EOL;
|
||||||
|
|
||||||
|
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debug(string $msg): void {
|
||||||
|
$this->log('DEBUG', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function info(string $msg): void {
|
||||||
|
$this->log('INFO', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function warning(string $msg): void {
|
||||||
|
$this->log('WARNING', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error(string $msg): void {
|
||||||
|
$this->log('ERROR', $msg);
|
||||||
|
}
|
||||||
|
}
|
87
src/MessageHandler.php
Normal file
87
src/MessageHandler.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class MessageHandler {
|
||||||
|
private $messages = [
|
||||||
|
'error' => [],
|
||||||
|
'warning' => [],
|
||||||
|
'notice' => [],
|
||||||
|
'success' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(array $sessionMessages = [])
|
||||||
|
{
|
||||||
|
// Merge existing session messages into the default structure
|
||||||
|
foreach ($this->messages as $type => $_) {
|
||||||
|
if (isset($sessionMessages[$type]) && is_array($sessionMessages[$type])) {
|
||||||
|
$this->messages[$type] = $sessionMessages[$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a message of a specific type
|
||||||
|
public function addMessage($type, $message) {
|
||||||
|
if (!isset($this->messages[$type])) {
|
||||||
|
throw new Exception("Invalid message type: $type");
|
||||||
|
}
|
||||||
|
$this->messages[$type][] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error($message){
|
||||||
|
$this->addMessage('error', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notice($message){
|
||||||
|
$this->addMessage('notice', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all messages of a specific type
|
||||||
|
public function getMessages($type) {
|
||||||
|
return $this->messages[$type] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all messages of a specific type
|
||||||
|
public function showMessages($type) {
|
||||||
|
$result = $this->messages[$type] ?? [];
|
||||||
|
$this->messages[$type] = []; // Clear messages after showing
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all messages of all types
|
||||||
|
public function getAllMessages() {
|
||||||
|
return $this->messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the count of messages for a specific type
|
||||||
|
public function count($type) {
|
||||||
|
return isset($this->messages[$type]) ? count($this->messages[$type]) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the total count of all messages
|
||||||
|
public function totalCount() {
|
||||||
|
return array_sum(array_map('count', $this->messages));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any messages of a specific type
|
||||||
|
public function hasMessages($type) {
|
||||||
|
return !empty($this->messages[$type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any messages at all
|
||||||
|
public function hasAnyMessages() {
|
||||||
|
return $this->totalCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear messages of a specific type
|
||||||
|
public function clear($type) {
|
||||||
|
if (isset($this->messages[$type])) {
|
||||||
|
$this->messages[$type] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all messages
|
||||||
|
public function clearAll() {
|
||||||
|
foreach ($this->messages as $type => $list) {
|
||||||
|
$this->messages[$type] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Post.php
Normal file
25
src/Post.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Post {
|
||||||
|
private $data = [];
|
||||||
|
|
||||||
|
public function __construct($post) {
|
||||||
|
$this->sanitize($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitize($post) {
|
||||||
|
foreach ($post as $key => $value) {
|
||||||
|
$this->data[$key] = is_array($value)
|
||||||
|
? filter_var_array($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS)
|
||||||
|
: filter_var($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key, $default = null) {
|
||||||
|
return $this->data[$key] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function all() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
40
src/Redirect.php
Normal file
40
src/Redirect.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use
|
||||||
|
* $redirect->url('/login');
|
||||||
|
* to trigger a redirect
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class Redirect {
|
||||||
|
private ?string $url = null;
|
||||||
|
private int $statusCode = 303;
|
||||||
|
|
||||||
|
public function url(string $relativeUrl, int $statusCode = 303): void {
|
||||||
|
$this->statusCode = $statusCode;
|
||||||
|
|
||||||
|
// Detect HTTPS
|
||||||
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
|
||||||
|
|
||||||
|
// Get Hostname
|
||||||
|
$host = $_SERVER['HTTP_HOST'];
|
||||||
|
|
||||||
|
// Get Base Directory
|
||||||
|
$basePath = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
|
||||||
|
|
||||||
|
// Construct Absolute URL
|
||||||
|
$this->url = "$protocol://$host$basePath/" . ltrim($relativeUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isset(): bool {
|
||||||
|
return !is_null($this->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(): void {
|
||||||
|
if ($this->url) {
|
||||||
|
header("Location: " . $this->url, true, $this->statusCode);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
src/Router.php
125
src/Router.php
@ -4,22 +4,46 @@ class Router {
|
|||||||
public $routes = [];
|
public $routes = [];
|
||||||
public $query = [];
|
public $query = [];
|
||||||
public $path;
|
public $path;
|
||||||
public $controllerPath = BASEPATH . '/App/controllers/404.php';
|
public $controller;
|
||||||
|
public $controllerPath;
|
||||||
|
public $parameters = [];
|
||||||
|
public $requestType = 'get';
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->loadRoutes();
|
$this->routes = $this->loadRoutes();
|
||||||
$this->preparePath();
|
$this->path = $this->preparePath();
|
||||||
$this->prepareQuery();
|
$this->query = $this->prepareQuery();
|
||||||
$this->setRouteFile();
|
$this->requestType = $this->getRequestType();
|
||||||
|
$this->controller = $this->findController();
|
||||||
|
$this->controllerPath = $this->setRouteFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadRoutes() {
|
private function loadRoutes() {
|
||||||
require_once( BASEPATH . '/App/routes.php');
|
require_once(FRAMEWORKPATH . '/config/routes.php');
|
||||||
$this->routes = $routes;
|
// Check if Path exists
|
||||||
|
if (file_exists(BASEPATH . '/App/routes.php')) {
|
||||||
|
require_once( BASEPATH . '/App/routes.php');
|
||||||
|
}
|
||||||
|
$routes = array_merge((array)$routes, (array)$framework_routes);
|
||||||
|
|
||||||
|
return $routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function preparePath() {
|
private function preparePath() {
|
||||||
$this->path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||||
|
|
||||||
|
//homepage
|
||||||
|
if ($path === '/') {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove empty directory path
|
||||||
|
$path = rtrim($path, '/'); // remove trailing slash
|
||||||
|
|
||||||
|
//remove anything after and including ampersand
|
||||||
|
$path = preg_replace('/&.+$/', '', $path);
|
||||||
|
|
||||||
|
return $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareQuery() {
|
private function prepareQuery() {
|
||||||
@ -28,16 +52,91 @@ class Router {
|
|||||||
if (isset($parsedUri['query'])) {
|
if (isset($parsedUri['query'])) {
|
||||||
parse_str($parsedUri['query'], $queryArray);
|
parse_str($parsedUri['query'], $queryArray);
|
||||||
}
|
}
|
||||||
$this->query = $queryArray;
|
return $queryArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setRouteFile() {
|
private function getRequestType() {
|
||||||
|
// is the requewst a get or post?
|
||||||
|
if (empty($_POST)) {
|
||||||
|
return 'get';
|
||||||
|
} else {
|
||||||
|
return 'post';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findController() {
|
||||||
|
|
||||||
|
// one to one match
|
||||||
|
if (array_key_exists($this->path, $this->routes)) {
|
||||||
|
if (!empty($this->routes[$this->path][$this->requestType])) {
|
||||||
|
return $this->routes[$this->path][$this->requestType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->routes as $key => $value) {
|
foreach ($this->routes as $key => $value) {
|
||||||
if ( $this->path == $key) {
|
// Check if key contains a curly bracket, if not continue. We already checked above.
|
||||||
$this->controllerPath = BASEPATH . '/App/controllers/' . $value['file'] . '.php';
|
if (!strpos($key, '{')) continue;
|
||||||
break;
|
|
||||||
|
// Remove everything after the curly bracket, from key
|
||||||
|
$keyPath = substr($key, 0, strpos($key, '{'));
|
||||||
|
|
||||||
|
//see if keyPath matches the first characters of $this->path, only the first characters have to match
|
||||||
|
if (strpos($this->path, $keyPath) === 0) {
|
||||||
|
|
||||||
|
// We have a potential match. Now check if the parameter count is equal
|
||||||
|
$keyParams = explode('/', $key);
|
||||||
|
$pathParams = explode('/', $this->path);
|
||||||
|
$keyParamCount = count($keyParams);
|
||||||
|
$pathParamCount = count($pathParams);
|
||||||
|
if ($keyParamCount === $pathParamCount) {
|
||||||
|
for ($i=0; $i < $pathParamCount; $i++) {
|
||||||
|
if (strpos($keyParams[$i], '{') !== false) {
|
||||||
|
$keyParams[$i] = substr($keyParams[$i], 1, -1);
|
||||||
|
$this->parameters[$keyParams[$i]] = $pathParams[$i];
|
||||||
|
return $this->routes[$key][$this->requestType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '404';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if the file exists, sets file path
|
||||||
|
private function setRouteFile() {
|
||||||
|
|
||||||
|
if (str_starts_with($this->controller, 'NOVACONIUM')) {
|
||||||
|
$trimmed = substr($this->controller, strlen('NOVACONIUM/'));
|
||||||
|
$cp = FRAMEWORKPATH . '/controllers/' . $trimmed . '.php';
|
||||||
|
} else {
|
||||||
|
$cp = BASEPATH . '/App/controllers/' . $this->controller . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($cp)) {
|
||||||
|
return $cp;
|
||||||
|
} else {
|
||||||
|
//Check if 404 exits
|
||||||
|
if (file_exists(BASEPATH . '/App/controllers/404.php')) {
|
||||||
|
return BASEPATH . '/App/controllers/404.php';
|
||||||
|
} else {
|
||||||
|
return FRAMEWORKPATH . '/defaults/App/controllers/404.php';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function debug() {
|
||||||
|
echo '<div id="router-debug-container" class="debug">';
|
||||||
|
echo '<table border="1" cellpadding="10" cellspacing="0">';
|
||||||
|
echo '<tr><th>Url Path</th><td>' . htmlspecialchars($this->path) . '</td></tr>';
|
||||||
|
echo '<tr><th>Controller Path</th><td>' . htmlspecialchars($this->controllerPath) . '</td></tr>';
|
||||||
|
echo '<tr><th>Parameters</th><td><pre>' . print_r($this->parameters, true) . '</pre></td></tr>';
|
||||||
|
echo '<tr><th>Routes</th><td><pre>' . print_r($this->routes, true) . '</pre></td></tr>';
|
||||||
|
echo '</table></div>';
|
||||||
|
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
63
src/Session.php
Normal file
63
src/Session.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
$this->session = &$_SESSION; // Reference $_SESSION to keep them in sync
|
||||||
|
if (!isset($this->session['token'])) {
|
||||||
|
$this->setToken();
|
||||||
|
}
|
||||||
|
if (!isset($this->session['messages'])) {
|
||||||
|
$this->session['messages'] = []; // Always ensure messages is an array
|
||||||
|
}
|
||||||
|
if (!isset($this->session['formData'])) {
|
||||||
|
$this->session['formData'] = []; // Initialize formData
|
||||||
|
}
|
||||||
|
if (!isset($this->session['errors'])) {
|
||||||
|
$this->session['errors'] = []; // Initialize errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setToken() {
|
||||||
|
$this->session['token'] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($key, $value) {
|
||||||
|
$this->session[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key) {
|
||||||
|
return isset($this->session[$key]) ? $this->session[$key] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flash($key) {
|
||||||
|
$return = $this->get($key);
|
||||||
|
$this->delete($key);
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debug() {
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($key) {
|
||||||
|
if (isset($this->session[$key])) {
|
||||||
|
unset($this->session[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write() {
|
||||||
|
// No need to assign to $_SESSION since $this->session is a reference
|
||||||
|
session_write_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kill() {
|
||||||
|
$this->session = [];
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once(BASEPATH . '/vendor/autoload.php');
|
|
||||||
|
|
||||||
//Twig
|
|
||||||
$loader = new Twig\Loader\FilesystemLoader(BASEPATH . '/App/views/');
|
|
||||||
$loader->addPath(BASEPATH . '/vendor/4lt/novaconium/twig', 'novaconium');
|
|
||||||
$loader->addPath(BASEPATH . '/App/templates', 'override');
|
|
||||||
$twig = new Twig\Environment($loader);
|
|
||||||
|
|
||||||
// Load a controller
|
|
||||||
require_once('Router.php');
|
|
||||||
$router = new Router();
|
|
||||||
require_once($router->controllerPath);
|
|
29
src/functions.php
Normal file
29
src/functions.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump and Die
|
||||||
|
*/
|
||||||
|
function dd(...$vars) {
|
||||||
|
echo "<pre style='background:#222;color:#0f0;padding:10px;border-radius:5px;'>";
|
||||||
|
foreach ($vars as $var) {
|
||||||
|
var_dump($var);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
echo "</pre>";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeitso() {
|
||||||
|
global $session, $db, $redirect, $config, $messages, $log;
|
||||||
|
|
||||||
|
if (!empty($config['database']['host'])) {
|
||||||
|
$db->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
$session->set('messages', $messages->getAllMessages());
|
||||||
|
$session->write();
|
||||||
|
|
||||||
|
$redirect->execute();
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
63
src/novaconium.php
Normal file
63
src/novaconium.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
define('FRAMEWORKPATH', BASEPATH . '/vendor/4lt/novaconium');
|
||||||
|
|
||||||
|
require_once(BASEPATH . '/vendor/autoload.php');
|
||||||
|
|
||||||
|
//Check if config file exists
|
||||||
|
if (file_exists(BASEPATH . '/App/config.php')) {
|
||||||
|
require_once(BASEPATH . '/App/config.php');
|
||||||
|
} else {
|
||||||
|
require_once(FRAMEWORKPATH . '/defaults/App/config.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Logger.php');
|
||||||
|
$log = new Logger(BASEPATH . $config['logfile'], $config['loglevel']);
|
||||||
|
|
||||||
|
// Global Functions
|
||||||
|
require_once(FRAMEWORKPATH . '/src/functions.php');
|
||||||
|
|
||||||
|
// Creates the view() function using twig
|
||||||
|
$data = array();
|
||||||
|
require_once(FRAMEWORKPATH . '/src/twig.php');
|
||||||
|
|
||||||
|
// Start a Session
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Session.php');
|
||||||
|
$session = new Session();
|
||||||
|
$data['token'] = $session->get('token');
|
||||||
|
$data['username'] = $session->get('username');
|
||||||
|
if ($config['loglevel'] == 'DEBUG') {
|
||||||
|
$data['debug'] = nl2br(print_r($session->debug(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
require_once(FRAMEWORKPATH . '/src/MessageHandler.php');
|
||||||
|
$messages = new MessageHandler($session->flash('messages'));
|
||||||
|
|
||||||
|
foreach (['error','notice'] as $key){
|
||||||
|
$data[$key] = $messages->showMessages($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Database Class
|
||||||
|
if (!empty($config['database']['host'])) {
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Database.php');
|
||||||
|
$db = new Database($config['database']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanatize POST Data
|
||||||
|
if (!empty($_POST)) {
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Post.php');
|
||||||
|
$post = new POST($_POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a Redirect
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Redirect.php');
|
||||||
|
$redirect = new Redirect();
|
||||||
|
|
||||||
|
// Load a controller
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Router.php');
|
||||||
|
$router = new Router();
|
||||||
|
//$router->debug();
|
||||||
|
require_once($router->controllerPath);
|
||||||
|
|
||||||
|
makeitso();
|
32
src/twig.php
Normal file
32
src/twig.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
//Twig
|
||||||
|
function view($name = '', $moreData = []) {
|
||||||
|
global $config, $data; // Use the globally included $config
|
||||||
|
|
||||||
|
if (!empty($moreData)){
|
||||||
|
$data = array_merge($data, $moreData);
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader = new Twig\Loader\FilesystemLoader(BASEPATH . '/App/views/');
|
||||||
|
$loader->addPath(FRAMEWORKPATH . '/twig', 'novaconium');
|
||||||
|
$loader->addPath(FRAMEWORKPATH . '/views', 'novacore');
|
||||||
|
$loader->addPath(BASEPATH . '/App/templates', 'override');
|
||||||
|
|
||||||
|
$twig = new Twig\Environment($loader);
|
||||||
|
|
||||||
|
// Add config to Twig globally
|
||||||
|
$twig->addGlobal('config', $config);
|
||||||
|
|
||||||
|
// Check if the template exists
|
||||||
|
if (file_exists(BASEPATH . '/App/views/' . $name . '.html.twig')) {
|
||||||
|
echo $twig->render($name . '.html.twig', $data);
|
||||||
|
return true;
|
||||||
|
} elseif (str_starts_with($name, '@')) { // Check if using framework
|
||||||
|
echo $twig->render($name . '.html.twig', $data);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
echo "Error: Twig Template ($name) Not Found.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
6
twig/foot.html.twig
Normal file
6
twig/foot.html.twig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<!--
|
||||||
|
What goes very last on the page.
|
||||||
|
right before the /body
|
||||||
|
like javascript
|
||||||
|
or analytics
|
||||||
|
-->
|
3
twig/footer.html.twig
Normal file
3
twig/footer.html.twig
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<!--
|
||||||
|
What goes in the footer html tag
|
||||||
|
-->
|
@ -1,6 +1,6 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>{{ title | default('Welcome') }}</title>
|
<title>{{ title | default('Welcome To Novaconium') }}</title>
|
||||||
<meta name="generator" content="nickyeoman/phpframework" />
|
<meta name="generator" content="Novaconium" />
|
||||||
|
|
||||||
<meta name="description" content="{{ description | default('No description given') }}">
|
<meta name="description" content="{{ description | default('No description given') }}">
|
||||||
<meta name="keywords" content="{{ keywords | default('website') }}">
|
<meta name="keywords" content="{{ keywords | default('website') }}">
|
||||||
@ -19,8 +19,8 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
|
||||||
{# https://developers.google.com/fonts/docs/getting_started #}
|
{# https://developers.google.com/fonts/docs/getting_started #}
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro|Material+Icons|Material+Icons+Outlined">
|
<link href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Source+Code+Pro&display=swap&family=Material+Icons&family=Material+Icons+Outlined" rel="stylesheet">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
<link rel="stylesheet" href="/css/novaconium.css">
|
||||||
|
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
11
twig/left.html.twig
Normal file
11
twig/left.html.twig
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% if username is not empty %}
|
||||||
|
<div class="left">
|
||||||
|
<ul id="leftnav">
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/novaconium/dashboard">Dashboard</a></li>
|
||||||
|
<li><a href="/novaconium/pages">Pages</a></li>
|
||||||
|
<li><a href="/novaconium/messages">Messages</a></li>
|
||||||
|
<li><a href="/novaconium/logout">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
@ -1,54 +1,63 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
|
|
||||||
{% include '@override/mod_above_head.html.twig' ignore missing %}
|
{% include '@override/above_head.html.twig' ignore missing %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% include ['@override/mod_head.html.twig', '@novaconium/head.html.twig'] %}
|
{% include ['@override/head.html.twig', '@novaconium/head.html.twig'] %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="{{ pageid | default('pageid') }}">
|
<body id="{{ pageid | default('pageid') }}" class="{{ pageclass | default('pageclass') }}" >
|
||||||
|
|
||||||
|
{# Page Header #}
|
||||||
|
<header>
|
||||||
|
{% block headerbefore %}{% endblock %}
|
||||||
|
{% include ['@override/nav.html.twig', '@novaconium/nav.html.twig'] %}
|
||||||
|
{% block headerafter %}{% endblock %}
|
||||||
|
</header>
|
||||||
|
|
||||||
{# Page Header #}
|
<!-- Main Content Of The Page -->
|
||||||
<header>
|
<div id="page">
|
||||||
{% block headerbefore %}{% endblock %}
|
<div class="container">
|
||||||
{% include ['@override/mod_nav.html.twig', '@novaconium/nav.html.twig'] %}
|
|
||||||
{% block headerafter %}{% endblock %}
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Main Content Of The Page -->
|
{% include ['@override/left.html.twig','@novaconium/left.html.twig'] %}
|
||||||
<div id="page">
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="middle">
|
<div class="middle">
|
||||||
{% if error|default is not empty %}
|
{% if error|default is not empty %}
|
||||||
{% for key, val in error %}
|
{% for key, val in error %}
|
||||||
<div class="error">{{ val }}</div>
|
<div class="error">{{ val }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if notice|default is not empty %}
|
{% if notice|default is not empty %}
|
||||||
{% for key, val in notice %}
|
{% for key, val in notice %}
|
||||||
<div class="notice">{{ val }}</div>
|
<div class="notice">{{ val }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<article>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{% include ['@override/right.html.twig','@novaconium/right.html.twig'] %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<article>
|
|
||||||
{% include 'cms/mod_alex.html.twig' ignore missing %}
|
|
||||||
{% block content %}{% endblock %}
|
|
||||||
{% include 'cms/mod_simon.html.twig' ignore missing %}
|
|
||||||
</article>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Page Footer #}
|
{# Page Footer #}
|
||||||
<footer>
|
<footer>
|
||||||
{% block footerbefore %}{% endblock %}
|
{% block footerbefore %}{% endblock %}
|
||||||
{% include ['@override/mod_footer.html.twig', '@novaconium/footer.html.twig'] %}
|
{% include ['@override/footer.html.twig', '@novaconium/footer.html.twig'] %}
|
||||||
{% block footerafter %}{% endblock %}
|
{% block footerafter %}{% endblock %}
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{% include ['@override/mod_foot.html.twig', '@novaconium/foot.html.twig'] %}
|
{% if debug is not empty %}
|
||||||
|
<div id="debug">
|
||||||
|
<h2>Debugging Information</h2>
|
||||||
|
{{ debug|raw }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% include ['@override/foot.html.twig', '@novaconium/foot.html.twig'] %}
|
||||||
</body></html>
|
</body></html>
|
||||||
|
@ -1,21 +1,5 @@
|
|||||||
<div class="container">
|
<div id="topnav">
|
||||||
<div id="logo"><a href="/">Logo Goes Here</a></div>
|
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
|
<! -- Navigation Goes Here -->
|
||||||
<ul>
|
|
||||||
|
|
||||||
<li><a href="/">Home</a></li>
|
|
||||||
<li><a href="/contact">Contact Us</a></li>
|
|
||||||
{% if loggedin|default(false) %}
|
|
||||||
<li><a href="/logout/">Logout</a></li>
|
|
||||||
{% else %}
|
|
||||||
<li><a href="/login/">Login</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if admin|default(false) == 'admin' %}
|
|
||||||
<li><a href="/admin">Admin</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
1
twig/right.html.twig
Normal file
1
twig/right.html.twig
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!-- Right Col -->
|
8
views/dashboard.html.twig
Normal file
8
views/dashboard.html.twig
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<p>Dashboard page</p>
|
||||||
|
<p><a href="/">Homepage</a></p>
|
||||||
|
<p><a href="/novaconium/logout">logout</p>
|
||||||
|
{% endblock %}
|
30
views/editmessage.html.twig
Normal file
30
views/editmessage.html.twig
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Edit Message - {{ title }}</h2>
|
||||||
|
|
||||||
|
<p><a href="/novaconium/messages/delete/{{ themessage.id }}">Delete</a></p>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/message_save">
|
||||||
|
<input type="hidden" name="id" value="{{ themessage.id }}">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}">
|
||||||
|
|
||||||
|
<label for="name">Name:</label>
|
||||||
|
<input type="text" id="name" name="name" value="{{ themessage.name }}" required>
|
||||||
|
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
<input type="email" id="email" name="email" value="{{ themessage.email }}" required>
|
||||||
|
|
||||||
|
<label for="message">Message:</label>
|
||||||
|
<textarea id="message" name="message" rows="10" required>{{ themessage.message }}</textarea>
|
||||||
|
|
||||||
|
<label for="unread">
|
||||||
|
<input type="checkbox" id="unread" name="unread" value="1" {% if themessage.unread %}checked{% endif %}>
|
||||||
|
Unread
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<p><strong>Created:</strong> {{ themessage.created|date("Y-m-d H:i:s") }}</p>
|
||||||
|
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
61
views/editpage.html.twig
Normal file
61
views/editpage.html.twig
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Edit Page - {{ title }}</h2>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/savePage">
|
||||||
|
<input type="hidden" name="id" value="{{ rows.id }}">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}">
|
||||||
|
|
||||||
|
<label for="title">Title:</label>
|
||||||
|
<input type="text" id="title" name="title" value="{{ rows.title }}" required>
|
||||||
|
|
||||||
|
<label for="heading">Heading:</label>
|
||||||
|
<input type="text" id="heading" name="heading" value="{{ rows.heading }}">
|
||||||
|
|
||||||
|
<label for="description">Description:</label>
|
||||||
|
<input type="text" id="description" name="description" value="{{ rows.description }}">
|
||||||
|
|
||||||
|
<label for="keywords">Keywords:</label>
|
||||||
|
<input type="text" id="keywords" name="keywords" value="{{ rows.keywords }}">
|
||||||
|
|
||||||
|
<label for="author">Author:</label>
|
||||||
|
<input type="text" id="author" name="author" value="{{ rows.author }}">
|
||||||
|
|
||||||
|
<label for="slug">Slug: (<a href="/page/{{ rows.slug }}" target="_new">/page/{{ rows.slug }}</a>)</label>
|
||||||
|
<input type="text" id="slug" name="slug" value="{{ rows.slug }}" required>
|
||||||
|
|
||||||
|
<label for="path">Path:</label>
|
||||||
|
<input type="text" id="path" name="path" value="{{ rows.path }}">
|
||||||
|
|
||||||
|
<label for="intro">Intro:</label>
|
||||||
|
<textarea id="intro" name="intro" rows="5">{{ rows.intro }}</textarea>
|
||||||
|
|
||||||
|
<label for="body">Body:</label>
|
||||||
|
<textarea id="body" name="body" rows="10">{{ rows.body }}</textarea>
|
||||||
|
|
||||||
|
<label for="notes">Notes:</label>
|
||||||
|
<textarea id="notes" name="notes" rows="5">{{ rows.notes }}</textarea>
|
||||||
|
|
||||||
|
<label for="draft">
|
||||||
|
<input type="checkbox" id="draft" name="draft" value="1" {% if rows.draft %}checked{% endif %}>
|
||||||
|
Save as Draft
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="changefreq">Change Frequency:</label>
|
||||||
|
<select id="changefreq" name="changefreq">
|
||||||
|
{% set freqs = ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'] %}
|
||||||
|
{% for freq in freqs %}
|
||||||
|
<option value="{{ freq }}" {% if rows.changefreq == freq %}selected{% endif %}>{{ freq|capitalize }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="priority">Priority (0.0 - 1.0):</label>
|
||||||
|
<input type="number" id="priority" name="priority" value="{{ rows.priority }}" step="0.1" min="0" max="1">
|
||||||
|
|
||||||
|
<p><strong>Created:</strong> {{ rows.created|date("Y-m-d H:i:s") }}</p>
|
||||||
|
<p><strong>Last Updated:</strong> {{ rows.updated|date("Y-m-d H:i:s") }}</p>
|
||||||
|
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
67
views/init.html.twig
Normal file
67
views/init.html.twig
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
|
||||||
|
{% if not secure_key %}
|
||||||
|
<div id="secure_key">
|
||||||
|
<h2>Secure Key</h2>
|
||||||
|
<p>Please set the <code>secure_key</code> in <code>App/config.php</code> to an alphanumeric code that is 64 characters in length.</p>
|
||||||
|
<p>You can generate a secure key like this:</p>
|
||||||
|
<pre><code>pwgen -sB 64 1</code></pre>
|
||||||
|
<p>Or use this one:</p>
|
||||||
|
<pre><code>{{gen_key}}</code></pre>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if users_created %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Users Table Created</h2>
|
||||||
|
<p>There was no users table in the database. One was created.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if empty_users and secure_key %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Create Admin</h2>
|
||||||
|
<p>No admin users exist, make an admin user now.</p>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/create_admin">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="email">Email:</label><br>
|
||||||
|
<input type="text" id="email" name="email" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<label for="secure_key">Secure Key <i>The <code>secure_key</code> from your config.php file</i>:</label><br>
|
||||||
|
<input type="text" id="secure_key" name="secure_key" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Create User</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if show_login %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Administrator Login</h2>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/login">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
23
views/login.html.twig
Normal file
23
views/login.html.twig
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="login_form">
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/login">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
43
views/messages.html.twig
Normal file
43
views/messages.html.twig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<table class="messages-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Message Preview</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for msg in messages %}
|
||||||
|
<tr class="{{ msg.unread ? 'unread' : 'read' }}">
|
||||||
|
<td><a href="mailto:{{ msg.email }}">{{ msg.email }}</a></td>
|
||||||
|
<td>{{ msg.name }}</td>
|
||||||
|
<td>{{ msg.message }}</td>
|
||||||
|
<td>{{ msg.created|date('Y-m-d H:i') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if msg.unread %}
|
||||||
|
<strong>Unread</strong>
|
||||||
|
{% else %}
|
||||||
|
Read
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="/novaconium/messages/edit/{{ msg.id }}" class="btn btn-edit">Edit</a>
|
||||||
|
<a href="/novaconium/messages/delete/{{ msg.id }}">Delete</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" style="text-align:center;">No messages found</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
30
views/pages.html.twig
Normal file
30
views/pages.html.twig
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<p><a href="/novaconium/page/create">Create Page</a></p>
|
||||||
|
<table class="pages-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Updated</th>
|
||||||
|
<th>Draft</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for page in pages %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/novaconium/page/edit/{{ page.id }}">{{ page.title }}</a></td>
|
||||||
|
<td>{{ page.created }}</td>
|
||||||
|
<td>{{ page.updated|default('Not updated') }}</td>
|
||||||
|
<td>{{ page.draft ? 'Draft' : 'Published' }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6">No pages found.</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user