2024-10-22 12:26:08 +02:00
|
|
|
<?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 {
|
2024-12-05 16:03:11 +01:00
|
|
|
if (!empty($this->mdFileCache[$slug]) && file_exists($this->mdFileCache[$slug]['file'])) {
|
2024-10-22 12:26:08 +02:00
|
|
|
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 {
|
2024-12-05 16:03:11 +01:00
|
|
|
if (isset($this->mdFileCache[$slug])) {
|
|
|
|
return $this->mdFileCache[$slug];
|
|
|
|
}
|
|
|
|
return [];
|
2024-10-22 12:26:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2024-12-05 16:03:11 +01:00
|
|
|
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]);
|
|
|
|
}
|
2024-10-22 12:26:08 +02:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|