Compare commits
2 Commits
230476e8e5
...
4484f88d8d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4484f88d8d | |||
| 7ee47a0b1e |
@@ -0,0 +1,66 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
Novaconium is a PHP 8.1+ micro-framework distributed as a Composer package (`4lt/novaconium`, PSR-4 autoloaded under the `Novaconium\` namespace from `src/`). This repo *is* the framework itself — there is no "app" here to run directly. Consuming projects `composer require` it and copy `skeleton/novaconium/` into their own project to get an `App/` directory (config, controllers, views, routes) plus a `public/` entrypoint.
|
||||||
|
|
||||||
|
Because of this, most of the interesting behavior lives in two places:
|
||||||
|
- `src/` — the framework runtime (bootstrap, router, session, db, etc.)
|
||||||
|
- `controllers/` + `views/` + `twig/` — the framework's own built-in admin panel ("NOVACONIUM" control panel routes), shipped so consuming apps get login/dashboard/page-editing/messages for free.
|
||||||
|
|
||||||
|
`skeleton/` is what gets copied into a new consuming project — treat it as a template, not live framework code.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
There is no local PHP/Composer install expected — Composer, PHP/Apache, and Sass all run in Docker containers (see `docs/docker.md`, `docs/Composer.md`, `docs/Sass.md`). There is no test suite or linter configured in this repo.
|
||||||
|
|
||||||
|
Compiling Sass (from repo root):
|
||||||
|
```bash
|
||||||
|
docker run --rm -v "$(pwd):/usr/src/app" -w /usr/src/app sass-container --style=compressed sass/novaconium.sass skeleton/novaconium/public/css/novaconium.css
|
||||||
|
```
|
||||||
|
|
||||||
|
For iterating against a real consuming project without re-running `composer require` on every change, use the fake autoloader dev pattern in `docs/Dev-Fake_autoload.md` (registers a `spl_autoload_register` shim pointing at a manually cloned copy of this repo under the app's `vendor/4lt/novaconium`).
|
||||||
|
|
||||||
|
## Request lifecycle
|
||||||
|
|
||||||
|
Entry point in a consuming app is `App/public/index.php` (see `skeleton/novaconium/public/index.php`), which:
|
||||||
|
1. Defines `BASEPATH` (app root) and `FRAMEWORKPATH` (`BASEPATH/vendor/4lt/novaconium`) — these two constants are used everywhere in `src/` to resolve app vs. framework paths, and are assumed to already be defined by the time any framework code runs.
|
||||||
|
2. Requires Composer's autoloader, then `FRAMEWORKPATH/src/novaconium.php` (the bootstrap).
|
||||||
|
3. Calls the global `makeitso()` at the very end.
|
||||||
|
|
||||||
|
`src/novaconium.php` (the bootstrap) runs in this order and wires up several **globals** that framework code and app controllers rely on implicitly (`$config`, `$log`, `$data`, `$session`, `$messages`, `$db`, `$post`, `$redirect`, `$router`):
|
||||||
|
1. Loads `App/config.php` (falls back to the skeleton's default config if the app hasn't created one).
|
||||||
|
2. Loads `src/functions.php` (global helpers: `dd()`, `makeitso()`) and `src/twig.php` (global `view()` helper).
|
||||||
|
3. Creates `$log` (`Novaconium\Logger`), `$session` (`Novaconium\Session`), `$messages` (`Novaconium\MessageHandler`, seeded from flashed session messages), `$db` (`Novaconium\Database`, only if `config['database']['host']` is set), `$post` (`Novaconium\Post`, only if `$_POST` is non-empty), `$redirect` (`Novaconium\Redirect`).
|
||||||
|
4. Builds `$router` (`Novaconium\Router`), whose constructor resolves the matched controller file, then immediately `require`s it — so the matched controller file executes inline, still inside bootstrap, with all the globals above already in scope.
|
||||||
|
|
||||||
|
Controllers are plain PHP scripts (not classes) that use the ambient globals directly (e.g. `$session->get(...)`, `$messages->error(...)`, `$post->get(...)`, `$db->getRow(...)`) and typically end by calling the global `view($templateName, $data)` or setting `$redirect->url(...)` followed by `makeitso()` to end the request (writes session, closes DB, performs any pending redirect, flashes messages, then `exit`).
|
||||||
|
|
||||||
|
## Router (`src/Router.php`)
|
||||||
|
|
||||||
|
Full behavioral spec is in `docs/Router.md` — read it before touching routing logic, since several quirks are **intentionally preserved for backward compatibility**, not bugs:
|
||||||
|
- Only the *first* `{param}` in a route pattern is ever extracted; subsequent params in the same pattern are ignored.
|
||||||
|
- For parameterized routes, the first route whose static prefix + segment count matches wins, even if that route has no handler for the current HTTP method — the router does not keep searching for a better match.
|
||||||
|
- No wildcard/optional-segment support; segment counts must match exactly.
|
||||||
|
|
||||||
|
Routes are plain associative arrays keyed by path, mapping `get`/`post` to a controller string (see `config/routes.php` for the framework's own admin routes, `skeleton/novaconium/App/routes.php` for the app-level shape). `App/routes.php` routes are merged with (and can override) framework routes. Controller strings prefixed with `NOVACONIUM` resolve against `FRAMEWORKPATH/controllers/`; everything else resolves against `BASEPATH/App/controllers/`. Unresolved routes/files fall back to the app's `404.php`, then the framework's `controllers/404.php`.
|
||||||
|
|
||||||
|
## Views (Twig)
|
||||||
|
|
||||||
|
`view($name, $data)` (`src/twig.php`) loads templates from `App/views/` by default, with two additional Twig namespaces registered: `@novaconium` → `FRAMEWORKPATH/twig` (layout partials: `master.html.twig`, `nav.html.twig`, `head.html.twig`, etc.) and `@novacore` → `FRAMEWORKPATH/views` (the framework's built-in admin pages: dashboard, pages, messages, settings). Apps can override any framework template by placing a same-named file under `App/templates/` (`override` namespace). The framework's own controllers render via the `@novacore/...` namespace (e.g. `view('@novacore/dashboard', $data)`).
|
||||||
|
|
||||||
|
## Other framework primitives (all under `src/`, namespace `Novaconium`)
|
||||||
|
|
||||||
|
- `Database` — thin `mysqli` wrapper (`query`, `getRow`, `getRows`); always uses prepared statements but binds every parameter as a string type.
|
||||||
|
- `Session` — thin `$_SESSION` wrapper; auto-initializes `token`/`messages`/`formData`/`errors` keys; `flash()` reads-then-deletes.
|
||||||
|
- `MessageHandler` — categorized flash messages (`error`/`warning`/`notice`/`success`), persisted across requests via `$session`.
|
||||||
|
- `Post` — sanitizes `$_POST` via `FILTER_SANITIZE_FULL_SPECIAL_CHARS` on construction; access via `$post->get($key)`.
|
||||||
|
- `Redirect` — buffers a target URL/status; only the *last* `url()` call before `makeitso()` takes effect; actual `header()` redirect happens inside `makeitso()`.
|
||||||
|
- `Logger` — level-gated file logger (`DEBUG`/`INFO`/`WARNING`/`ERROR`/`NONE`), configured via `config['logfile']`/`config['loglevel']`.
|
||||||
|
- `src/Services/` — newer, thinner service classes (`Auth`, `TagManager`) alongside the older top-level `src/` classes; check here first before adding new cross-cutting logic.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
App-level config lives in `App/config.php` (see `docs/ConfigurationFile.md` and `skeleton/novaconium/App/config.php` for the shape): `database` (mysqli connection), `base_url`, `secure_key` (used to pepper password hashes via `hash_hmac('sha3-512', ...)`, see `controllers/authenticate.php`), `logfile`, `loglevel`, plus optional `matomo_url`/`matomo_id`/`fonts` passed straight into Twig's global `$data`.
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Login Page',
|
'title' => 'Novaconium Login Page',
|
||||||
'pageclass' => 'novaconium'
|
'pageclass' => 'novaconium'
|
||||||
]);
|
]);
|
||||||
// Don't come here if logged in
|
// Don't come here if logged in
|
||||||
if ($session->get('username')) {
|
if ($ctx->session->get('username')) {
|
||||||
$redirect->url('/novaconium/dashboard');
|
$ctx->redirect->url('/novaconium/dashboard');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
view('@novacore/auth/login');
|
view('@novacore/auth/login');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
$session->kill();
|
$ctx->session->kill();
|
||||||
$log->info("Logout - Logout Success - " . $_SERVER['REMOTE_ADDR']);
|
$ctx->log->info("Logout - Logout Success - " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
|
||||||
$redirect->url('/');
|
$ctx->redirect->url('/');
|
||||||
makeitso();
|
makeitso();
|
||||||
|
|||||||
@@ -8,63 +8,63 @@ $url_fail = '/novaconium/login';
|
|||||||
|
|
||||||
|
|
||||||
// Don't go further if already logged in
|
// Don't go further if already logged in
|
||||||
if ( !empty($session->get('username')) ) {
|
if ( !empty($ctx->session->get('username')) ) {
|
||||||
$redirect->url($url_success);
|
$ctx->redirect->url($url_success);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure Session Token is correct
|
// Make sure Session Token is correct
|
||||||
if ($session->get('token') != $post->get('token')) {
|
if ($ctx->session->get('token') != $ctx->post->get('token')) {
|
||||||
$messages->addMessage('error', "Invalid Session.");
|
$ctx->messages->addMessage('error', "Invalid Session.");
|
||||||
$log->error("Login Authentication - Invalid Session Token");
|
$ctx->log->error("Login Authentication - Invalid Session Token");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Username
|
// Handle Username
|
||||||
$rawUsername = $post->get('username', null);
|
$rawUsername = $ctx->post->get('username', null);
|
||||||
$cleanUsername = $v->clean($rawUsername); // Clean the input
|
$cleanUsername = $v->clean($rawUsername); // Clean the input
|
||||||
$username = strtolower($cleanUsername); // Convert to lowercase
|
$username = strtolower($cleanUsername); // Convert to lowercase
|
||||||
if (!$username) {
|
if (!$username) {
|
||||||
$messages->addMessage('error', "No Username given.");
|
$ctx->messages->addMessage('error', "No Username given.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Password
|
// Handle Password
|
||||||
$password = $v->clean($post->get('password', null));
|
$password = $v->clean($ctx->post->get('password', null));
|
||||||
if ( empty($password) ) {
|
if ( empty($password) ) {
|
||||||
$messages->addMessage('error', "Password Empty.");
|
$ctx->messages->addMessage('error', "Password Empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************************************************
|
/*************************************************************************************************************
|
||||||
* Query Database
|
* Query Database
|
||||||
************************************************************************************************************/
|
************************************************************************************************************/
|
||||||
|
|
||||||
if ($messages->count('error') === 0) {
|
if ($ctx->messages->count('error') === 0) {
|
||||||
$query = "SELECT id, username, email, password, blocked FROM users WHERE username = ? OR email = ?";
|
$query = "SELECT id, username, email, password, blocked FROM users WHERE username = ? OR email = ?";
|
||||||
$matched = $db->getRow($query, [$username, $username]);
|
$matched = $ctx->db->getRow($query, [$username, $username]);
|
||||||
if (empty($matched)) {
|
if (empty($matched)) {
|
||||||
$messages->addMessage('error', "User or Password incorrect.");
|
$ctx->messages->addMessage('error', "User or Password incorrect.");
|
||||||
$log->warning("Login Authentication - Login Error, user doesn't exist");
|
$ctx->log->warning("Login Authentication - Login Error, user doesn't exist");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($messages->count('error') === 0) {
|
if ($ctx->messages->count('error') === 0) {
|
||||||
// Re-apply pepper
|
// Re-apply pepper
|
||||||
$peppered = hash_hmac('sha3-512', $password, $config['secure_key']);
|
$peppered = hash_hmac('sha3-512', $password, $ctx->config['secure_key']);
|
||||||
|
|
||||||
// Verify hashed password
|
// Verify hashed password
|
||||||
if (!password_verify($peppered, $matched['password'])) {
|
if (!password_verify($peppered, $matched['password'])) {
|
||||||
$messages->addMessage('error', "User or Password incorrect.");
|
$ctx->messages->addMessage('error', "User or Password incorrect.");
|
||||||
$log->warning("Login Authentication - Login Error, password wrong");
|
$ctx->log->warning("Login Authentication - Login Error, password wrong");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process Login or Redirect
|
// Process Login or Redirect
|
||||||
if ($messages->count('error') === 0) {
|
if ($ctx->messages->count('error') === 0) {
|
||||||
$query = "SELECT groupName FROM user_groups WHERE user_id = ?";
|
$query = "SELECT groupName FROM user_groups WHERE user_id = ?";
|
||||||
$groups = $db->getRow($query, [$matched['id']]);
|
$groups = $ctx->db->getRow($query, [$matched['id']]);
|
||||||
$session->set('username', $cleanUsername);
|
$ctx->session->set('username', $cleanUsername);
|
||||||
$session->set('group', $groups['groupName']);
|
$ctx->session->set('group', $groups['groupName']);
|
||||||
$redirect->url($url_success);
|
$ctx->redirect->url($url_success);
|
||||||
$log->info("Login Authentication - Login Success");
|
$ctx->log->info("Login Authentication - Login Success");
|
||||||
} else {
|
} else {
|
||||||
$redirect->url($url_fail);
|
$ctx->redirect->url($url_fail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Coming Soon',
|
'title' => 'Coming Soon',
|
||||||
'heading' => 'Coming Soon',
|
'heading' => 'Coming Soon',
|
||||||
'countdown' => true,
|
'countdown' => true,
|
||||||
'launch_date' => '2026-01-01T00:00:00'
|
'launch_date' => '2026-01-01T00:00:00'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
view('@novacore/coming-soon', $data);
|
view('@novacore/coming-soon', $ctx->data);
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use Nickyeoman\Validation;
|
|||||||
|
|
||||||
$validate = new Validation\Validate();
|
$validate = new Validation\Validate();
|
||||||
$valid = true;
|
$valid = true;
|
||||||
$p = $post->all();
|
$p = $ctx->post->all();
|
||||||
|
|
||||||
// Check secure key
|
// Check secure key
|
||||||
if (empty($p['secure_key']) || $p['secure_key'] !== $config['secure_key']) {
|
if (empty($p['secure_key']) || $p['secure_key'] !== $ctx->config['secure_key']) {
|
||||||
$valid = false;
|
$valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ if (empty($p['password'])) {
|
|||||||
$valid = false;
|
$valid = false;
|
||||||
} else {
|
} else {
|
||||||
// Use pepper + Argon2id
|
// Use pepper + Argon2id
|
||||||
$peppered = hash_hmac('sha3-512', $p['password'], $config['secure_key']);
|
$peppered = hash_hmac('sha3-512', $p['password'], $ctx->config['secure_key']);
|
||||||
$hashed_password = password_hash($peppered, PASSWORD_ARGON2ID);
|
$hashed_password = password_hash($peppered, PASSWORD_ARGON2ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +45,16 @@ if ($valid) {
|
|||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$params = [$name, $hashed_password, $p['email']];
|
$params = [$name, $hashed_password, $p['email']];
|
||||||
$db->query($query, $params);
|
$ctx->db->query($query, $params);
|
||||||
$userid = $db->lastid();
|
$userid = $ctx->db->lastid();
|
||||||
|
|
||||||
// Assign admin group
|
// Assign admin group
|
||||||
$groupInsertQuery = <<<EOSQL
|
$groupInsertQuery = <<<EOSQL
|
||||||
INSERT INTO `user_groups` (`user_id`, `groupName`) VALUES (?, ?);
|
INSERT INTO `user_groups` (`user_id`, `groupName`) VALUES (?, ?);
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$db->query($groupInsertQuery, [$userid, 'admin']);
|
$ctx->db->query($groupInsertQuery, [$userid, 'admin']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always redirect at end
|
// Always redirect at end
|
||||||
$redirect->url('/novaconium');
|
$ctx->redirect->url('/novaconium');
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Dashboard Page',
|
'title' => 'Novaconium Dashboard Page',
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'pageid' => 'controlPanel'
|
'pageid' => 'controlPanel'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
view('@novacore/dashboard', $data);
|
view('@novacore/dashboard', $ctx->data);
|
||||||
+10
-10
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Edit Page',
|
'title' => 'Novaconium Edit Page',
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'pageid' => 'controlPanel',
|
'pageid' => 'controlPanel',
|
||||||
@@ -8,14 +8,14 @@ $data = array_merge($data, [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if logged in
|
// Check if logged in
|
||||||
if (empty($session->get('username'))) {
|
if (empty($ctx->session->get('username'))) {
|
||||||
$messages->error('You are not logged in');
|
$ctx->messages->error('You are not logged in');
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get page ID from router parameters
|
// Get page ID from router parameters
|
||||||
$pageid = $router->parameters['id'] ?? null;
|
$pageid = $ctx->router->parameters['id'] ?? null;
|
||||||
|
|
||||||
if (!empty($pageid)) {
|
if (!empty($pageid)) {
|
||||||
// Existing page: fetch from database
|
// Existing page: fetch from database
|
||||||
@@ -51,23 +51,23 @@ WHERE p.id = ?
|
|||||||
GROUP BY p.id;
|
GROUP BY p.id;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$data['rows'] = $db->getRow($query, [$pageid]);
|
$ctx->data['rows'] = $ctx->db->getRow($query, [$pageid]);
|
||||||
|
|
||||||
// If no row is found, treat as new page
|
// If no row is found, treat as new page
|
||||||
if (!$data['rows']) {
|
if (!$ctx->data['rows']) {
|
||||||
$pageid = null;
|
$pageid = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($pageid)) {
|
if (empty($pageid)) {
|
||||||
// New page: set default values for all fields
|
// New page: set default values for all fields
|
||||||
$data['rows'] = [
|
$ctx->data['rows'] = [
|
||||||
'id' => 'newpage',
|
'id' => 'newpage',
|
||||||
'title' => '',
|
'title' => '',
|
||||||
'heading' => '',
|
'heading' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'keywords' => '',
|
'keywords' => '',
|
||||||
'author' => $session->get('username') ?? '',
|
'author' => $ctx->session->get('username') ?? '',
|
||||||
'slug' => '',
|
'slug' => '',
|
||||||
'path' => '',
|
'path' => '',
|
||||||
'intro' => '',
|
'intro' => '',
|
||||||
@@ -82,4 +82,4 @@ if (empty($pageid)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the edit page view
|
// Render the edit page view
|
||||||
view('@novacore/editpage/index', $data);
|
view('@novacore/editpage/index', $ctx->data);
|
||||||
|
|||||||
+30
-30
@@ -1,22 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = [
|
$ctx->data = [
|
||||||
'secure_key' => false,
|
'secure_key' => false,
|
||||||
'gen_key' => NULL,
|
'gen_key' => NULL,
|
||||||
'users_created' => false,
|
'users_created' => false,
|
||||||
'empty_users' => false,
|
'empty_users' => false,
|
||||||
'show_login' => false,
|
'show_login' => false,
|
||||||
'token' => $session->get('token'),
|
'token' => $ctx->session->get('token'),
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'title' => 'Novaconium Admin'
|
'title' => 'Novaconium Admin'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Check if SECURE KEY is Set in
|
// Check if SECURE KEY is Set in
|
||||||
if ($config['secure_key'] !== null && strlen($config['secure_key']) === 64) {
|
if ($ctx->config['secure_key'] !== null && strlen($ctx->config['secure_key']) === 64) {
|
||||||
$data['secure_key'] = true;
|
$ctx->data['secure_key'] = true;
|
||||||
} else {
|
} else {
|
||||||
$data['gen_key'] = substr(bin2hex(random_bytes(32)), 0, 64);
|
$ctx->data['gen_key'] = substr(bin2hex(random_bytes(32)), 0, 64);
|
||||||
$log->warn('secure_key not detected');
|
$ctx->log->warn('secure_key not detected');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user table exists
|
// Check if user table exists
|
||||||
@@ -26,7 +26,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'users';
|
AND TABLE_NAME = 'users';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
@@ -46,9 +46,9 @@ if ($result->num_rows === 0) {
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$data['users_created'] = true;
|
$ctx->data['users_created'] = true;
|
||||||
$log->info('Users Table Created');
|
$ctx->log->info('Users Table Created');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'user_groups';
|
AND TABLE_NAME = 'user_groups';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
@@ -72,8 +72,8 @@ if ($result->num_rows === 0) {
|
|||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
|
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$log->info('User_groups Table Created');
|
$ctx->log->info('User_groups Table Created');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'pages';
|
AND TABLE_NAME = 'pages';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
@@ -109,8 +109,8 @@ if ($result->num_rows === 0) {
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$log->info('Pages Table Created');
|
$ctx->log->info('Pages Table Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ContactForm Table
|
// Check ContactForm Table
|
||||||
@@ -120,7 +120,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'contactForm';
|
AND TABLE_NAME = 'contactForm';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
@@ -135,20 +135,20 @@ if ($result->num_rows === 0) {
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$log->info('ContactForm Table Created');
|
$ctx->log->info('ContactForm Table Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a user exists
|
// Check if a user exists
|
||||||
$result = $db->query("SELECT COUNT(*) as total FROM users");
|
$result = $ctx->db->query("SELECT COUNT(*) as total FROM users");
|
||||||
$row = $result->fetch_assoc();
|
$row = $result->fetch_assoc();
|
||||||
|
|
||||||
if ($row['total'] < 1) {
|
if ($row['total'] < 1) {
|
||||||
$data['empty_users'] = true;
|
$ctx->data['empty_users'] = true;
|
||||||
} else {
|
} else {
|
||||||
$log->info('Init Run complete, all sql tables exist with a user.');
|
$ctx->log->info('Init Run complete, all sql tables exist with a user.');
|
||||||
// Everything is working, send them to login page
|
// Everything is working, send them to login page
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'tags';
|
AND TABLE_NAME = 'tags';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
CREATE TABLE IF NOT EXISTS `tags` (
|
CREATE TABLE IF NOT EXISTS `tags` (
|
||||||
@@ -170,8 +170,8 @@ CREATE TABLE IF NOT EXISTS `tags` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$log->info('Tags Table Created');
|
$ctx->log->info('Tags Table Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Page Tags Junction Table (after tags table)
|
// Check Page Tags Junction Table (after tags table)
|
||||||
@@ -181,7 +181,7 @@ FROM information_schema.tables
|
|||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
AND TABLE_NAME = 'page_tags';
|
AND TABLE_NAME = 'page_tags';
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$result = $db->query($query);
|
$result = $ctx->db->query($query);
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
$query = <<<EOSQL
|
$query = <<<EOSQL
|
||||||
CREATE TABLE IF NOT EXISTS `page_tags` (
|
CREATE TABLE IF NOT EXISTS `page_tags` (
|
||||||
@@ -195,8 +195,8 @@ CREATE TABLE IF NOT EXISTS `page_tags` (
|
|||||||
FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
|
FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$db->query($query);
|
$ctx->db->query($query);
|
||||||
$log->info('Page Tags Junction Table Created');
|
$ctx->log->info('Page Tags Junction Table Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
view('@novacore/init', $data);
|
view('@novacore/init', $ctx->data);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
$messageid = $router->parameters['id'];
|
$messageid = $ctx->router->parameters['id'];
|
||||||
$query="DELETE FROM contactForm WHERE `contactForm`.`id` = ?";
|
$query="DELETE FROM contactForm WHERE `contactForm`.`id` = ?";
|
||||||
$db->query($query, [$messageid]);
|
$ctx->db->query($query, [$messageid]);
|
||||||
|
|
||||||
$redirect->url('/novaconium/messages');
|
$ctx->redirect->url('/novaconium/messages');
|
||||||
$messages->notice("Removed Message $messageid");
|
$ctx->messages->notice("Removed Message $messageid");
|
||||||
makeitso();
|
makeitso();
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Message Page',
|
'title' => 'Novaconium Message Page',
|
||||||
'pageclass' => 'novaconium'
|
'pageclass' => 'novaconium'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
$messageid = $router->parameters['id'];
|
$messageid = $ctx->router->parameters['id'];
|
||||||
$query = "SELECT id, name, email, message, created, unread FROM contactForm WHERE id = '$messageid'";
|
$query = "SELECT id, name, email, message, created, unread FROM contactForm WHERE id = ?";
|
||||||
|
|
||||||
$data['themessage'] = $db->getRow($query);
|
$ctx->withData(['themessage' => $ctx->db->getRow($query, [$messageid])]);
|
||||||
|
|
||||||
view('@novacore/editmessage', $data);
|
view('@novacore/editmessage', $ctx->data);
|
||||||
@@ -5,53 +5,53 @@ use Nickyeoman\Validation;
|
|||||||
$v = new Nickyeoman\Validation\Validate();
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
$url_success = '/novaconium/messages';
|
$url_success = '/novaconium/messages';
|
||||||
$url_error = '/novaconium/messages/edit/' . $post->get('id'); // Redirect back to the message edit form on error
|
$url_error = '/novaconium/messages/edit/' . $ctx->post->get('id'); // Redirect back to the message edit form on error
|
||||||
|
|
||||||
// Check if logged in
|
// Check if logged in
|
||||||
if (empty($session->get('username'))) {
|
if (empty($ctx->session->get('username'))) {
|
||||||
$messages->error('You are not logged in');
|
$ctx->messages->error('You are not logged in');
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check CSRF token
|
// Check CSRF token
|
||||||
if ($session->get('token') != $post->get('token')) {
|
if ($ctx->session->get('token') != $ctx->post->get('token')) {
|
||||||
$messages->error('Invalid token');
|
$ctx->messages->error('Invalid token');
|
||||||
$redirect->url($url_success);
|
$ctx->redirect->url($url_success);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get POST data
|
// Get POST data
|
||||||
$id = $post->get('id');
|
$id = $ctx->post->get('id');
|
||||||
$name = $post->get('name');
|
$name = $ctx->post->get('name');
|
||||||
$email = $post->get('email');
|
$email = $ctx->post->get('email');
|
||||||
$message = $post->get('message');
|
$message = $ctx->post->get('message');
|
||||||
$unread = !empty($post->get('unread')) ? 1 : 0;
|
$unread = !empty($ctx->post->get('unread')) ? 1 : 0;
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (empty($id) || empty($message) || empty($email)) {
|
if (empty($id) || empty($message) || empty($email)) {
|
||||||
$messages->error('One of the required fields was empty.');
|
$ctx->messages->error('One of the required fields was empty.');
|
||||||
$redirect->url($url_error);
|
$ctx->redirect->url($url_error);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Prepare update query
|
// Prepare update query
|
||||||
$query = "UPDATE `contactForm`
|
$query = "UPDATE `contactForm`
|
||||||
SET `name` = ?, `email` = ?, `message` = ?, `unread` = ?
|
SET `name` = ?, `email` = ?, `message` = ?, `unread` = ?
|
||||||
WHERE `id` = ?";
|
WHERE `id` = ?";
|
||||||
|
|
||||||
$params = [$name, $email, $message, $unread, $id];
|
$params = [$name, $email, $message, $unread, $id];
|
||||||
|
|
||||||
$db->query($query, $params);
|
$ctx->db->query($query, $params);
|
||||||
|
|
||||||
$messages->notice('Message updated successfully');
|
$ctx->messages->notice('Message updated successfully');
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$messages->error('Error updating message: ' . $e->getMessage());
|
$ctx->messages->error('Error updating message: ' . $e->getMessage());
|
||||||
$redirect->url($url_error);
|
$ctx->redirect->url($url_error);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to success page
|
// Redirect to success page
|
||||||
$redirect->url($url_success);
|
$ctx->redirect->url($url_success);
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Messages',
|
'title' => 'Novaconium Messages',
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'pageid' => 'controlPanel'
|
'pageid' => 'controlPanel'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the pages
|
// Get the pages
|
||||||
$query = "SELECT id, name, email, LEFT(message, 40) AS message, created, unread FROM contactForm";
|
$query = "SELECT id, name, email, LEFT(message, 40) AS message, created, unread FROM contactForm";
|
||||||
|
|
||||||
$matched = $db->getRows($query);
|
$matched = $ctx->db->getRows($query);
|
||||||
|
|
||||||
$data['messages'] = $matched;
|
$ctx->withData(['messages' => $matched]);
|
||||||
|
|
||||||
view('@novacore/messages', $data);
|
view('@novacore/messages', $ctx->data);
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Pages',
|
'title' => 'Novaconium Pages',
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'pageid' => 'controlPanel'
|
'pageid' => 'controlPanel'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the pages
|
// Get the pages
|
||||||
$query = "SELECT id, title, created, updated, draft FROM pages";
|
$query = "SELECT id, title, created, updated, draft FROM pages";
|
||||||
$matched = $db->getRows($query);
|
$matched = $ctx->db->getRows($query);
|
||||||
|
|
||||||
$data['pages'] = $matched;
|
$ctx->withData(['pages' => $matched]);
|
||||||
|
|
||||||
view('@novacore/pages', $data);
|
view('@novacore/pages', $ctx->data);
|
||||||
@@ -10,7 +10,7 @@ $pt = '@novacore/samples'; //Define the view directory
|
|||||||
//$pt = 'samples'; //drop the core for your project
|
//$pt = 'samples'; //drop the core for your project
|
||||||
|
|
||||||
//Grab the slug
|
//Grab the slug
|
||||||
$slug = $router->parameters['slug'];
|
$slug = $ctx->router->parameters['slug'];
|
||||||
|
|
||||||
//build path
|
//build path
|
||||||
$tmpl = $pt . '/' . $slug;
|
$tmpl = $pt . '/' . $slug;
|
||||||
@@ -26,7 +26,7 @@ if (strpos($pt, '@novacore') !== false) {
|
|||||||
$possibleFile = $baseDir . '/' . $slug . '.html.twig'; // add .twig extension if needed
|
$possibleFile = $baseDir . '/' . $slug . '.html.twig'; // add .twig extension if needed
|
||||||
|
|
||||||
if (is_file($possibleFile) && is_readable($possibleFile)) {
|
if (is_file($possibleFile) && is_readable($possibleFile)) {
|
||||||
view($tmpl, $data);
|
view($tmpl, $ctx->data);
|
||||||
} else {
|
} else {
|
||||||
http_response_code('404');
|
http_response_code('404');
|
||||||
header("Content-Type: text/html");
|
header("Content-Type: text/html");
|
||||||
|
|||||||
+34
-34
@@ -5,44 +5,44 @@ use Novaconium\Services\TagManager;
|
|||||||
|
|
||||||
$v = new Nickyeoman\Validation\Validate();
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
$url_error = '/novaconium/page/edit/' . $post->get('id'); // fallback for errors
|
$url_error = '/novaconium/page/edit/' . $ctx->post->get('id'); // fallback for errors
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Check login
|
// Check login
|
||||||
// -------------------------
|
// -------------------------
|
||||||
if (empty($session->get('username'))) {
|
if (empty($ctx->session->get('username'))) {
|
||||||
$messages->error('You are not logged in');
|
$ctx->messages->error('You are not logged in');
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Check CSRF token
|
// Check CSRF token
|
||||||
// -------------------------
|
// -------------------------
|
||||||
if ($session->get('token') != $post->get('token')) {
|
if ($ctx->session->get('token') != $ctx->post->get('token')) {
|
||||||
$messages->error('Invalid Token');
|
$ctx->messages->error('Invalid Token');
|
||||||
$redirect->url('/novaconium/pages');
|
$ctx->redirect->url('/novaconium/pages');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Gather POST data
|
// Gather POST data
|
||||||
// -------------------------
|
// -------------------------
|
||||||
$id = $post->get('id');
|
$id = $ctx->post->get('id');
|
||||||
$title = $_POST['title'] ?? '';
|
$title = $ctx->post->get('title', '');
|
||||||
$heading = $_POST['heading'] ?? '';
|
$heading = $ctx->post->get('heading', '');
|
||||||
$description = $_POST['description'] ?? '';
|
$description = $ctx->post->get('description', '');
|
||||||
$keywords = $_POST['keywords'] ?? '';
|
$keywords = $ctx->post->get('keywords', '');
|
||||||
$author = $_POST['author'] ?? '';
|
$author = $ctx->post->get('author', '');
|
||||||
$slug = $_POST['slug'] ?? '';
|
$slug = $ctx->post->get('slug', '');
|
||||||
$path = $_POST['path'] ?? null;
|
$path = $ctx->post->get('path');
|
||||||
$intro = $_POST['intro'] ?? '';
|
$intro = $ctx->post->get('intro', '');
|
||||||
$body = $_POST['body'] ?? '';
|
$body = $ctx->post->get('body', '');
|
||||||
$notes = $_POST['notes'] ?? '';
|
$notes = $ctx->post->get('notes', '');
|
||||||
$draft = !empty($post->get('draft')) ? 1 : 0;
|
$draft = !empty($ctx->post->get('draft')) ? 1 : 0;
|
||||||
$changefreq = $_POST['changefreq'] ?? 'monthly';
|
$changefreq = $ctx->post->get('changefreq', 'monthly');
|
||||||
$priority = $_POST['priority'] ?? 0.0;
|
$priority = $ctx->post->get('priority', 0.0);
|
||||||
$tags_json = $_POST['tags_json'] ?? '[]';
|
$tags_json = $ctx->post->get('tags_json', '[]');
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Decode & sanitize tags
|
// Decode & sanitize tags
|
||||||
@@ -57,13 +57,13 @@ $tags = array_unique($tags);
|
|||||||
// Validate required fields
|
// Validate required fields
|
||||||
// -------------------------
|
// -------------------------
|
||||||
if (empty($title) || empty($slug) || empty($body)) {
|
if (empty($title) || empty($slug) || empty($body)) {
|
||||||
$messages->error('Title, Slug, and Body are required.');
|
$ctx->messages->error('Title, Slug, and Body are required.');
|
||||||
$redirect->url($url_error);
|
$ctx->redirect->url($url_error);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$tagManager = new TagManager();
|
$tagManager = new TagManager($ctx->db);
|
||||||
|
|
||||||
if ($id == 'newpage') {
|
if ($id == 'newpage') {
|
||||||
// -------------------------
|
// -------------------------
|
||||||
@@ -79,9 +79,9 @@ try {
|
|||||||
$slug, $path, $intro, $body, $notes,
|
$slug, $path, $intro, $body, $notes,
|
||||||
$draft, $changefreq, $priority
|
$draft, $changefreq, $priority
|
||||||
];
|
];
|
||||||
$db->query($query, $params);
|
$ctx->db->query($query, $params);
|
||||||
$id = $db->lastid;
|
$id = $ctx->db->lastid;
|
||||||
$messages->notice('Page Created');
|
$ctx->messages->notice('Page Created');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// -------------------------
|
// -------------------------
|
||||||
@@ -97,8 +97,8 @@ try {
|
|||||||
$slug, $path, $intro, $body, $notes,
|
$slug, $path, $intro, $body, $notes,
|
||||||
$draft, $changefreq, $priority, $id
|
$draft, $changefreq, $priority, $id
|
||||||
];
|
];
|
||||||
$db->query($query, $params);
|
$ctx->db->query($query, $params);
|
||||||
$messages->notice('Page Updated');
|
$ctx->messages->notice('Page Updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
@@ -106,11 +106,11 @@ try {
|
|||||||
// -------------------------
|
// -------------------------
|
||||||
$tagManager->setTagsForPage($id, $tags);
|
$tagManager->setTagsForPage($id, $tags);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$messages->error($e->getMessage());
|
$ctx->messages->error($e->getMessage());
|
||||||
$redirect->url($url_error);
|
$ctx->redirect->url($url_error);
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect back to edit page
|
// Redirect back to edit page
|
||||||
$redirect->url('/novaconium/page/edit/' . $id);
|
$ctx->redirect->url('/novaconium/page/edit/' . $id);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Novaconium Settings',
|
'title' => 'Novaconium Settings',
|
||||||
'pageclass' => 'novaconium',
|
'pageclass' => 'novaconium',
|
||||||
'pageid' => 'controlPanel'
|
'pageid' => 'controlPanel'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ( empty($session->get('username'))) {
|
if ( empty($ctx->session->get('username'))) {
|
||||||
$redirect->url('/novaconium/login');
|
$ctx->redirect->url('/novaconium/login');
|
||||||
$messages->error('You are not loggedin');
|
$ctx->messages->error('You are not loggedin');
|
||||||
makeitso();
|
makeitso();
|
||||||
}
|
}
|
||||||
|
|
||||||
view('@novacore/settings', $data);
|
view('@novacore/settings', $ctx->data);
|
||||||
@@ -10,7 +10,7 @@ $query=<<<EOSQL
|
|||||||
AND draft = 0
|
AND draft = 0
|
||||||
ORDER BY updated DESC;
|
ORDER BY updated DESC;
|
||||||
EOSQL;
|
EOSQL;
|
||||||
$thepages = $db->getRows($query);
|
$thepages = $ctx->db->getRows($query);
|
||||||
|
|
||||||
// Start the view
|
// Start the view
|
||||||
echo '<?xml version="1.0" encoding="UTF-8"?>';
|
echo '<?xml version="1.0" encoding="UTF-8"?>';
|
||||||
@@ -25,9 +25,9 @@ if ( ! empty($thepages) ) {
|
|||||||
echo "<url>";
|
echo "<url>";
|
||||||
|
|
||||||
if ( empty($v['path']) )
|
if ( empty($v['path']) )
|
||||||
echo "<loc>" . $config['base_url'] . '/page/' . $v['slug'] . "</loc>";
|
echo "<loc>" . $ctx->config['base_url'] . '/page/' . $v['slug'] . "</loc>";
|
||||||
else
|
else
|
||||||
echo "<loc>" . $config['base_url'] . $v['path'] . "</loc>";
|
echo "<loc>" . $ctx->config['base_url'] . $v['path'] . "</loc>";
|
||||||
|
|
||||||
echo "<lastmod>" . $date . "</lastmod>";
|
echo "<lastmod>" . $date . "</lastmod>";
|
||||||
echo "<changefreq>" . $v['changefreq'] . "</changefreq>";
|
echo "<changefreq>" . $v['changefreq'] . "</changefreq>";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
$data = array_merge($data, [
|
$ctx->withData([
|
||||||
'title' => 'Welcome to Novaconium Index Page',
|
'title' => 'Welcome to Novaconium Index Page',
|
||||||
'pageclass' => 'novaconium'
|
'pageclass' => 'novaconium'
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
$slug = $router->parameters['slug'];
|
$slug = $ctx->router->parameters['slug'];
|
||||||
$query=<<<EOSQL
|
$query=<<<EOSQL
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
heading,
|
heading,
|
||||||
@@ -11,23 +11,22 @@ $query=<<<EOSQL
|
|||||||
body,
|
body,
|
||||||
created,
|
created,
|
||||||
updated
|
updated
|
||||||
FROM pages
|
FROM pages
|
||||||
WHERE slug = '$slug'
|
WHERE slug = ?
|
||||||
EOSQL;
|
EOSQL;
|
||||||
|
|
||||||
//$db->debugGetRow($query);
|
$page = $ctx->db->getRow($query, [$slug]);
|
||||||
$data = $db->getRow($query);
|
if(!$page) {
|
||||||
if(!$data) {
|
|
||||||
http_response_code('404');
|
http_response_code('404');
|
||||||
header("Content-Type: text/html");
|
header("Content-Type: text/html");
|
||||||
view('404');
|
view('404');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = array_merge($data, [
|
$ctx->withData(array_merge($page, [
|
||||||
'menuActive' => 'blog',
|
'menuActive' => 'blog',
|
||||||
'pageid' => 'page',
|
'pageid' => 'page',
|
||||||
'permissionsGroup' => $session->get('group')
|
'permissionsGroup' => $ctx->session->get('group')
|
||||||
]);
|
]));
|
||||||
|
|
||||||
view('page', $data);
|
view('page', $ctx->data);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ header('Content-Type: text/plain; charset=UTF-8');
|
|||||||
header('Cache-Control: public, max-age=604800');
|
header('Cache-Control: public, max-age=604800');
|
||||||
|
|
||||||
// Use $config['base_url'] as-is
|
// Use $config['base_url'] as-is
|
||||||
$baseUrl = $config['base_url'];
|
$baseUrl = $ctx->config['base_url'];
|
||||||
|
|
||||||
echo <<<TXT
|
echo <<<TXT
|
||||||
# robots.txt for sites powered by Novaconium framework
|
# robots.txt for sites powered by Novaconium framework
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Novaconium;
|
||||||
|
|
||||||
|
final class Context
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public array $config,
|
||||||
|
public Logger $log,
|
||||||
|
public Session $session,
|
||||||
|
public MessageHandler $messages,
|
||||||
|
public ?Database $db,
|
||||||
|
public ?Post $post,
|
||||||
|
public Redirect $redirect,
|
||||||
|
public Router $router,
|
||||||
|
public array $data = [],
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge additional values into the Twig data array.
|
||||||
|
*/
|
||||||
|
public function withData(array $more): static
|
||||||
|
{
|
||||||
|
$this->data = array_merge($this->data, $more);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -25,7 +25,7 @@ class Database {
|
|||||||
// Prepare the SQL statement
|
// Prepare the SQL statement
|
||||||
$stmt = $this->conn->prepare($query);
|
$stmt = $this->conn->prepare($query);
|
||||||
if (!$stmt) {
|
if (!$stmt) {
|
||||||
throw new Exception("Query preparation failed: " . $this->conn->error);
|
throw new \Exception("Query preparation failed: " . $this->conn->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind parameters if needed
|
// Bind parameters if needed
|
||||||
@@ -37,7 +37,7 @@ class Database {
|
|||||||
// Execute the statement
|
// Execute the statement
|
||||||
if (!$stmt->execute()) {
|
if (!$stmt->execute()) {
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
throw new Exception("Query execution failed: " . $stmt->error);
|
throw new \Exception("Query execution failed: " . $stmt->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save last insert id if it's an INSERT query
|
// Save last insert id if it's an INSERT query
|
||||||
@@ -70,7 +70,7 @@ class Database {
|
|||||||
// Prepare the SQL statement
|
// Prepare the SQL statement
|
||||||
$stmt = $this->conn->prepare($query);
|
$stmt = $this->conn->prepare($query);
|
||||||
if (!$stmt) {
|
if (!$stmt) {
|
||||||
throw new Exception("Query preparation failed: " . $this->conn->error);
|
throw new \Exception("Query preparation failed: " . $this->conn->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind parameters
|
// Bind parameters
|
||||||
@@ -82,7 +82,7 @@ class Database {
|
|||||||
// Execute the statement
|
// Execute the statement
|
||||||
if (!$stmt->execute()) {
|
if (!$stmt->execute()) {
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
throw new Exception("Query execution failed: " . $stmt->error);
|
throw new \Exception("Query execution failed: " . $stmt->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get result
|
// Get result
|
||||||
@@ -92,7 +92,7 @@ class Database {
|
|||||||
$stmt->close();
|
$stmt->close();
|
||||||
return $row;
|
return $row;
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo "An error occurred: " . $e->getMessage();
|
echo "An error occurred: " . $e->getMessage();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class MessageHandler {
|
|||||||
// Add a message of a specific type
|
// Add a message of a specific type
|
||||||
public function addMessage($type, $message) {
|
public function addMessage($type, $message) {
|
||||||
if (!isset($this->messages[$type])) {
|
if (!isset($this->messages[$type])) {
|
||||||
throw new Exception("Invalid message type: $type");
|
throw new \Exception("Invalid message type: $type");
|
||||||
}
|
}
|
||||||
$this->messages[$type][] = $message;
|
$this->messages[$type][] = $message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
namespace Novaconium\Services;
|
namespace Novaconium\Services;
|
||||||
|
|
||||||
|
use Novaconium\Database;
|
||||||
|
|
||||||
class TagManager
|
class TagManager
|
||||||
{
|
{
|
||||||
protected $db;
|
protected $db;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(?Database $db = null)
|
||||||
{
|
{
|
||||||
global $db;
|
$this->db = $db ?? ($GLOBALS['ctx']->db ?? null) ?? ($GLOBALS['db'] ?? null);
|
||||||
$this->db = $db;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+13
-2
@@ -24,7 +24,18 @@ function dd(...$vars): void {
|
|||||||
* connection if configured, and performs a redirect.
|
* connection if configured, and performs a redirect.
|
||||||
*/
|
*/
|
||||||
function makeitso(): void {
|
function makeitso(): void {
|
||||||
global $session, $db, $redirect, $config, $messages, $log;
|
global $session, $db, $redirect, $config, $messages, $log, $ctx;
|
||||||
|
|
||||||
|
// Prefer $ctx when available; its service properties are the same
|
||||||
|
// object instances as the bare globals, so this is behavior-neutral.
|
||||||
|
if (isset($ctx)) {
|
||||||
|
$session = $ctx->session;
|
||||||
|
$db = $ctx->db;
|
||||||
|
$redirect = $ctx->redirect;
|
||||||
|
$config = $ctx->config;
|
||||||
|
$messages = $ctx->messages;
|
||||||
|
$log = $ctx->log;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
// Close database if configured
|
// Close database if configured
|
||||||
@@ -44,7 +55,7 @@ function makeitso(): void {
|
|||||||
|
|
||||||
// Set all messages in the session
|
// Set all messages in the session
|
||||||
$session->set('messages', $messages->getAllMessages());
|
$session->set('messages', $messages->getAllMessages());
|
||||||
|
|
||||||
// Write any buffered session data to persistent storage
|
// Write any buffered session data to persistent storage
|
||||||
$session->write();
|
$session->write();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use Novaconium\Database;
|
|||||||
use Novaconium\Post;
|
use Novaconium\Post;
|
||||||
use Novaconium\Redirect;
|
use Novaconium\Redirect;
|
||||||
use Novaconium\Router;
|
use Novaconium\Router;
|
||||||
|
use Novaconium\Context;
|
||||||
|
|
||||||
$db = null;
|
$db = null;
|
||||||
$post = null;
|
$post = null;
|
||||||
@@ -56,4 +57,21 @@ $redirect = new Redirect();
|
|||||||
|
|
||||||
// --- Router ---
|
// --- Router ---
|
||||||
$router = new Router();
|
$router = new Router();
|
||||||
|
|
||||||
|
// --- Typed Context ---
|
||||||
|
// Bundles the globals above into one typed object. The bare globals are
|
||||||
|
// left in place (same object instances) so existing plain-script
|
||||||
|
// controllers keep working unmodified; new controllers can use $ctx.
|
||||||
|
$ctx = new Context(
|
||||||
|
config: $config,
|
||||||
|
log: $log,
|
||||||
|
session: $session,
|
||||||
|
messages: $messages,
|
||||||
|
db: $db,
|
||||||
|
post: $post,
|
||||||
|
redirect: $redirect,
|
||||||
|
router: $router,
|
||||||
|
data: $data,
|
||||||
|
);
|
||||||
|
|
||||||
require_once $router->controllerPath;
|
require_once $router->controllerPath;
|
||||||
|
|||||||
+9
-2
@@ -15,13 +15,20 @@ use Twig\Loader\FilesystemLoader;
|
|||||||
*/
|
*/
|
||||||
function view(string $name = '', array $moreData = []): bool
|
function view(string $name = '', array $moreData = []): bool
|
||||||
{
|
{
|
||||||
global $config, $data;
|
global $config, $data, $ctx;
|
||||||
|
|
||||||
if (!is_array($data)) {
|
if (!is_array($data)) {
|
||||||
$data = [];
|
$data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($moreData)) {
|
// Reconcile the bare $data global with $ctx->data: either style of
|
||||||
|
// controller ($data = array_merge($data, [...]) or $ctx->withData([...]))
|
||||||
|
// ends up folded in here, regardless of which one was used.
|
||||||
|
if (isset($ctx)) {
|
||||||
|
$data = array_merge($data, $ctx->data, $moreData);
|
||||||
|
$ctx->data = $data;
|
||||||
|
$config = $ctx->config;
|
||||||
|
} elseif (!empty($moreData)) {
|
||||||
$data = array_merge($data, $moreData);
|
$data = array_merge($data, $moreData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user