%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /usr/local/sakura-blog/themes/theme-basic/orange/v1.1.5/
Upload File :
Create Path :
Current File : //usr/local/sakura-blog/themes/theme-basic/orange/v1.1.5/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);
	}
}

Zerion Mini Shell 1.0