diff --git a/app/controllers/BlogController.php b/app/controllers/BlogController.php new file mode 100644 index 0000000..23f595e --- /dev/null +++ b/app/controllers/BlogController.php @@ -0,0 +1,63 @@ +<?php + +namespace App\Controllers; + +use App\Model\BlogModel; + +class BlogController extends PageController { + + public function indexAction(): void + { + $query = $this->router->request()->param('search', ''); + $posts = $this->search($query); + + $this->defineHelpers(); + + $this->router->service()->search = $query; + $this->router->service()->posts = $posts; + $this->router->service()->injectView = $this->views . '/partials/blog-posts.php'; + parent::view('blog', 'Hello'); + } + + public function searchAction(): void + { + $query = $this->router->request()->param('query', ''); + $posts = $this->search($query); + + $this->defineHelpers(); + + $this->router->service()->posts = $posts; + $this->router->service()->partial($this->views . '/partials/blog-posts.php', $posts); + } + + //-------------------------------------// + + private function search(string $query): ?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 = 0 AND + (blog_post.title LIKE :query OR blog_post.tag LIKE :query) + ', [[':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): array { + // Remove empty elements via array_filter() + return array_filter(explode(':', $tags)); + }; + } + + +} diff --git a/app/model/BlogModel.php b/app/model/BlogModel.php new file mode 100644 index 0000000..f6fa299 --- /dev/null +++ b/app/model/BlogModel.php @@ -0,0 +1,9 @@ +<?php + +namespace App\Model; + +class BlogModel extends Model { + + protected $table = 'blog_post'; + +} diff --git a/app/views/admin/cache.php b/app/views/admin/cache.php index 9ca138d..d3600fd 100644 --- a/app/views/admin/cache.php +++ b/app/views/admin/cache.php @@ -1,6 +1,5 @@ <?php - -use \App\Classes\Config; + use \App\Classes\Config; ?> <div class="content shadow p-4 mb-4"> <h3 class="mb-4">Cache</h3> diff --git a/app/views/blog.php b/app/views/blog.php new file mode 100644 index 0000000..c09f342 --- /dev/null +++ b/app/views/blog.php @@ -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> diff --git a/app/views/partials/blog-posts.php b/app/views/partials/blog-posts.php new file mode 100644 index 0000000..9654423 --- /dev/null +++ b/app/views/partials/blog-posts.php @@ -0,0 +1,38 @@ +<?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: + <?php $tags = ($this->tags)($post['tag']); ?> + <?php foreach ($tags as $key => $tag) { ?> + <?= $tag . (($key === array_key_last($tags)) ? '' : ', '); ?> + <?php } ?> + </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 } ?> diff --git a/public/css/style.css b/public/css/style.css index 0a8a1c7..3a2b4d9 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -57,6 +57,11 @@ nav.shadow { cursor: pointer; } +a.clear { + color: inherit; + text-decoration: none; +} + /* Anchor offset */ h3 { position: relative; diff --git a/public/js/site.js b/public/js/site.js index ef22a2c..13f8273 100644 --- a/public/js/site.js +++ b/public/js/site.js @@ -12,6 +12,39 @@ $(document).ready(function() { return elementBottom > viewportTop && elementTop < viewportBottom; } + //------------------------------------------// + // Blog search + + function blogSearch(input) + { + var url = input.data("url"); + var search = input.val(); + window.location.href = url + '?search=' + 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 + '/search?query=' + search) + .then(response => response.text()) + .then(data => { + $("#blog-posts").empty().append(data); + }); + } + }); + //------------------------------------------// // Image hover mouseenter diff --git a/route.php b/route.php index e12df84..8061f6c 100644 --- a/route.php +++ b/route.php @@ -17,6 +17,8 @@ return [ ['/img/captcha.jpg', 'IndexController', 'captcha'], ['/robots.txt', 'IndexController', 'robots'], ['/sitemap.xml', 'IndexController', 'sitemap'], + ['/blog', 'BlogController'], + ['/blog/search', 'BlogController', 'search'], ['/login', 'LoginController', 'login', ['', 'Sign in', '']], ['/reset-password', 'LoginController', 'reset', ['', 'Reset password', '']], ['/logout', 'LoginController', 'logout'],