KT-personal-website/app/Blogs/Blogs.php
2024-12-05 16:15:14 +01:00

296 lines
9.8 KiB
PHP

<?php
namespace App\Blogs;
use Cocur\Slugify\Slugify;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Mention\MentionExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\MarkdownConverter;
class Blogs {
private string $postsDir = __DIR__ . '/../../datas/posts';
private string $cacheFile = __DIR__ . '/../../cache/fileIndex.json';
private array $mdFileCache = [];
private int $totalPost = 0;
private int $postPerPage;
public int $totalPage = 0;
private array $postList = [];
public string $postsHash;
private array $params;
private int $numberEntry;
private $notFound = [
'file' => __DIR__ . '/../../datas/pages/404.md',
'filename' => '404',
'created_at' => '1970-01-01',
'modified_at' => '1970-01-01',
];
/**
* Constructs a new instance of the class
*
* @param array $params An array of parameters
*/
function __construct($params) {
if (!isset($params['config']['cron'])) {
$params['config']['cron'] = false;
}
if (file_exists($this->cacheFile) && $params['config']['cron'] === false) {
$mdFileCache = json_decode(file_get_contents($this->cacheFile), true);
} else {
$mdFileCache = $this->indexMdFiles();
}
$this->postPerPage = $params['config']['postPerPage'];
$this->totalPost = count($mdFileCache);
$this->totalPage = ceil($this->totalPost / $this->postPerPage);
$this->numberEntry = $params['config']['numberOfLastItem'];
$this->mdFileCache = $mdFileCache;
}
/**
* Indexes all the markdown files in the posts directory
*
* @return array The indexed markdown files
*/
private function indexMdFiles(): array {
$directory = new \RecursiveDirectoryIterator($this->postsDir);
$slugify = new Slugify();
foreach (new \RecursiveIteratorIterator($directory) as $file) {
if ($file->getExtension() === 'md') {
$filename = $file->getBasename('.md');
$pathParts = explode(DIRECTORY_SEPARATOR, $file->getPath());
$year = $pathParts[count($pathParts) - 2];
$month = $pathParts[count($pathParts) - 1];
$nameParts = explode(' - ', $filename);
$day = $nameParts[0];
if (empty($nameParts['1'])) {
continue;
}
$slug = $slugify->slugify(trim($nameParts[1]));
$createDate = \DateTimeImmutable::createFromFormat("Y-m-d H:i:s", "$year-$month-$day" . ' ' . date('H:i:s'));
$created_at = $createDate->format("Y-m-d");
$modifiedDate = \DateTimeImmutable::createFromFormat("U", $file->getMTime());
$modified_at = $modifiedDate->format(\DateTimeImmutable::ATOM);
$this->mdFileCache[$slug] = [
'file' => $file->getPathname(),
'filename' => $filename,
'title' => ucfirst(trim($nameParts[1])),
'year' => $year,
'month' => $month,
'created_at' => $created_at,
'modified_at' => $modified_at
];
}
}
uasort($this->mdFileCache, function ($a, $b) {
return strtotime($b['created_at']) <=> strtotime($a['created_at']);
});
file_put_contents($this->cacheFile, json_encode($this->mdFileCache));
return $this->mdFileCache;
}
/**
* Retrieves the markdown files for a given page
*
* @param int $page The page number
* @return array An array of markdown files
*/
function getFilesForPage(int $page): array {
if ($page > $this->totalPage) {
$page = $this->totalPage;
}
$offset = ($page - 1) * $this->postPerPage;
return array_slice($this->mdFileCache, $offset, $this->postPerPage, true);
}
/**
* Finds a markdown file by slug and returns its content
*
* @param string $slug The slug of posts/page
* @return string The content of the markdown
*/
public function findPostBySlug(string $slug): string {
if (!empty($this->mdFileCache[$slug]) && file_exists($this->mdFileCache[$slug]['file'])) {
return file_get_contents($this->mdFileCache[$slug]['file']);
} else {
return file_get_contents($this->notFound['file']);
}
}
/**
* Finds all the markdown files for a given year
*
* @param int $year
* @return array An array of markdown file
*/
public function findPostByYear(int $year): array {
$result = array();
foreach ($this->mdFileCache as $key => $post) {
if ((int)$post['year'] === $year) {
$result[$key] = $post;
}
}
$this->mdFileCache = $result;
$this->totalPage = ceil(count($this->mdFileCache) / $this->postPerPage);
return $result;
}
/**
* Finds all the markdown files for a given month
*
* @param int $month
* @return array An array of markdown file
*/
public function findPostByMonth(int $month): array {
$result = array();
foreach ($this->mdFileCache as $key => $post) {
if ((int)$post['year'] === $month) {
$result[$key] = $post;
}
}
$this->mdFileCache = $result;
$this->totalPage = ceil(count($this->mdFileCache) / $this->postPerPage);
return $result;
}
/**
* Returns the information of a markdown file for given slug
*
* @param string $slug The slug of post/page
* @return array The information of the markdown file
*/
public function returnPostInfo(string $slug): array {
if (isset($this->mdFileCache[$slug])) {
return $this->mdFileCache[$slug];
}
return [];
}
/**
* Returns the most recent post
*
* @return array An array of the most recent mdFileCache
*/
public function getLastPost(): array {
if ($this->totalPost > 0) {
$this->postList = array_slice($this->mdFileCache, 0, $this->numberEntry);
}
return $this->postList;
}
/**
* Generates the HTML for the pagination links
*
* @param int $page The current page number
* @return string The generated HTML for the pagination links
*/
public function makePagination(int $page): string {
$html = '';
for ($i = 1; $i <= $this->totalPage; $i++) {
if ($i === $page) {
$class = 'class="current" ';
} else {
$class = null;
}
$html .= '<li>
<a ' . $class . 'href="/posts/' . $i . '">' . $i . '</a>
</li>';
}
return $html;
}
/**
* Generates the HTML for the list of posts
*
* @return string|null The generated HTML for the list of posts or null
*/
public function makeList(): string {
if (!empty($this->postList)) {
$htmlPost = '<ul data-lastUpdate="' . date("Y-m-d") . '">';
foreach ($this->postList as $key => $value) {
$htmlPost .=
'
<li>
<a href="' . '/post/' . $key . '">' . $value['title'] . '</a>
</li>';
}
$htmlPost .= '
</ul>';
$this->postsHash = sha1($htmlPost);
return $htmlPost;
}
$this->postsHash = sha1('empty');
return '';
}
/**
* Converts markdown content to HTML
*
* @param string $content The markdown content to convert to HTML
* @return string The converted HTML content
*/
function markdownToHtml(string $content): string {
$config = [
'disallowed_raw_html' => [
'disallowed_tags' => ['title', 'textarea', 'style', 'xmp', 'iframe', 'noembed', 'noframes', 'script', 'plaintext'],
],
'table' => [
'wrap' => [
'enabled' => false,
'tag' => 'div',
'attributes' => [],
],
'alignment_attributes' => [
'left' => ['align' => 'left'],
'center' => ['align' => 'center'],
'right' => ['align' => 'right'],
],
],
'mentions' => [
'tag_handle' => [
'prefix' => '#',
'pattern' => '[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}(?!\w)',
'generator' => '/tag/%s',
]
],
];
$environment = new Environment($config);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new MentionExtension());
$environment->addExtension(new TableExtension());
$converter = new MarkdownConverter($environment);
return $converter->convert($content);
}
/**
* Extracts the lead paragraph from a post
*
* @param string $markdownContent The markdown content to extract the lead paragraph from
* @return string|null The extracted lead paragraph or null
*/
static function extractLead(string $markdownFile): ?string {
if (file_exists($markdownFile)) {
$markdownContent = file_get_contents($markdownFile);
$pattern = '/---\s*(.*?)\s*---/s';
if (preg_match($pattern, $markdownContent, $matches)) {
return trim($matches[1]);
}
}
return null;
}
}