zap/app/Models/Page.php

167 lines
5.6 KiB
PHP

<?php
namespace App\Models;
use App\Render\BladeRenderer;
use App\Render\MarkdownRenderer;
use App\Render\Renderer;
use App\SiteConfiguration;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
use League\CommonMark\MarkdownConverter;
use function Illuminate\Filesystem\join_paths;
// TODO: Load page's frontmatter and replace things like
// title and layout from it. Also set any default,
// values for things like layout from the config
// if they're not present on the page.
class Page
{
public string $filename;
public string $fileExtension;
public string $directory;
public string $title;
public string $slug;
public bool $isIndex;
public string $key;
protected Filesystem $sourceDisk;
public ?string $layout;
public ?string $collection = null;
public string $entityName; // The name of this page. Ex: projects/myproject = myproject
protected string $content;
public function __construct(public string $path)
{
$this->sourceDisk = Storage::disk('source');
$this->filename = basename($this->path);
$this->fileExtension = (str_ends_with($this->filename, '.blade.php')) ? 'blade.php' : pathinfo($this->filename, PATHINFO_EXTENSION);
$this->fileExtension = str($this->fileExtension)->trim('.')->toString();
$this->title = str($this->filename)->remove("." . $this->fileExtension)->title()->toString();
$this->slug = str($this->title)->slug();
$this->isIndex = str($this->filename)->remove("." . $this->fileExtension)->toString() == "index";
$this->directory = dirname($this->path);
$this->key = str($this->path)->remove("." . $this->fileExtension)->replace("/", ".")->lower()->toString();
$this->entityName = $this->isIndex ? basename($this->directory) : $this->slug;
$this->detectCollection();
$this->fillAdditionalData();
}
// TODO: Do something with the [params]
// from the collection name if present.
// Maybe store it in a $collectionParams
// array on the page or something.
public function detectCollection()
{
$key_parts = explode('.', $this->key);
$collections = SiteConfiguration::collections();
foreach ($collections as $c) {
$parts = explode('.', $c);
// If we have key: projects.something
// and collection projects.[project_name].docs
// then it's not it
if (count($parts) > $key_parts) {
continue;
}
// Get a subset of max length key parts
$key_compare = array_slice($key_parts, 0, count($parts));
$match = true;
foreach ($key_compare as $i => $v) {
// TODO: Use regex here to search for [] instead of just [
if ($v !== $parts[$i] && !str($parts[$i])->contains("[")) {
$match = false;
break;
}
}
if ($match) $this->collection = $c;
}
}
/**
* Grab data from the file's front matter
* as well as the config and overwrite
* or fill in any missing attributes.
*
* @return void
*/
public function fillAdditionalData()
{
// If we have a collection, get the settings from it
if (!empty($this->collection)) {
$config = SiteConfiguration::getConfig()['collections'][$this->collection];
// if ($this->collection == "projects") {
// dd($config);
// }
$fillIfEmpty = ['layout'];
foreach ($fillIfEmpty as $field) {
if (empty($this->$field) && !empty($config[$field])) {
$this->$field = $config[$field];
}
}
}
// Grab front matter data if any
if ($this->fileExtension == 'md') {
// Build the markdown parser
$mdEnvironment = new Environment();
$mdEnvironment->addExtension(new CommonMarkCoreExtension);
$mdEnvironment->addExtension(new FrontMatterExtension);
$parser = new MarkdownConverter($mdEnvironment);
$parsed = $parser->convert($this->content());
if ($parsed instanceof RenderedContentWithFrontMatter) {
$frontMatter = $parsed->getFrontMatter();
foreach ($frontMatter as $field => $value) {
$this->$field = $value;
}
}
}
// If layout is still empty just set it to main
if (empty($this->layout)) $this->layout = 'main';
}
public function renderer(): Renderer
{
return ($this->fileExtension == 'blade.php') ? new BladeRenderer : new MarkdownRenderer;
}
public function render(): string
{
return $this->renderer()->render($this);
}
public function outputPath(): string
{
$baseDirectory = $this->directory;
if ($this->isIndex) {
return join_paths($baseDirectory, "index.html");
}
return join_paths($baseDirectory, "{$this->slug}", "index.html");
}
public function content(): string
{
if (!empty($this->content)) {
return $this->content;
}
$content = Storage::disk('source')->get($this->path);
$this->content = $content;
return $content;
}
}