%PDF- %PDF-
Direktori : /usr/local/sakura-blog/themes/theme-basic/gray/v1.1.3/ |
Current File : //usr/local/sakura-blog/themes/theme-basic/gray/v1.1.3/index.php |
<?php namespace SakuraBlog\Theme\Basic; use Error; use PDO; use SakuraBlog\Errors\NotFoundException; use SakuraBlog\GetArticleOptions; use SakuraBlog\GetArticlesCountOptions; use SakuraBlog\GetArticlesOptions; use SakuraBlog\GetCategoriesOptions; use SakuraBlog\GetPrevAndNextArticleIDsOptions; use SakuraBlog\GetTagRelatedArticlesOptions; use SakuraBlog\GetTagsOptions; use SakuraBlog\Models\Blog; use Throwable; use function SakuraBlog\createThumbnail; use function SakuraBlog\getArchives; use function SakuraBlog\getArticle; use function SakuraBlog\getArticles; use function SakuraBlog\getArticlesCount; use function SakuraBlog\getCategories; use function SakuraBlog\getCategoryBySlug; use function SakuraBlog\getLimitedArticle; use function SakuraBlog\getPrevAndNextArticleIDs; use function SakuraBlog\getTagBySlug; use function SakuraBlog\getTagRelatedArticles; use function SakuraBlog\getTagRelatedArticlesCount; use function SakuraBlog\getTags; require_once(__DIR__ . "/lib/sakura-blog-lib.php"); function start(string $blog_dir, array $settings, PDO $db) { $blog = new Blog($settings); $uri = "/"; if (isset($_SERVER["REQUEST_URI"])) { $uri = $_SERVER["REQUEST_URI"]; } $pos = strpos($uri, '?'); if ($pos !== false) { $uri = substr($uri, 0, $pos); } $uri = rawurldecode($uri); $routes = [ "/" => "index", "/articles/:id" => "show", "/privates/:access_key" => "private", "/search" => "search", "/categories/:slug" => "searchByCategory", "/tags/:slug" => "searchByTag", "/archives/:slug" => "searchByArchive", "/assets/:path" => "assets", "/thumbnails/:size/:path" => "thumbnails" ]; $c = new Controller($blog_dir, $blog, $db); try { foreach ($routes as $path => $handler) { $pattern = preg_quote($path, "/"); $pattern = preg_replace("/\\\:id/", "(?P<id>\d+)", $pattern); $pattern = preg_replace("/\\\:access_key/", "(?P<access_key>[a-zA-Z0-9]+)", $pattern); $pattern = preg_replace("/\\\:slug/", "(?P<slug>[a-zA-Z0-9-_]+)", $pattern); $pattern = preg_replace("/\\\:path/", "(?P<path>.+)", $pattern); $pattern = preg_replace("/\\\:size/", "(?P<size>(auto|[1-9]\d*x[1-9]\d*))", $pattern); $pattern = "^$pattern$"; if (preg_match("/" . $pattern . "/", $uri, $match)) { $args = null; if (isset($match["id"])) { $args = intval($match["id"]); } else if (isset($match["slug"])) { $args = $match["slug"]; } else if (isset($match["path"])) { $args = $match["path"]; } else if (isset($match["access_key"])) { $args = $match["access_key"]; } // FIXME... if (isset($match["size"])) { $args = [ "path" => $args, "size" => $match["size"], ]; } $c->$handler($args); return; } } throw new NotFoundException(); } catch (NotFoundException $e) { $c->notFound(); } catch (Throwable $e) { $c->internalServerError(); } } class Controller { private string $blog_dir; private PDO $db; private Blog $blog; function __construct(string $blog_dir, Blog $blog, PDO $db) { $this->blog_dir = $blog_dir; $this->db = $db; $this->blog = $blog; } function index() { $page = $this->getQueryPage(); $page_size = $this->getQueryPageSize(); $articles = getArticles( $this->db, (new GetArticlesOptions()) ->page($page)->pageSize($page_size) ->withAuthor(true) ->withCategory(true) ->withTags(true), ); $total = getArticlesCount($this->db); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $this->render("index", [ "blog" => $this->blog, "articles" => $articles, "page" => $page, "total" => $total, "total_pages" => $this->calcTotalPages($total, $page_size), "blog_profile" => $this->getBlogProfile(), "categories" => $categories, "tags" => $tags, "archives" => $archives, ]); } function show(int $id) { $article = getArticle( $this->db, $id, (new GetArticleOptions()) ->withAuthor(true) ->withCategory(true) ->withTags(true) ); [$prev, $next] = getPrevAndNextArticleIDs($this->db, $id); $prev_article = $next_article = null; if (!is_null($prev)) { $prev_article = getArticle($this->db, $prev); } if (!is_null($next)) { $next_article = getArticle($this->db, $next); } $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $this->render("show", [ "blog" => $this->blog, "article" => $article, "description" => $this->omitContent($article->content, 100, "..."), "navigation" => [ "prev" => $prev_article, "next" => $next_article, ], "blog_profile" => $this->getBlogProfile(), "categories" => $categories, "tags" => $tags, "archives" => $archives, "is_private" => false ]); } function private(string $access_key) { $article = getLimitedArticle( $this->db, $access_key, (new GetArticleOptions()) ->withAuthor(true) ->withCategory(true) ->withTags(true) ); [$prev, $next] = getPrevAndNextArticleIDs( $this->db, $article->id, (new GetPrevAndNextArticleIDsOptions())->isLimited(true), ); $prev_article = $next_article = null; if (!is_null($prev)) { $prev_article = getArticle($this->db, $prev); } if (!is_null($next)) { $next_article = getArticle($this->db, $next); } $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $this->render("show", [ "blog" => $this->blog, "article" => $article, "description" => $this->omitContent($article->content, 100, "..."), "navigation" => [ "prev" => $prev_article, "next" => $next_article, ], "blog_profile" => $this->getBlogProfile(), "categories" => $categories, "tags" => $tags, "archives" => $archives, "is_private" => true ]); } public function search() { $page = $this->getQueryPage(); $page_size = $this->getQueryPageSize(); $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $keyword = filter_input(INPUT_GET, "keyword"); if (is_null($keyword) || $keyword === "") { $articles = getArticles( $this->db, (new GetArticlesOptions()) ->page($page)->pageSize($page_size) ->withAuthor(true) ->withCategory(true) ->withTags(true), ); $total = getArticlesCount($this->db); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $this->render("search/search", [ "blog" => $this->blog, "articles" => $articles, "categories" => $categories, "tags" => $tags, "archives" => $archives, "page" => $page, "total" => $total, "total_pages" => $this->calcTotalPages($total, $page_size), "blog_profile" => $this->getBlogProfile(), ]); return; } $total = getArticlesCount( $this->db, (new GetArticlesCountOptions())->keyword($keyword) ); // 検索結果がない場合は新しい記事を表示する $option = (new GetArticlesOptions()) ->page($page)->pageSize($page_size) ->withAuthor(true) ->withCategory(true) ->withTags(true); $is_found = $total > 0; if ($is_found) { $option = $option->keyword($keyword); } else { $total = getArticlesCount($this->db); } $total_pages = $this->calcTotalPages($total, $page_size); $articles = getArticles($this->db, $option); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $this->render("search/result-keyword", [ "blog" => $this->blog, "keyword" => $keyword, "articles" => $articles, "page" => $page, "total" => $total, "total_pages" => $total_pages, "categories" => $categories, "tags" => $tags, "archives" => $archives, "blog_profile" => $this->getBlogProfile(), "is_found" => $is_found, ]); } public function searchByCategory($slug) { $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $category = getCategoryBySlug($this->db, $slug); $total = getArticlesCount( $this->db, (new GetArticlesCountOptions())->categoryID($category->id) ); $page = $this->getQueryPage(); $page_size = $this->getQueryPageSize(); $total_pages = $this->calcTotalPages($total, $page_size); $articles = getArticles( $this->db, (new GetArticlesOptions()) ->page($page)->pageSize($page_size) ->categoryID($category->id) ->withAuthor(true) ->withCategory(true) ->withTags(true) ); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $this->render("search/result-category", [ "blog" => $this->blog, "category" => $category, "articles" => $articles, "page" => $page, "total" => $total, "total_pages" => $total_pages, "categories" => $categories, "tags" => $tags, "archives" => $archives, "blog_profile" => $this->getBlogProfile(), ]); } public function searchByTag(string $slug) { $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $tag = getTagBySlug($this->db, $slug); $total = getTagRelatedArticlesCount($this->db, $tag->id); $page = $this->getQueryPage(); $page_size = $this->getQueryPageSize(); $total_pages = $this->calcTotalPages($total, $page_size); $articles = getTagRelatedArticles( $this->db, $tag->id, (new GetTagRelatedArticlesOptions()) ->page($page)->pageSize($page_size) ->withAuthor(true) ->withCategory(true) ->withTags(true) ); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $this->render("search/result-tag", [ "blog" => $this->blog, "tag" => $tag, "articles" => $articles, "page" => $page, "total" => $total, "total_pages" => $total_pages, "categories" => $categories, "tags" => $tags, "archives" => $archives, "blog_profile" => $this->getBlogProfile(), ]); } public function searchByArchive($year_month) { $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); $total = getArticlesCount( $this->db, (new GetArticlesCountOptions())->yearMonth($year_month) ); $page = $this->getQueryPage(); $page_size = $this->getQueryPageSize(); $total_pages = $this->calcTotalPages($total, $page_size); $articles = getArticles( $this->db, (new GetArticlesOptions()) ->page($page)->pageSize($page_size) ->yearMonth($year_month) ->withAuthor(true) ->withCategory(true) ->withTags(true) ); foreach ($articles as $article) { $article->content = $this->omitContent($article->content, 104, "..."); $articles->update($article->id, $article); } $this->render("search/result-archive", [ "blog" => $this->blog, "year_month" => $year_month, "articles" => $articles, "page" => $page, "total" => $total, "total_pages" => $total_pages, "categories" => $categories, "tags" => $tags, "archives" => $archives, "blog_profile" => $this->getBlogProfile(), ]); } public function assets($path) { $assets_path = __DIR__ . "/assets/"; $path = $assets_path . $path; $rpath = realpath($path); if ($rpath === false || !str_starts_with($rpath, $assets_path)) { http_response_code(404); exit(); } $content_type = ""; if (str_ends_with($rpath, ".css")) { $content_type = "text/css"; } else if (str_ends_with($rpath, ".js")) { $content_type = "text/javascript"; } else { $content_type = mime_content_type($rpath); } if ($content_type !== "") { header("Content-Type: " . $content_type); } echo file_get_contents($rpath); } private function parseThumbnailSize(string $size_str): array|false { $availables = [ "auto", "640x360", "{$this->blog->thumbnail_size}", ]; foreach ($availables as $size) { if ($size_str === $size) { if ($size_str === "auto") { $size_str = "{$this->blog->thumbnail_size}"; } list($width_str, $height_str) = explode("x", $size_str); $width = intval($width_str); $height = intval($height_str); return [$width, $height]; } } return false; } public function thumbnails($args) { $path = $args["path"]; $files_path = $this->blog_dir . "/files/"; $src_path = $files_path . $path; $src_rpath = realpath($src_path); if ($src_rpath === false || !str_starts_with($src_rpath, $files_path) || $src_rpath !== $src_path) { http_response_code(404); exit(); } $size_str = $args["size"]; $size = $this->parseThumbnailSize($size_str); if ($size === false) { http_response_code(404); exit(); } list($max_width, $max_height) = $size; $thumbnails_path = $this->blog_dir . "/thumbnails/{$size_str}/"; if (!is_dir($thumbnails_path)) { if (mkdir($thumbnails_path, 0755, true, null) === false) { throw new Error("failed to thumbnails directory"); } } $content_type = ""; if (preg_match('/\.jpe?g$/i', $src_rpath)) { $content_type = "image/jpeg"; } else if (preg_match('/\.png$/i', $src_rpath)) { $content_type = "image/png"; } else if (preg_match('/\.gif$/i', $src_rpath)) { $content_type = "image/gif"; } else if (preg_match('/\.bmp$/i', $src_rpath)) { $content_type = "image/bmp"; } else { http_response_code(404); exit(); } if ($content_type !== "") { header("Content-Type: " . $content_type); } createThumbnail($src_rpath, $thumbnails_path . $path, $max_width, $max_height); echo file_get_contents($thumbnails_path . $path); } public function notFound() { $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); $archives = getArchives($this->db); http_response_code(404); $this->render("not-found", [ "blog" => $this->blog, "blog_profile" => $this->getBlogProfile(), "categories" => $categories, "tags" => $tags, "archives" => $archives, ]); } public function internalServerError() { $categories = $this->getAllUsingCategories(); $tags = $this->getAllUsingTags(); http_response_code(500); $this->render("internal-server-error", [ "blog" => $this->blog, "categories" => $categories, "tags" => $tags, ]); } private function getBlogProfile() { return [ "name" => $this->blog->title, "description" => $this->blog->description, "profile_image_filename" => $this->blog->icon_filename, "sns" => [ "x_username" => $this->blog->sns->x_username, "facebook_username" => $this->blog->sns->facebook_username, "instagram_username" => $this->blog->sns->instagram_username, ], ]; } private function getAllUsingCategories() { return getCategories( $this->db, (new GetCategoriesOptions()) ->excludeUnused(true) ); } private function getAllUsingTags() { return getTags( $this->db, (new GetTagsOptions()) ->excludeUnused(true) ); } private function render(string $template, array $ctx = []) { $template_path = __DIR__ . "/templates/pages/$template.php"; if (!file_exists($template_path)) { throw new Error("template not found"); } extract($ctx); include($template_path); } private function getQueryInt(string $key, int $default) { $v = filter_input(INPUT_GET, $key, FILTER_VALIDATE_INT); if ($v === NULL || $v === false) { return $default; } return $v; } private function getQueryPage() { return $this->getQueryInt("page", 1); } private function getQueryPageSize() { return $this->getQueryInt("page_size", $this->blog->page_size) ?: 10; } private function calcTotalPages(int $total, int $page_size) { return max((int) ceil($total / $page_size), 1); } private function omitContent(string $content, int $length, string $ellipsis): string { return mb_strimwidth( $this->compressSpaces(strip_tags($content)), 0, $length, $ellipsis ); } private function compressSpaces(string $s): string { return preg_replace("/\s+/", " ", $s); } }