Compare commits

...
15 Commits
25 changed files with 305 additions and 181 deletions
+35 -101
View File
@@ -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
+112
View File
@@ -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&apos;s Webpage');
$channel->addChild('link', Config::c('APP_URL'));
$channel->addChild('description', 'Recent content on Rick&apos;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;
};
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Model;
class BlogModel extends Model {
protected $table = 'blog_post';
}
+1 -1
View File
@@ -98,7 +98,7 @@ abstract class Model {
protected static function query(string $query, array $parameters = [], protected static function query(string $query, array $parameters = [],
$type = ':'): ?array $type = ':'): ?array
{ {
if (substr_count($query, $type) != count($parameters)) { if ($type == '?' && substr_count($query, $type) != count($parameters)) {
return null; return null;
} }
+1 -4
View File
@@ -1,6 +1,5 @@
<?php <?php
use \App\Classes\Config;
use \App\Classes\Config;
?> ?>
<div class="content shadow p-4 mb-4"> <div class="content shadow p-4 mb-4">
<h3 class="mb-4">Cache</h3> <h3 class="mb-4">Cache</h3>
@@ -81,6 +80,4 @@ use \App\Classes\Config;
</div> </div>
</div> </div>
<?php } ?> <?php } ?>
<div class="pb-5"></div>
</div> </div>
+4 -3
View File
@@ -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>
+1 -2
View File
@@ -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>
-2
View File
@@ -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>
-2
View File
@@ -33,8 +33,6 @@
<?php } ?> <?php } ?>
</tbody> </tbody>
</table> </table>
<div class="pb-5"></div>
</div> </div>
</div> </div>
</div> </div>
-2
View File
@@ -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>
-1
View File
@@ -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>
-2
View File
@@ -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>
+17
View File
@@ -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>
-4
View File
@@ -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 } ?>
-2
View File
@@ -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>
+7 -7
View File
@@ -9,18 +9,18 @@
<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" rel="stylesheet"> <link href="<?= Config::c('APP_URL'); ?>/css/style.css" rel="stylesheet">
-2
View File
@@ -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>
+32
View File
@@ -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 -4
View File
@@ -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">
&copy; <?= date('Y'); ?> Rick van Vonderen &copy; <?= date('Y'); ?> Rick van Vonderen
</div> </div>
</div>
</footer> </footer>
+17 -18
View File
@@ -1,27 +1,26 @@
<?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"></script> <script src="<?= Config::c('APP_URL'); ?>/js/app.js"></script>
<?php } ?> <?php } ?>
-2
View File
@@ -9,6 +9,4 @@
<?php } ?> <?php } ?>
<?= $this->partial($this->injectView); ?> <?= $this->partial($this->injectView); ?>
<div class="pb-5"></div>
</div> </div>
+6 -8
View File
@@ -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
View File
@@ -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';
+45
View File
@@ -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) {
+14 -12
View File
@@ -12,20 +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/cache', 'CacheController', 'cache', ''], ['/admin/cache', 'CacheController', 'cache'],
['/admin/toggle', 'AdminController', 'toggle', ''], ['/admin/cache/purge', 'CacheController', 'purge'],
['/admin/cache/purge', 'CacheController', 'purge', ''], ['/admin/cache/toggle', 'CacheController', 'toggle'],
['/admin/cache/toggle', 'CacheController', 'toggle', ''], ['/admin/toggle', 'AdminController', 'toggle'],
['/admin/syntax-highlighting', 'AdminController', 'syntax', ''], ['/admin/syntax-highlighting', 'AdminController', 'syntax'],
['/test', 'TestController', '', ''], ['/test', 'TestController', '', ''],
// ["", "", "", ""], // ["", "", "", ""],
]; ];