Compare commits
31
Commits
8e8d5714b4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98edad5bf7 | ||
|
|
91de0f1462 | ||
|
|
dfdcd992ad | ||
|
|
f7c6276a7a | ||
|
|
0138a14c29 | ||
|
|
2ecc79c714 | ||
|
|
ee40846bd6 | ||
|
|
7beef3a1d5 | ||
|
|
3b47e0ed71 | ||
|
|
8d576a497b | ||
|
|
54eee2416a | ||
|
|
c18c80d732 | ||
|
|
a0d302daec | ||
|
|
20a8afa9ce | ||
|
|
d85fe1b75c | ||
|
|
ccd2a2776a | ||
|
|
5d3962bba4 | ||
|
|
e782ea5979 | ||
|
|
9a99c31d47 | ||
|
|
d50443b10a | ||
|
|
b244b625ab | ||
|
|
2311c98257 | ||
|
|
9af5f50135 | ||
|
|
d3dedfb09a | ||
|
|
ef9b1a1998 | ||
|
|
077e4c434c | ||
|
|
2dee309279 | ||
|
|
ac66a97d5e | ||
|
|
f49f615238 | ||
|
|
2fb4481876 | ||
|
|
253a3fe2f6 |
@@ -42,7 +42,7 @@ class Mail {
|
|||||||
return mail($to, $subject, $message, $headers);
|
return mail($to, $subject, $message, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sendMail(string $subject, string $message, string $from = '', string $to = ''): bool
|
public static function sendMail(string $subject, string $message, string $to = '', string $from = ''): bool
|
||||||
{
|
{
|
||||||
if ($to == '') {
|
if ($to == '') {
|
||||||
$to = self::$to;
|
$to = self::$to;
|
||||||
|
|||||||
+35
-101
@@ -40,15 +40,8 @@ class Router {
|
|||||||
|
|
||||||
// Process basic routes
|
// Process basic routes
|
||||||
foreach (self::$routes as $route) {
|
foreach (self::$routes as $route) {
|
||||||
// If route does not match the base url
|
|
||||||
if ($route[0] != $path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ["/example/my-page", "ExampleController", "action", "" : ["view", "title", "description"]],
|
// ["/example/my-page", "ExampleController", "action", "" : ["view", "title", "description"]],
|
||||||
self::addBasicRoute(['GET', 'POST'], $route);
|
self::addRoute(['GET', 'POST'], $route);
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::createNavigation();
|
self::createNavigation();
|
||||||
@@ -81,13 +74,13 @@ class Router {
|
|||||||
* DELETE /route/{id} destroyAction
|
* DELETE /route/{id} destroyAction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
self::addRoute(['GET'], [$route, $controller, 'indexAction']);
|
self::addRoute(['GET'], [$route, $controller, 'index']);
|
||||||
self::addRoute(['GET'], [$route . '/create', $controller, 'createAction']);
|
self::addRoute(['GET'], [$route . '/create', $controller, 'create']);
|
||||||
self::addRoute(['POST'], [$route, $controller, 'storeAction']);
|
self::addRoute(['POST'], [$route, $controller, 'store']);
|
||||||
self::addRoute(['GET'], [$route . '/[i:id]', $controller, 'showAction', ['id']]);
|
self::addRoute(['GET'], [$route . '/[i:id]', $controller, 'show']);
|
||||||
self::addRoute(['GET'], [$route . '/[i:id]/edit', $controller, 'editAction', ['id']]);
|
self::addRoute(['GET'], [$route . '/[i:id]/edit', $controller, 'edit']);
|
||||||
self::addRoute(['PUT', 'PATCH'], [$route . '/[i:id]', $controller, 'updateAction', ['id']]);
|
self::addRoute(['PUT', 'PATCH'], [$route . '/[i:id]', $controller, 'update']);
|
||||||
self::addRoute(['DELETE'], [$route . '/[i:id]', $controller, 'destroyAction', ['id']]);
|
self::addRoute(['DELETE'], [$route . '/[i:id]', $controller, 'destroy']);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------//
|
//-------------------------------------//
|
||||||
@@ -146,7 +139,7 @@ class Router {
|
|||||||
$url = '/' . $section[$page['section_id']] . '/' . $page['page'];
|
$url = '/' . $section[$page['section_id']] . '/' . $page['page'];
|
||||||
|
|
||||||
// Add route
|
// Add route
|
||||||
self::$routes[] = [$url, 'PageController', 'route', $page['id']];
|
self::$routes[] = [$url, 'PageController', 'route', [$page['id']]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache sections and pages
|
// Cache sections and pages
|
||||||
@@ -156,109 +149,53 @@ class Router {
|
|||||||
|
|
||||||
protected static function addRoute(array $method = [], array $data = []): void
|
protected static function addRoute(array $method = [], array $data = []): void
|
||||||
{
|
{
|
||||||
if (!_exists($method) || !_exists($data)) {
|
if (!_exists($method) || !_exists($data) || count($data) < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = $data[0] ?? '';
|
$route = $data[0] ?? null;
|
||||||
$controller = $data[1] ?? '';
|
$controller = $data[1] ?? null;
|
||||||
$action = $data[2] ?? '';
|
$action = $data[2] ?? null;
|
||||||
$param = $data[3] ?? [];
|
$parameters = $data[3] ?? [];
|
||||||
if ($route == '' || $controller == '' || $action == '') {
|
|
||||||
|
if (!$route || !$controller || !is_array($parameters)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Klein route
|
// Create Klein route
|
||||||
self::$router->respond($method, $route, function($request, $response, $service)
|
self::$router->respond($method, $route, function($request, $response, $service) use($controller, $action, $parameters) {
|
||||||
use($controller, $action, $param) {
|
|
||||||
|
|
||||||
// Create new Controller object
|
// Create new Controller object
|
||||||
$controller = '\App\Controllers\\' . $controller;
|
$controller = '\App\Controllers\\' . $controller;
|
||||||
$controller = new $controller(self::$router);
|
$controller = new $controller(self::$router);
|
||||||
|
|
||||||
$stillValid = true;
|
// Complete action variable
|
||||||
|
if (!$action) {
|
||||||
|
$action = 'indexAction';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$action .= 'Action';
|
||||||
|
}
|
||||||
|
|
||||||
// If method does not exist in object
|
// If method does not exist in object
|
||||||
if (!method_exists($controller, $action)) {
|
if (!method_exists($controller, $action)) {
|
||||||
$stillValid = false;
|
return $controller->throw404();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no valid permissions
|
// If no valid permissions
|
||||||
if ($controller->getAdminSection() &&
|
if ($controller->getAdminSection() && !$controller->getLoggedIn()) {
|
||||||
$controller->getLoggedIn() == false) {
|
return $controller->throw404();
|
||||||
$stillValid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get URL parameters
|
||||||
|
$namedParameters = array_filter($request->paramsNamed()->all(), 'is_string', ARRAY_FILTER_USE_KEY);
|
||||||
|
$namedParameters = array_values($namedParameters);
|
||||||
|
|
||||||
|
// Append URL parameters to the hard-coded parameters
|
||||||
|
array_push($parameters, ...$namedParameters);
|
||||||
|
|
||||||
// Call Controller action
|
// Call Controller action
|
||||||
if ($stillValid) {
|
return $controller->{$action}(...$parameters);
|
||||||
|
|
||||||
// Loop through params
|
|
||||||
$params = [];
|
|
||||||
foreach ($param as $name) {
|
|
||||||
$params[] = $request->param($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $controller->{$action}(...$params);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$controller->throw404();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function addBasicRoute(array $method = [], array $route = []): void
|
|
||||||
{
|
|
||||||
if (!_exists($method) || !_exists($route)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Klein route
|
|
||||||
self::$router->respond($method, $route[0], function() use($route) {
|
|
||||||
|
|
||||||
// Create new Controller object
|
|
||||||
$controller = '\App\Controllers\\' . $route[1];
|
|
||||||
$controller = new $controller(self::$router);
|
|
||||||
|
|
||||||
// Complete action variable
|
|
||||||
if ($route[2] == '') {
|
|
||||||
$route[2] = 'indexAction';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$route[2] .= 'Action';
|
|
||||||
}
|
|
||||||
|
|
||||||
$stillValid = true;
|
|
||||||
|
|
||||||
// If method does not exist in object
|
|
||||||
if (!method_exists($controller, $route[2])) {
|
|
||||||
$stillValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no valid permissions
|
|
||||||
if ($controller->getAdminSection() &&
|
|
||||||
$controller->getLoggedIn() == false) {
|
|
||||||
$stillValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call Controller action
|
|
||||||
if ($stillValid) {
|
|
||||||
if (is_array($route[3])) {
|
|
||||||
return $controller->{$route[2]}(
|
|
||||||
$route[3][0] ?? '',
|
|
||||||
$route[3][1] ?? '',
|
|
||||||
$route[3][2] ?? ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ($route[3] != '') {
|
|
||||||
return $controller->{$route[2]}($route[3]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return $controller->{$route[2]}();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$controller->throw404();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +285,3 @@ class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Router::_init();
|
Router::_init();
|
||||||
|
|
||||||
// @Todo
|
|
||||||
// - combine addRoute and addBasicroute functionality
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class User {
|
|||||||
$user = UserModel::search(['username' => $username]);
|
$user = UserModel::search(['username' => $username]);
|
||||||
|
|
||||||
$success = false;
|
$success = false;
|
||||||
if ($user->exists() && $user->failed_login_attempt <= 2) {
|
if ($user->exists() && $user->loginAllowed()) {
|
||||||
$saltPassword = $user->salt . $password;
|
$saltPassword = $user->salt . $password;
|
||||||
if (password_verify($saltPassword, $user->password)) {
|
if (password_verify($saltPassword, $user->password)) {
|
||||||
$success = true;
|
$success = true;
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Http;
|
||||||
|
|
||||||
|
class Http {
|
||||||
|
|
||||||
|
private string $bodyContent;
|
||||||
|
|
||||||
|
private string $bodyFormat;
|
||||||
|
|
||||||
|
private array $options = [];
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(string $url, $data = []): Response
|
||||||
|
{
|
||||||
|
return $this->send('DELETE', $url, [
|
||||||
|
$this->bodyFormat => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $url, $query = null): Response
|
||||||
|
{
|
||||||
|
return $this->send('GET', $url, [
|
||||||
|
'query' => $query
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch(string $url, array $data = []): Response
|
||||||
|
{
|
||||||
|
return $this->send('PATCH', $url, [
|
||||||
|
$this->bodyFormat => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function put(string $url, array $data = []): Response
|
||||||
|
{
|
||||||
|
return $this->send('PUT', $url, [
|
||||||
|
$this->bodyFormat => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post(string $url, array $data = []): Response
|
||||||
|
{
|
||||||
|
return $this->send('POST', $url, [
|
||||||
|
$this->bodyFormat => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
public function accept(string $contentType): Http
|
||||||
|
{
|
||||||
|
return $this->withHeaders(['Accept' => $contentType]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acceptJson(): Http
|
||||||
|
{
|
||||||
|
return $this->accept('application/json');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function asForm(): Http
|
||||||
|
{
|
||||||
|
return $this->bodyFormat('form_params')
|
||||||
|
->contentType('application/x-www-form-urlencoded');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function asJson(): Http
|
||||||
|
{
|
||||||
|
return $this->bodyFormat('json')
|
||||||
|
->contentType('application/json');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bodyFormat(string $format): Http
|
||||||
|
{
|
||||||
|
$this->bodyFormat = $format;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contentType(string $type): Http
|
||||||
|
{
|
||||||
|
return $this->withHeaders(['Content-Type' => $type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withBody(string $content, string $contentType): Http
|
||||||
|
{
|
||||||
|
$this->bodyFormat('body');
|
||||||
|
|
||||||
|
$this->bodyContent = $content;
|
||||||
|
|
||||||
|
$this->contentType($contentType);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withHeaders(array $headers): Http
|
||||||
|
{
|
||||||
|
$this->options = array_merge_recursive($this->options, [
|
||||||
|
'headers' => $headers,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withToken(string $token): Http
|
||||||
|
{
|
||||||
|
$this->withHeaders(['Authorization' => 'Bearer ' . trim($token)]);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
public function send(string $method, string $url, array $options = []): Response
|
||||||
|
{
|
||||||
|
// Format headers
|
||||||
|
$headers = [];
|
||||||
|
if (_exists($this->options, 'headers')) {
|
||||||
|
foreach ($this->options['headers'] as $key => $value) {
|
||||||
|
$headers[] = "$key: $value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill body content
|
||||||
|
switch ($this->bodyFormat) {
|
||||||
|
case 'body':
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
if (_exists($options, 'json')) {
|
||||||
|
$this->bodyContent = json_encode($options['json']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'form_params':
|
||||||
|
if (_exists($options, 'form_params')) {
|
||||||
|
$this->bodyContent = http_build_query($options['form_params']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send HTTP request
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt($curl, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
|
||||||
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $this->bodyContent);
|
||||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
// On failed requests
|
||||||
|
if (!$response) {
|
||||||
|
$response = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Http;
|
||||||
|
|
||||||
|
class Response {
|
||||||
|
|
||||||
|
private string $response;
|
||||||
|
|
||||||
|
public function __construct(string $response)
|
||||||
|
{
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
public function body(): string
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Classes\Config;
|
||||||
|
use App\Model\BlogModel;
|
||||||
|
|
||||||
|
class BlogController extends PageController {
|
||||||
|
|
||||||
|
public function searchAction(): void
|
||||||
|
{
|
||||||
|
$archived = $this->router->request()->param('archived', 0);
|
||||||
|
$search = $this->router->request()->param('search', '');
|
||||||
|
$posts = $this->search($search, $archived);
|
||||||
|
|
||||||
|
$this->defineHelpers();
|
||||||
|
|
||||||
|
// AJAX search request will only render the posts partial
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
$this->router->service()->partial($this->views . '/partials/blog-posts.php', ['posts' => $posts]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->router->service()->posts = $posts;
|
||||||
|
$this->router->service()->search = $search;
|
||||||
|
$this->router->service()->injectView = $this->views . '/partials/blog-posts.php';
|
||||||
|
parent::view('blog', 'Blog');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rssAction(): void
|
||||||
|
{
|
||||||
|
date_default_timezone_set('Europe/Amsterdam');
|
||||||
|
|
||||||
|
$xml = new \SimpleXMLElement('<rss version="2.0"/>');
|
||||||
|
$channel = $xml->addChild('channel');
|
||||||
|
$channel->addChild('title', 'Rick's Webpage');
|
||||||
|
$channel->addChild('link', Config::c('APP_URL'));
|
||||||
|
$channel->addChild('description', 'Recent content on Rick's Webpage.');
|
||||||
|
$channel->addChild('language', 'en-us');
|
||||||
|
$channel->addChild('lastBuildDate', date('D, d M Y H:i:s O'));
|
||||||
|
|
||||||
|
// Fetch all non-archived blog posts
|
||||||
|
$search = $this->router->request()->param('search', '');
|
||||||
|
$posts = $this->search($search, 0);
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$link = Config::c('APP_URL') . '/' . $post['section'] . '/' . $post['page'];
|
||||||
|
$date = (new \DateTime($post['created_at']))->format('D, d M Y H:i:s O');
|
||||||
|
|
||||||
|
$item = $channel->addChild('item');
|
||||||
|
$item->addChild('title', $post['title']);
|
||||||
|
$item->addChild('link', $link);
|
||||||
|
$item->addChild('description', $post['content']);
|
||||||
|
$item->addChild('pubDate', $date);
|
||||||
|
$item->addChild('guid', $link);
|
||||||
|
}
|
||||||
|
|
||||||
|
header('content-type: text/xml; charset=UTF-8');
|
||||||
|
$dom = new \DOMDocument("1.0");
|
||||||
|
$dom->preserveWhiteSpace = false;
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
$dom->loadXML($xml->asXML());
|
||||||
|
print $dom->saveXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
private function search(string $query = '', int $archived = 0): ?array
|
||||||
|
{
|
||||||
|
return BlogModel::selectAll(
|
||||||
|
'blog_post.*, media.filename, media.extension, page.page, section.section, log.created_at', '
|
||||||
|
LEFT JOIN media ON blog_post.media_id = media.id
|
||||||
|
LEFT JOIN page ON blog_post.page_id = page.id
|
||||||
|
LEFT JOIN section ON page.section_id = section.id
|
||||||
|
LEFT JOIN log ON blog_post.log_id = log.id
|
||||||
|
WHERE blog_post.archived = :archived AND
|
||||||
|
(blog_post.content LIKE :query OR
|
||||||
|
blog_post.title LIKE :query OR
|
||||||
|
blog_post.tag LIKE :query)
|
||||||
|
', [
|
||||||
|
[':archived', "$archived", \PDO::PARAM_INT],
|
||||||
|
[':query', "%$query%", \PDO::PARAM_STR]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function defineHelpers(): void
|
||||||
|
{
|
||||||
|
$this->router->service()->prettyTimestamp = function (string $timestamp): string {
|
||||||
|
$date = date_create($timestamp);
|
||||||
|
$date = date_format($date, 'd M Y');
|
||||||
|
return "<u class=\"text-decoration-none text-reset\" title=\"{$timestamp}\">{$date}</u>";
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->router->service()->tags = function (string $tags): string {
|
||||||
|
$result = "";
|
||||||
|
|
||||||
|
// Remove empty elements via array_filter()
|
||||||
|
$splitTags = array_filter(explode(':', $tags), function ($tag) {
|
||||||
|
return !empty(trim($tag));
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($splitTags as $key => $tag) {
|
||||||
|
$result .= $tag;
|
||||||
|
$result .= (($key === array_key_last($splitTags)) ? '' : ', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Classes\Config;
|
||||||
|
use App\Classes\Http\Http;
|
||||||
|
use App\Classes\Session;
|
||||||
|
use App\Model\ConfigModel;
|
||||||
|
|
||||||
|
class CacheController extends PageController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum amount of files that can be purged on a single request
|
||||||
|
*/
|
||||||
|
public static int $purgeLimit = 30;
|
||||||
|
|
||||||
|
public function cacheAction(): void
|
||||||
|
{
|
||||||
|
$config = $this->getConfigValues();
|
||||||
|
|
||||||
|
$this->router->service()->config = $config;
|
||||||
|
$this->router->service()->csrfToken = Session::token();
|
||||||
|
$this->router->service()->purgeUrl = $this->url . '/purge';
|
||||||
|
$this->router->service()->toggleUrl = $this->url . '/toggle';
|
||||||
|
parent::view();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function purgeAction(): void
|
||||||
|
{
|
||||||
|
if (!$this->validatePostRequest()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Config::c('CLOUDFLARE_TOKEN');
|
||||||
|
$zone = Config::c('CLOUDFLARE_ZONE');
|
||||||
|
|
||||||
|
$url = "https://api.cloudflare.com/client/v4/zones/$zone/purge_cache";
|
||||||
|
|
||||||
|
if (!_exists($_POST, 'type')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bodies = [];
|
||||||
|
switch($_POST['type']) {
|
||||||
|
case 'css-js':
|
||||||
|
$body = self::generateUrls(['css', 'js']);
|
||||||
|
$chunks = array_chunk($body['files'], self::$purgeLimit);
|
||||||
|
foreach($chunks as $chunk) {
|
||||||
|
$bodies[]['files'] = $chunk;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'fonts-images':
|
||||||
|
$body = self::generateUrls(['fonts', 'img', 'media']);
|
||||||
|
$chunks = array_chunk($body['files'], self::$purgeLimit);
|
||||||
|
foreach($chunks as $chunk) {
|
||||||
|
$bodies[]['files'] = $chunk;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
$bodies[] = ['purge_everything' => true];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = null;
|
||||||
|
foreach($bodies as $body) {
|
||||||
|
$response = (new Http)->withToken($token)
|
||||||
|
->asJson()
|
||||||
|
->acceptJson()
|
||||||
|
->post($url, $body);
|
||||||
|
if (empty($response->body())
|
||||||
|
|| !json_decode($response->body(), true)['success']) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response) {
|
||||||
|
echo $response->body();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleAction(): void
|
||||||
|
{
|
||||||
|
if (!$this->validatePostRequest()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Config::c('CLOUDFLARE_TOKEN');
|
||||||
|
$zone = Config::c('CLOUDFLARE_ZONE');
|
||||||
|
|
||||||
|
$url = "https://api.cloudflare.com/client/v4/zones/$zone/settings/development_mode";
|
||||||
|
|
||||||
|
$config = $this->getConfigValues();
|
||||||
|
$currentState = $config['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'];
|
||||||
|
|
||||||
|
$response = (new Http)->withToken($token)
|
||||||
|
->asJson()
|
||||||
|
->acceptJson()
|
||||||
|
->patch($url, [
|
||||||
|
'value' => $currentState == '1' ? 'off' : 'on',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->saveConfigValues($response->body());
|
||||||
|
|
||||||
|
echo $response->body();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
private function validatePostRequest(): bool
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
|
||||||
|
parent::throw404();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config::c('CLOUDFLARE_ENABLED') != '1') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Session::validateToken($_POST)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateUrls(array $directories): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($directories as $directory) {
|
||||||
|
$files = array_diff(scandir($directory), ['..', '.']);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$result['files'][] = Config::c('APP_URL') . "/$directory/$file";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getConfigValues(): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$config = [
|
||||||
|
'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED' => '0',
|
||||||
|
'CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT' => '',
|
||||||
|
'CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($config as $key => $value) {
|
||||||
|
$result[$key] = ConfigModel::firstOrCreate(
|
||||||
|
['key' => $key],
|
||||||
|
['value' => $value]
|
||||||
|
)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED']) {
|
||||||
|
$expiresIn = $result['CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN'];
|
||||||
|
$updatedAt = $result['CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT'];
|
||||||
|
|
||||||
|
$expiresAtObject = new \DateTime($updatedAt);
|
||||||
|
$expiresAt = $expiresAtObject
|
||||||
|
->modify("+ $expiresIn seconds")
|
||||||
|
->format('Y-m-d H:i:s');
|
||||||
|
$nowObject = new \DateTime('now');
|
||||||
|
$now = $nowObject->format('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
if ($now >= $expiresAt) {
|
||||||
|
ConfigModel::updateOrCreate(
|
||||||
|
['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'],
|
||||||
|
['value' => 0]);
|
||||||
|
$result['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'] = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$result['enabled-remaining'] = $expiresAtObject->modify(
|
||||||
|
'- ' . $nowObject->getTimestamp() . ' seconds'
|
||||||
|
)->format('H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function saveConfigValues(string $response): void
|
||||||
|
{
|
||||||
|
$decodedResponse = json_decode($response, true);
|
||||||
|
if ($decodedResponse['success'] == true) {
|
||||||
|
$state = $decodedResponse['result']['value'];
|
||||||
|
$expiresIn = $decodedResponse['result']['time_remaining'];
|
||||||
|
$updatedAt = $decodedResponse['result']['modified_on'];
|
||||||
|
|
||||||
|
$updatedAtFormatted = (new \DateTime($updatedAt))->format('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
ConfigModel::updateOrCreate(
|
||||||
|
['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'],
|
||||||
|
['value' => $state == 'on' ? 1 : 0]);
|
||||||
|
|
||||||
|
ConfigModel::updateOrCreate(
|
||||||
|
['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_EXPIRES_IN'],
|
||||||
|
['value' => $expiresIn]);
|
||||||
|
|
||||||
|
ConfigModel::updateOrCreate(
|
||||||
|
['key' => 'CLOUDFLARE_DEVELOPMENT_MODE_UPDATED_AT'],
|
||||||
|
['value' => $updatedAtFormatted]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ class LoginController extends PageController {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$user = User::getUser('', $_POST['username']);
|
$user = User::getUser('', $_POST['username']);
|
||||||
if ($user->exists() && $user->failed_login_attempt >= 5) {
|
if ($user->exists() && !$user->loginAllowed()) {
|
||||||
$this->setAlert('danger', 'User has been blocked.');
|
$this->setAlert('danger', 'User has been blocked.');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -151,7 +151,7 @@ class LoginController extends PageController {
|
|||||||
<a href='$resetUrl'>$resetUrl</a>
|
<a href='$resetUrl'>$resetUrl</a>
|
||||||
";
|
";
|
||||||
|
|
||||||
if (Mail::send($subject, $message, $user->email)) {
|
if (Mail::sendMail($subject, $message, $user->email)) {
|
||||||
$this->setAlert('success', 'Successfully requested password reset.');
|
$this->setAlert('success', 'Successfully requested password reset.');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+4
-1
@@ -51,14 +51,17 @@ function _randomStr(int $length, string $keyspace =
|
|||||||
* Print variable inside of a <pre> and exit
|
* Print variable inside of a <pre> and exit
|
||||||
*
|
*
|
||||||
* @param mixed[] $output The variable (single/array) to print
|
* @param mixed[] $output The variable (single/array) to print
|
||||||
|
* @param bool $die Call die(); after printing
|
||||||
*
|
*
|
||||||
* @return void Nothing
|
* @return void Nothing
|
||||||
*/
|
*/
|
||||||
function _log($output): void {
|
function _log($output, bool $die = true): void {
|
||||||
echo '<pre>';
|
echo '<pre>';
|
||||||
var_dump($output);
|
var_dump($output);
|
||||||
echo '</pre>';
|
echo '</pre>';
|
||||||
|
if ($die) {
|
||||||
die();
|
die();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------//
|
//-------------------------------------//
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model;
|
||||||
|
|
||||||
|
class BlogModel extends Model {
|
||||||
|
|
||||||
|
protected $table = 'blog_post';
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model;
|
||||||
|
|
||||||
|
use App\Traits\Log;
|
||||||
|
|
||||||
|
class ConfigModel extends Model {
|
||||||
|
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
protected $table = 'config';
|
||||||
|
protected $sort = 'key';
|
||||||
|
|
||||||
|
public $title = "Config";
|
||||||
|
|
||||||
|
// Attribute rules
|
||||||
|
// Name | Type | Required | Filtered
|
||||||
|
public $rules = [
|
||||||
|
["key", "text", 1, 0],
|
||||||
|
["value", "text", 0, 0],
|
||||||
|
["log_id", "text", 1, 1],
|
||||||
|
];
|
||||||
|
}
|
||||||
+75
-41
@@ -63,22 +63,42 @@ abstract class Model {
|
|||||||
|
|
||||||
//-------------------------------------//
|
//-------------------------------------//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retreive data via PDO prepared statements
|
||||||
|
*
|
||||||
|
* The most frequently used constants for PDO are listed below,
|
||||||
|
* find more at: {@link https://www.php.net/manual/en/pdo.constants.php}
|
||||||
|
* - PDO::PARAM_BOOL
|
||||||
|
* - PDO::PARAM_NULL
|
||||||
|
* - PDO::PARAM_INT
|
||||||
|
* - PDO::PARAM_STR
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* self::query(
|
||||||
|
* "SELECT * FROM `example` WHERE `id` = :id AND `number` = :number AND `text` = :text", [
|
||||||
|
* [':id', 1],
|
||||||
|
* [':number', 7, \PDO::PARAM_INT],
|
||||||
|
* [':text', 'A random string', \PDO::PARAM_STR],
|
||||||
|
* ]);
|
||||||
|
*
|
||||||
|
* self::query(
|
||||||
|
* 'SELECT * FROM `example` WHERE `id` IN (?, ?, ?) AND `thing` = ?, [
|
||||||
|
* 1, 2, 3, 'stuff'
|
||||||
|
* ],
|
||||||
|
* '?'
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* @param $query The full prepared query statement
|
||||||
|
* @param $parameters The values to insert into the prepared statement
|
||||||
|
* @param $type Type of prepared statement, ':' for named placeholders,
|
||||||
|
* '?' for value placeholders
|
||||||
|
*
|
||||||
|
* @return array|null Retreived data, or null
|
||||||
|
*/
|
||||||
protected static function query(string $query, array $parameters = [],
|
protected static function query(string $query, array $parameters = [],
|
||||||
$type = ':'): ?array
|
$type = ':'): ?array
|
||||||
{
|
{
|
||||||
// Example
|
if ($type == '?' && substr_count($query, $type) != count($parameters)) {
|
||||||
// $parameters = [
|
|
||||||
// [':id', 1],
|
|
||||||
// [':number', 7, \PDO::PARAM_INT],
|
|
||||||
// [':string', 'A random string', \PDO::PARAM_STR],
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// PDO::PARAM_BOOL
|
|
||||||
// PDO::PARAM_NULL
|
|
||||||
// PDO::PARAM_INT
|
|
||||||
// PDO::PARAM_STR
|
|
||||||
|
|
||||||
if (substr_count($query, $type) != count($parameters)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +257,7 @@ abstract class Model {
|
|||||||
|
|
||||||
$required = false;
|
$required = false;
|
||||||
foreach ($this->rules as $rule) {
|
foreach ($this->rules as $rule) {
|
||||||
if ($rule[0] == $attribute && $rule[2] == 1) {
|
if ($rule[0] == $attribute && $rule[2] == 1 && $rule[3] == 0) {
|
||||||
$required = true;
|
$required = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -325,16 +345,6 @@ abstract class Model {
|
|||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
// $media = MediaModel::selectAll(
|
|
||||||
// '*', 'ORDER BY id DESC LIMIT :offset, :limit', [
|
|
||||||
// [':offset', $offset, \PDO::PARAM_INT],
|
|
||||||
// [':limit', $limit, \PDO::PARAM_INT],
|
|
||||||
// ]
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// $contents = ContentModel::selectAll(
|
|
||||||
// '*', 'WHERE id IN (?, ?, ?)', [1, 2, 3], '?'
|
|
||||||
// );
|
|
||||||
public static function selectAll(string $select = '*', string $filter = '',
|
public static function selectAll(string $select = '*', string $filter = '',
|
||||||
array $parameters = [], $type = ':'): ?array
|
array $parameters = [], $type = ':'): ?array
|
||||||
{
|
{
|
||||||
@@ -413,14 +423,15 @@ abstract class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load Model data: all, with a limit or pagination
|
* Load all Models, optionally with a limit or pagination
|
||||||
*
|
*
|
||||||
* @param int $limitOrPage Treated as page if $limit is provided, limit otherwise
|
* @param int $limitOrPage Treated as page if $limit is provided, limit otherwise
|
||||||
* @param int $limit The amount to limit by
|
* @param int $limit The amount to limit by
|
||||||
*
|
*
|
||||||
* @return array|null The found model data, or null
|
* @return array|null The found model data, or null
|
||||||
*/
|
*/
|
||||||
public static function all(int $limitOrPage = -1, int $limit = -1): ?array {
|
public static function all(int $limitOrPage = -1, int $limit = -1): ?array
|
||||||
|
{
|
||||||
$class = get_called_class();
|
$class = get_called_class();
|
||||||
$model = new $class;
|
$model = new $class;
|
||||||
|
|
||||||
@@ -451,7 +462,9 @@ abstract class Model {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreive Model, or instantiate
|
* Retreive Model, or instantiate
|
||||||
* Usage: $model = \App\Model\Example::firstOrNew(['name' => 'Example name']);
|
*
|
||||||
|
* Usage:
|
||||||
|
* $model = \App\Model\Example::firstOrNew(['name' => 'Example name']);
|
||||||
*
|
*
|
||||||
* @param $search Retrieve by
|
* @param $search Retrieve by
|
||||||
* @param $data Instantiate with search plus data
|
* @param $data Instantiate with search plus data
|
||||||
@@ -474,13 +487,16 @@ abstract class Model {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new Model
|
* Create new Model
|
||||||
* Usage: $model = \App\Model\Example::create(['name' => 'Example name']);
|
*
|
||||||
|
* Usage:
|
||||||
|
* $model = \App\Model\Example::create(['name' => 'Example name']);
|
||||||
*
|
*
|
||||||
* @param $data Create with this data
|
* @param $data Create with this data
|
||||||
*
|
*
|
||||||
* @return Model The Model
|
* @return Model The Model
|
||||||
*/
|
*/
|
||||||
public static function create(array $data): Model {
|
public static function create(array $data): Model
|
||||||
|
{
|
||||||
$class = get_called_class();
|
$class = get_called_class();
|
||||||
$model = new $class;
|
$model = new $class;
|
||||||
$model->fill($data);
|
$model->fill($data);
|
||||||
@@ -489,15 +505,20 @@ abstract class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreive Model, or create
|
* Retreive Model, create if it doesn't exist
|
||||||
* Usage: $model = \App\Model\Example::firstOrCreate(['name' => 'Example name']);
|
*
|
||||||
|
* Usage:
|
||||||
|
* $model = \App\Model\AddressModel::firstOrCreate(
|
||||||
|
* ['zip_code' => '1234AB', 'house_number' => 3],
|
||||||
|
* ['street' => 'Example lane']);
|
||||||
*
|
*
|
||||||
* @param $search Retrieve by
|
* @param $search Retrieve by
|
||||||
* @param $data Instantiate with search plus data
|
* @param $data Data used for creation
|
||||||
*
|
*
|
||||||
* @return Model The Model
|
* @return Model The Model
|
||||||
*/
|
*/
|
||||||
public static function firstOrCreate(array $search, array $data = []): Model {
|
public static function firstOrCreate(array $search, array $data = []): Model
|
||||||
|
{
|
||||||
$model = self::firstOrNew($search, $data);
|
$model = self::firstOrNew($search, $data);
|
||||||
|
|
||||||
if (!$model->exists()) {
|
if (!$model->exists()) {
|
||||||
@@ -507,14 +528,27 @@ abstract class Model {
|
|||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Update existing Model, or create it if doesn't exist
|
/**
|
||||||
// public static function updateOrCreate(array $data, array $data): Model {
|
* Update Model, create if it doesn't exist
|
||||||
// // $flight = App\Flight::updateOrCreate(
|
*
|
||||||
// // ['departure' => 'Oakland', 'destination' => 'San Diego'],
|
* Usage:
|
||||||
// // ['price' => 99]
|
* $model = \App\Model\FlightModel::updateOrCreate(
|
||||||
// // );
|
* ['departure' => 'Oakland', 'desination' => 'San Diego'],
|
||||||
// return new Model;
|
* ['price' => 99]);
|
||||||
// }
|
*
|
||||||
|
* @param $search Retrieve by
|
||||||
|
* @param $data Data used for creation
|
||||||
|
*
|
||||||
|
* @return Model The Model
|
||||||
|
*/
|
||||||
|
public static function updateOrCreate(array $search, array $data): Model
|
||||||
|
{
|
||||||
|
$model = self::firstOrNew($search, $data);
|
||||||
|
$model->fill($data);
|
||||||
|
$model->save();
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,18 @@
|
|||||||
namespace App\Model;
|
namespace App\Model;
|
||||||
|
|
||||||
class UserModel extends Model {
|
class UserModel extends Model {
|
||||||
|
|
||||||
protected $table = 'user';
|
protected $table = 'user';
|
||||||
|
|
||||||
|
//-------------------------------------//
|
||||||
|
|
||||||
|
public function loginAllowed(): bool
|
||||||
|
{
|
||||||
|
if (property_exists($this, 'failed_login_attempt') && $this->failed_login_attempt < 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
use \App\Classes\Config;
|
||||||
|
?>
|
||||||
|
<div class="content shadow p-4 mb-4">
|
||||||
|
<h3 class="mb-4">Cache</h3>
|
||||||
|
|
||||||
|
<?php if (Config::c('CLOUDFLARE_ENABLED') != '1') { ?>
|
||||||
|
To enable the Cloudflare cache options,
|
||||||
|
make sure to set the following option in the <p>config.php</p> file:
|
||||||
|
<pre class="line-numbers mb-4 language-php"><p class="language-php"><span class="token single-quoted-string string">'CLOUDFLARE_ENABLED'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'1'</span><span class="token punctuation">,</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></p></pre>
|
||||||
|
</p><?php } else { ?>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Cloudflare cache options:
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-9 col-md-10 col-lg-10 col-xl-10">
|
||||||
|
<h5>Purge CSS/JavaScript</h5>
|
||||||
|
<p class="mb-0">Granuarly remove .css and .js files from Cloudflare's cache.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-md-2 col-lg-2 col-xl-2">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<a class="js-purge btn btn-dark" href="<?= $this->purgeUrl; ?>"
|
||||||
|
data-type="css-js" data-token="<?= $this->csrfToken; ?>">Purge</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-9 col-md-10 col-lg-10 col-xl-10">
|
||||||
|
<h5>Purge Fonts/Images</h5>
|
||||||
|
<p class="mb-0">Granuarly remove font and images files from Cloudflare's cache.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-md-2 col-lg-2 col-xl-2">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<a class="js-purge btn btn-danger" href="<?= $this->purgeUrl; ?>"
|
||||||
|
data-type="fonts-images" data-token="<?= $this->csrfToken; ?>">Purge</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-9 col-md-10 col-lg-10 col-xl-10">
|
||||||
|
<h5>Purge All Files</h5>
|
||||||
|
<p class="mb-0">Remove ALL files from Cloudflare's cache.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-md-2 col-lg-2 col-xl-2">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<a class="js-purge btn btn-danger" href="<?= $this->purgeUrl; ?>"
|
||||||
|
data-type="all" data-token="<?= $this->csrfToken; ?>">Purge</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-9 col-md-10 col-lg-10 col-xl-10">
|
||||||
|
<h5>Enable Development Mode</h5>
|
||||||
|
<p class="mb-0">
|
||||||
|
This will bypass Cloudflare's accelerated cache and slow down your site,
|
||||||
|
but is useful if you are making changes to cacheable content
|
||||||
|
(like images, CSS, or JavaScript) and would like to see those changes right away.
|
||||||
|
Once entered, development mode will last for 3 hours and then automatically toggle off.
|
||||||
|
<?php $state = $this->config['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED']; ?>
|
||||||
|
<span id="develop-enabled" style="visibility: <?= $state == '1' ? 'visible' : 'hidden'; ?>;">
|
||||||
|
Enabled for another
|
||||||
|
<code id="develop-remaining">
|
||||||
|
<?= $state == '1' ? $this->config['enabled-remaining'] : ''; ?>
|
||||||
|
</code> hours.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 col-md-2 col-lg-2 col-xl-2">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<input type="checkbox" id="development-mode"
|
||||||
|
data-href="<?= $this->toggleUrl; ?>" data-token="<?= $this->csrfToken; ?>"
|
||||||
|
<?= $this->config['CLOUDFLARE_DEVELOPMENT_MODE_ENABLED'] ? 'checked' : ''; ?>>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
@@ -45,7 +45,10 @@
|
|||||||
|
|
||||||
<select name="<?= $name; ?>" class="custom-select" <?= $required; ?>>
|
<select name="<?= $name; ?>" class="custom-select" <?= $required; ?>>
|
||||||
<?php foreach($this->dropdownData[$key] as $dropdownKey => $value) { ?>
|
<?php foreach($this->dropdownData[$key] as $dropdownKey => $value) { ?>
|
||||||
<option value="<?= $dropdownKey; ?>"><?= $value; ?></option>
|
<option value="<?= $dropdownKey; ?>"
|
||||||
|
<?= ($dropdownKey == 0) ? 'disabled selected' : ''; ?>>
|
||||||
|
<?= $value; ?>
|
||||||
|
</option>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -56,8 +59,6 @@
|
|||||||
|
|
||||||
<input type="hidden" name="_token" value="<?= $this->csrfToken; ?>" />
|
<input type="hidden" name="_token" value="<?= $this->csrfToken; ?>" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
<select name="<?= $name; ?>" class="custom-select" <?= $required; ?>>
|
<select name="<?= $name; ?>" class="custom-select" <?= $required; ?>>
|
||||||
<?php foreach($this->dropdownData[$key] as $dropdownKey => $value) { ?>
|
<?php foreach($this->dropdownData[$key] as $dropdownKey => $value) { ?>
|
||||||
<option value="<?= $dropdownKey; ?>"
|
<option value="<?= $dropdownKey; ?>"
|
||||||
|
<?= ($dropdownKey == 0) ? 'disabled' : ''; ?>
|
||||||
<?= $this->model->$name == $dropdownKey ? 'selected' : ''; ?>>
|
<?= $this->model->$name == $dropdownKey ? 'selected' : ''; ?>>
|
||||||
<?= $value; ?>
|
<?= $value; ?>
|
||||||
</option>
|
</option>
|
||||||
@@ -61,8 +62,6 @@
|
|||||||
|
|
||||||
<input type="hidden" name="_token" value="<?= $this->csrfToken; ?>" />
|
<input type="hidden" name="_token" value="<?= $this->csrfToken; ?>" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,8 +64,6 @@
|
|||||||
<a class="btn btn-dark" href="<?= $this->url ?>/create">New <?= $this->title; ?></a>
|
<a class="btn btn-dark" href="<?= $this->url ?>/create">New <?= $this->title; ?></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="content shadow p-4 mb-4">
|
<div class="content shadow p-4 mb-4">
|
||||||
<h3><?= _exists([$this->model->title]) ? ($this->escape)($this->model->title) : 'Show'; ?></h3>
|
<h3><?= property_exists($this->model, 'title') ? ($this->escape)($this->model->title) : 'Show'; ?></h3>
|
||||||
|
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
<?php } ?>
|
<?php } ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,4 @@
|
|||||||
<?= !empty($this->user->last_name) ? ' ' . $this->user->last_name : ''; ?>
|
<?= !empty($this->user->last_name) ? ' ' . $this->user->last_name : ''; ?>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -84,7 +84,6 @@
|
|||||||
|
|
||||||
<?= $this->partial('../app/views/partials/pagination.php'); ?>
|
<?= $this->partial('../app/views/partials/pagination.php'); ?>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,6 +28,4 @@
|
|||||||
|
|
||||||
<div id="syntax-parse"><pre class="line-numbers mb-4"><code class=""></code></pre></div>
|
<div id="syntax-parse"><pre class="line-numbers mb-4"><code class=""></code></pre></div>
|
||||||
<textarea id="syntax-parse-copy" class="admin-hidden"></textarea>
|
<textarea id="syntax-parse-copy" class="admin-hidden"></textarea>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="row mt-4">
|
||||||
|
<?php $size = $this->sideContent ? '8' : '12'; ?>
|
||||||
|
<div class="col-12 col-md-<?= $size; ?> col-lg-<?= $size; ?>">
|
||||||
|
|
||||||
|
<div class="input-group mb-4">
|
||||||
|
<input type="text" name="blog-search" id="js-blog-search" class="form-control"
|
||||||
|
autofocus="" placeholder="Search" value="<?= $this->search; ?>" onfocus="this.select();" data-url="<?= $this->url; ?>">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button type="button" id="js-blog-search-button" class="btn btn-dark"><i class="fa fa-search"></i> Search</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="blog-posts">
|
||||||
|
<?= $this->partial($this->injectView); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -17,10 +17,6 @@
|
|||||||
<?php if ($this->injectView != '') { ?>
|
<?php if ($this->injectView != '') { ?>
|
||||||
<?= $this->partial($this->injectView); ?>
|
<?= $this->partial($this->injectView); ?>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
<?php if ($key === array_key_last($this->contents)) { ?>
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|||||||
@@ -3,6 +3,4 @@
|
|||||||
<p>Error <strong>404</strong>.</p>
|
<p>Error <strong>404</strong>.</p>
|
||||||
<p>requested URL <?= $_SERVER["REQUEST_URI"] ?> was not found on this server.</p>
|
<p>requested URL <?= $_SERVER["REQUEST_URI"] ?> was not found on this server.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,20 +9,20 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="description" content="<?= $this->metaDescription; ?>">
|
<meta name="description" content="<?= $this->metaDescription; ?>">
|
||||||
|
|
||||||
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
|
||||||
|
|
||||||
<?php if ($this->adminSection) { ?>
|
<?php if ($this->adminSection) { ?>
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/codemirror.min.css" rel="stylesheet" integrity="sha384-K/FfhVUneW5TdId1iTRDHsOHhLGHoJekcX6UThyJhMRctwRxlL3XmSnTeWX2k3Qe" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/codemirror.min.css" rel="stylesheet" integrity="sha384-zaeBlB/vwYsDRSlFajnDd7OydJ0cWk+c2OWybl3eSUf6hW2EbhlCsQPqKr3gkznT" crossorigin="anonymous">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/theme/tomorrow-night-eighties.min.css" rel="stylesheet" integrity="sha384-zTCWZYMg963D68otcZCn2SQ2SBwih+lCwYxWqvx6xH8/Wt6+NN+giHIvcMpN4cPD" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/theme/tomorrow-night-eighties.min.css" rel="stylesheet" integrity="sha384-zTCWZYMg963D68otcZCn2SQ2SBwih+lCwYxWqvx6xH8/Wt6+NN+giHIvcMpN4cPD" crossorigin="anonymous">
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.15/dist/summernote-bs4.min.css" rel="stylesheet" integrity="sha384-JNFvp1YkK/DsvVg1KxCYX/jfLcrqFkwUE1+4kt+Zpkhvfeetb13H+j2ZZhrTJwRy" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.20/summernote-bs4.min.css" rel="stylesheet" integrity="sha384-hqv27sxmxAI2L4eughLkUpjS75/Z3/hg9DOWIl0PJWE4B6GJqM2Kx72ZPoQzsUpF" crossorigin="anonymous">
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/themes/prism-tomorrow.min.css" rel="stylesheet" integrity="sha384-rG0ypOerdVJPawfZS6juq8t8GVE9oCCPJbOXV/bF+e61zYW9Ib6u9WwSbTOK6CKA" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" integrity="sha384-wFjoQjtV1y5jVHbt0p35Ui8aV8GVpEZkyF99OXWqP/eNJDU93D3Ugxkoyh6Y2I4A" crossorigin="anonymous">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" integrity="sha384-n3/UuPVL3caytud/opHXuyFoezGp2oAUB0foYaCAIs2QwGv/nV0kULHS2WAaJuxR" crossorigin="anonymous">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" integrity="sha384-nUkTNLI8COlMCRJ0FHIdX76If83145OTCLUx4gQyfnO0gGeO/sD9czGEUBxtkcUv" crossorigin="anonymous">
|
||||||
|
|
||||||
<link href="<?= Config::c('APP_URL'); ?>/css/style.css?v=<?= rand(); ?>" rel="stylesheet">
|
<link href="<?= Config::c('APP_URL'); ?>/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
<title><?= ($this->escape)($this->pageTitle); ?><?= $this->pageTitle != '' ? ' - ' : '' ?>Rick van Vonderen</title>
|
<title><?= ($this->escape)($this->pageTitle); ?><?= $this->pageTitle != '' ? ' - ' : '' ?>Rick van Vonderen</title>
|
||||||
<link rel="icon" type="image/png" href="<?= Config::c('APP_URL'); ?>/img/favicon.png">
|
<link rel="icon" type="image/png" href="<?= Config::c('APP_URL'); ?>/img/favicon.png">
|
||||||
|
|||||||
@@ -22,6 +22,4 @@
|
|||||||
<br>
|
<br>
|
||||||
<a href="<?= Config::c('APP_URL'); ?>/reset-password">Forgot password</a>?
|
<a href="<?= Config::c('APP_URL'); ?>/reset-password">Forgot password</a>?
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,12 @@
|
|||||||
<br>
|
<br>
|
||||||
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/admin/syntax-highlighting">Syntax Highlighting</a>
|
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/admin/syntax-highlighting">Syntax Highlighting</a>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h5>Config</h5>
|
||||||
|
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/admin/cache">Cache</a>
|
||||||
|
<br>
|
||||||
|
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/admin/config">Config</a>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/logout">Log out</a>
|
- <a href="<?= \App\Classes\Config::c('APP_URL'); ?>/logout">Log out</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
use App\Classes\Config;
|
||||||
|
?>
|
||||||
|
<?php foreach ($this->posts as $post) { ?>
|
||||||
|
<a class="clear" href="<?= Config::c('APP_URL') . '/' . $post['section'] . '/' . $post['page']; ?>">
|
||||||
|
<div class="content shadow p-4 mb-4" style="min-height: 0;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 <?= _exists($post, 'media_id') ? 'col-md-9' : ''; ?>">
|
||||||
|
<div class="d-flex flex-wrap align-items-baseline">
|
||||||
|
<h4 class="mr-3"><strong><?= $post['title']; ?></strong></h4>
|
||||||
|
<small class="mb-2 text-muted"><?= ($this->prettyTimestamp)($post['created_at']); ?></small>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<?= $post['content']; ?>
|
||||||
|
</p>
|
||||||
|
<?php if (_exists($post, 'tag')) { ?>
|
||||||
|
<small>
|
||||||
|
<i>tags: <?= ($this->tags)($post['tag']); ?></i>
|
||||||
|
</small>
|
||||||
|
<div class="d-md-none mb-3"></div>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
<?php if (_exists($post, 'media_id')) { ?>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<img src="<?= Config::c('APP_URL'). '/media/' . $post['filename'] . '.' . $post['extension']; ?>"
|
||||||
|
loading="lazy" class="w-100" style="height: 125px; object-fit: cover;">
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<?php } ?>
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
use App\Classes\Config;
|
use App\Classes\Config;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<footer class="mb-4">
|
<footer class="row mb-2">
|
||||||
<div class="row">
|
<div class="col-12">
|
||||||
<div class="col-12 col-lg-12">
|
|
||||||
© <?= date('Y'); ?> Rick van Vonderen
|
© <?= date('Y'); ?> Rick van Vonderen
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -48,6 +48,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="https://gitlab.com/riyyi" target="_blank"><i class="fa fa-gitlab"></i> GitLab</a>
|
<a class="nav-link" href="https://gitlab.com/riyyi" target="_blank"><i class="fa fa-gitlab"></i> GitLab</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="https://linkedin.com/in/rickvanvonderen" target="_blank">Linked <i class="fa fa-linkedin-square"></i></a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
use App\Classes\Config;
|
use App\Classes\Config;
|
||||||
?>
|
?>
|
||||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha384-i61gTtaoovXtAbKjo903+O55Jkn2+RtzHtvNez+yI49HAASvznhe9sZyjaSHTau9" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
|
||||||
<?php if ($this->adminSection) { ?>
|
<?php if ($this->adminSection) { ?>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/codemirror.min.js" integrity="sha384-v0AyVIspkm1uirQ6Nsmr90ScryXAWf+xv0DlNrNo1BkSY3sqr7tsUKLZyTMHqy2G" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/codemirror.min.js" integrity="sha384-CYXtjHe/lDQEYXvQVyIBaQsJ/mSdH193+qSBy4GHEQJX4qN2LAHI/vrze0AC73We" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/clike/clike.min.js" integrity="sha384-7LfH34Nu+Rz2rkqh9L9Okzi17vt5/sX9YZvMjHgRhwH94EdtzCaQ5SpIkXfn80/2" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/clike/clike.min.js" integrity="sha384-PgWELdtHCInKQO7E0Iuj74Ih+ksuRESaj8vjQhxDVdAhhqdnfmhOsQUy/cBFASN/" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/xml/xml.min.js" integrity="sha384-Fohp26Rl1xXN67RS6nTZ+s+DEgvUoMZGBTzPuAwW/tjDMtaL27W3b4Irtxtf9KPw" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/xml/xml.min.js" integrity="sha384-xPpkMo5nDgD98fIcuRVYhxkZV6/9Y4L8s3p0J5c4MxgJkyKJ8BJr+xfRkq7kn6Tw" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/javascript/javascript.min.js" integrity="sha384-QPVXEBl5e4kOafHL/KBYAhkf9c7iaD6svR+RGt92QYCbdJiKcmfC4weMMPLif77e" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/javascript/javascript.min.js" integrity="sha384-kmQrbJf09Uo1WRLMDVGoVG3nM6F48frIhcj7f3FDUjeRzsiHwyBWDjMUIttnIeAf" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/css/css.min.js" integrity="sha384-3UHVlVGKahbp3CTUk/YC+ICeNAx8Na6vIIrmH18NimXtGR/zF4Ce4F1d+gNPFh9a" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/css/css.min.js" integrity="sha384-fpeIC2FZuPmw7mIsTvgB5BNc8QVxQC/nWg2W+CgPYOAiBiYVuHe2E8HiTWHBMIJQ" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/htmlmixed/htmlmixed.min.js" integrity="sha384-C4oV1iL5nO+MmIRm+JoD2sZ03wIafv758LRO92kyg7AFvpvn8oQAra4g3mrw5+53" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/htmlmixed/htmlmixed.min.js" integrity="sha384-xYIbc5F55vPi7pb/lUnFj3wu24HlpAMZdtBHkNrb2YhPzJV3pX7+eqXT2PXSNMrw" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/php/php.min.js" integrity="sha384-WFPjfiKs+lVB1vp9fq9wEhsgi/5Z86jVLyaGMe2wnxa/3o8SEUCySeqglAaRqxnI" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/php/php.min.js" integrity="sha384-1FUwPY2kaZKXw258/9CYBSS+zcc3CPggxE1zLjmYYiOdkcOw3KcXH5VNJWWbjw2U" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/shell/shell.min.js" integrity="sha384-VzBcr5K83ctjdWufr/bGFmHLB7LbEhdN5dhKWvE3Q/H5Qfvz9dKPJgfHXeaq74da" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/shell/shell.min.js" integrity="sha384-dmPRq54XNtYnlnAo7QrwJnZNa5r0JP5gU8z4H0686Cgz37qrR2jmNaDHUXyMI4wn" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/mode/python/python.min.js" integrity="sha384-Wh2d9/yLlLvOQErKI4FDPRJXSklc6GlymSMyq37kJcVmfB73bJ33OqA2TpVHBisf" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/python/python.min.js" integrity="sha384-anASZwLFwg9fpwvqs21eRCiI1Jxce0cbRF4W0KPN+4qrqlFGnByppUAMd6GeZb+A" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.59.4/keymap/vim.min.js" integrity="sha384-MTJMQ129Rzid0TmDEIKZomitzagfahspgvwEWmm1s2ReXw8Evv5NTVf77JlmAOOM" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/keymap/vim.min.js" integrity="sha384-DlidtUdSavEUghGXpIXr2aM++HC3RMLDux675TC5dNunOL7pRBxKkrrkfarvcL8z" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.15/dist/summernote-bs4.min.js" integrity="sha384-sH3/pYu1soe3hR5e4aLT8/9wusU2x7Xt10VNfJ6n0VbsUkmkPf+3jI7So6USyROv" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.20/summernote-bs4.min.js" integrity="sha384-tG37zJAk+EcUTn0PLQkIE5Bbmkuna7/Spxvd1GU2kyY8kKGX5V2K+qGitOvd5Sm1" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/components/prism-core.min.js" data-manual integrity="sha384-BhpFhn+hhQohKi2ygOAJsVKyJu2q+QYydLKC7rqeMZLyEJFQl1DZieRX4/WxKUsJ" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" data-manual integrity="sha384-MXybTpajaBV0AkcBaCPT4KIvo0FzoCiWXgcihYsw4FUkEz0Pv3JGV6tk2G8vJtDc" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha384-FIz0ezhzXpErvkjCsQ+74Je6cuWUBoVubidnSGt498wjjEBaAV27BAISa0/GaAFq" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha384-Uq05+JLko69eOiPr39ta9bh7kld5PKZoU+fF7g0EXTAriEollhZ+DrN8Q/Oi8J2Q" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/plugins/line-numbers/prism-line-numbers.min.js" integrity="sha384-xktrwc/DkME39VrlkNS1tFEeq/S0JFbc8J9Q8Bjx7Xy16Z3NnmUi+94RuffrOQZR" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js" integrity="sha384-6QJu8apxMmB9TiPVWzYKF5pRgKcz7snO0/QU+MrWmgBLECQjoa6erxX2VQ5t41Jd" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.22.0/plugins/highlight-keywords/prism-highlight-keywords.min.js" integrity="sha384-Rk2xv6YOAfQH8z3ZAK37pgnQihXfgkER8B5EYhoFc+mMNPzf+t7g2J9U74FAvy2T" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/highlight-keywords/prism-highlight-keywords.min.js" integrity="sha384-Wchy6vgUSGGGXWUpUsb15Y8wXgk4snlp96dF6c2GvaSQ3LeYP7778HuJUDJXg/so" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script src="<?= Config::c('APP_URL'); ?>/js/app.js?v=<?= rand(); ?>"></script>
|
<script src="<?= Config::c('APP_URL'); ?>/js/app.js"></script>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<script src="<?= Config::c('APP_URL'); ?>/js/site.js?v=<?= rand(); ?>"></script>
|
<script src="<?= Config::c('APP_URL'); ?>/js/site.js"></script>
|
||||||
|
|||||||
@@ -9,6 +9,4 @@
|
|||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
<?= $this->partial($this->injectView); ?>
|
<?= $this->partial($this->injectView); ?>
|
||||||
|
|
||||||
<div class="pb-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
],
|
],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"App\\Classes\\": "app/classes/",
|
"App\\Classes\\": "app/classes/",
|
||||||
|
"App\\Classes\\Http\\": "app/classes/http/",
|
||||||
"App\\Controllers\\": "app/controllers/",
|
"App\\Controllers\\": "app/controllers/",
|
||||||
"App\\Model\\": "app/model/",
|
"App\\Model\\": "app/model/",
|
||||||
"App\\Traits\\": "app/traits/"
|
"App\\Traits\\": "app/traits/"
|
||||||
|
|||||||
@@ -14,4 +14,8 @@ return [
|
|||||||
'MAIL_NAME' => '',
|
'MAIL_NAME' => '',
|
||||||
'MAIL_USERNAME' => '',
|
'MAIL_USERNAME' => '',
|
||||||
'MAIL_PASSWORD' => '',
|
'MAIL_PASSWORD' => '',
|
||||||
|
|
||||||
|
'CLOUDFLARE_ENABLED' => '',
|
||||||
|
'CLOUDFLARE_TOKEN' => '',
|
||||||
|
'CLOUDFLARE_ZONE' => '',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ nav.shadow {
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
/* height: calc(100% - 56px); */
|
min-height: calc(100vh - 104px - .5rem);
|
||||||
min-height: calc(100vh - 80px);
|
|
||||||
padding: 20px 20px 50px 20px;
|
padding: 20px 20px 50px 20px;
|
||||||
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -58,6 +57,11 @@ nav.shadow {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.clear {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Anchor offset */
|
/* Anchor offset */
|
||||||
h3 {
|
h3 {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -124,12 +128,6 @@ h3 span {
|
|||||||
/*----------------------------------------*/
|
/*----------------------------------------*/
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: calc(100% - 30px);
|
|
||||||
height: 50px;
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #909090;
|
color: #909090;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
||||||
ini_set('display_errors', 1);
|
ini_set('display_errors', 1);
|
||||||
|
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|||||||
@@ -227,6 +227,87 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
$('.js-purge').on('click', function(event)
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!confirm('Are you sure you want to continue?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const purgeType = $(this).attr('data-type');
|
||||||
|
const csrfToken = $(this).attr('data-token');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: $(this).attr('href'),
|
||||||
|
type: 'POST',
|
||||||
|
data: { type: purgeType, _token: csrfToken },
|
||||||
|
success: function(data)
|
||||||
|
{
|
||||||
|
if (data == '') {
|
||||||
|
alert("Cache could not be purged!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = JSON.parse(data);
|
||||||
|
if (response.success == false) {
|
||||||
|
console.log(data);
|
||||||
|
alert("Cache could not be purged!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert("Cache has been purged.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Developer mode
|
||||||
|
$('#development-mode').on('click', function(e)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!confirm('Are you sure you want to continue?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataHref = $(this).attr('data-href');
|
||||||
|
const csrfToken = $(this).attr('data-token');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: dataHref,
|
||||||
|
type: 'POST',
|
||||||
|
data: { _token: csrfToken },
|
||||||
|
success: function(data)
|
||||||
|
{
|
||||||
|
if (data == '') {
|
||||||
|
alert("Development mode could not be enabled!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = JSON.parse(data);
|
||||||
|
if (response.success == false) {
|
||||||
|
console.log(data);
|
||||||
|
alert("Development mode could not be enabled!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.result.value == 'on') {
|
||||||
|
e.target.checked = true;
|
||||||
|
$('#develop-enabled').css('visibility', 'visible');
|
||||||
|
$('#develop-remaining').text('03:00:00');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e.target.checked = false;
|
||||||
|
$('#develop-enabled').css('visibility', 'hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
alert("Development mode has been set to: '" + response.result.value + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// @Todo
|
// @Todo
|
||||||
|
|||||||
@@ -12,6 +12,45 @@ $(document).ready(function() {
|
|||||||
return elementBottom > viewportTop && elementTop < viewportBottom;
|
return elementBottom > viewportTop && elementTop < viewportBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------//
|
||||||
|
// Blog search
|
||||||
|
|
||||||
|
function blogSearch(input)
|
||||||
|
{
|
||||||
|
var url = input.data("url");
|
||||||
|
var search = input.val();
|
||||||
|
window.location.href = url + '?search=' + encodeURIComponent(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#js-blog-search").keydown(function(e) {
|
||||||
|
if (e.key == 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
blogSearch($(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#js-blog-search-button").click(function() {
|
||||||
|
blogSearch($("#js-blog-search"));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#js-blog-search").on("input", function() {
|
||||||
|
var url = $(this).data("url");
|
||||||
|
var search = $(this).val();
|
||||||
|
if (search.length == 0 || search.length >= 3) {
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: 'search=' + encodeURIComponent(search),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
$("#blog-posts").empty().append(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//------------------------------------------//
|
//------------------------------------------//
|
||||||
|
|
||||||
// Image hover mouseenter
|
// Image hover mouseenter
|
||||||
@@ -31,6 +70,12 @@ $(document).ready(function() {
|
|||||||
$("#img-hover").remove();
|
$("#img-hover").remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Image hover on click
|
||||||
|
$(".js-img-hover").click(function() {
|
||||||
|
var url = $(this).data("img-hover");
|
||||||
|
window.open(url, '_blank');
|
||||||
|
});
|
||||||
|
|
||||||
// Lazy load video's
|
// Lazy load video's
|
||||||
$(window).on('resize scroll', function() {
|
$(window).on('resize scroll', function() {
|
||||||
$('.js-video').each(function(index) {
|
$('.js-video').each(function(index) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use \App\Classes\Router;
|
use \App\Classes\Router;
|
||||||
|
|
||||||
|
Router::resource('/admin/config', 'CrudController');
|
||||||
Router::resource('/admin/section', 'CrudController');
|
Router::resource('/admin/section', 'CrudController');
|
||||||
Router::resource('/admin/page', 'CrudController');
|
Router::resource('/admin/page', 'CrudController');
|
||||||
Router::resource('/admin/content', 'CrudController');
|
Router::resource('/admin/content', 'CrudController');
|
||||||
@@ -11,17 +12,22 @@ Router::resource('/admin/media', 'MediaController');
|
|||||||
|
|
||||||
// Basic routes
|
// Basic routes
|
||||||
return [
|
return [
|
||||||
// URL, controller, action, view/title/description
|
// URL, controller, action, [view, title, description]
|
||||||
['/', 'IndexController', '', ''],
|
['/', 'IndexController'],
|
||||||
['/img/captcha.jpg', 'IndexController', 'captcha', ''],
|
['/img/captcha.jpg', 'IndexController', 'captcha'],
|
||||||
['/robots.txt', 'IndexController', 'robots', ''],
|
['/robots.txt', 'IndexController', 'robots'],
|
||||||
['/sitemap.xml', 'IndexController', 'sitemap', ''],
|
['/sitemap.xml', 'IndexController', 'sitemap'],
|
||||||
|
['/blog', 'BlogController', 'search'],
|
||||||
|
['/blog.xml', 'BlogController', 'rss'],
|
||||||
['/login', 'LoginController', 'login', ['', 'Sign in', '']],
|
['/login', 'LoginController', 'login', ['', 'Sign in', '']],
|
||||||
['/reset-password', 'LoginController', 'reset', ['', 'Reset password', '']],
|
['/reset-password', 'LoginController', 'reset', ['', 'Reset password', '']],
|
||||||
['/logout', 'LoginController', 'logout', ''],
|
['/logout', 'LoginController', 'logout'],
|
||||||
['/admin', 'AdminController', '', ''],
|
['/admin', 'AdminController'],
|
||||||
['/admin/toggle', 'AdminController', 'toggle', ''],
|
['/admin/cache', 'CacheController', 'cache'],
|
||||||
['/admin/syntax-highlighting', 'AdminController', 'syntax', ''],
|
['/admin/cache/purge', 'CacheController', 'purge'],
|
||||||
|
['/admin/cache/toggle', 'CacheController', 'toggle'],
|
||||||
|
['/admin/toggle', 'AdminController', 'toggle'],
|
||||||
|
['/admin/syntax-highlighting', 'AdminController', 'syntax'],
|
||||||
['/test', 'TestController', '', ''],
|
['/test', 'TestController', '', ''],
|
||||||
// ["", "", "", ""],
|
// ["", "", "", ""],
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user