diff --git a/app/Classes/Bundle.php b/app/Classes/Bundle.php
index 561ff03..e68fc3b 100644
--- a/app/Classes/Bundle.php
+++ b/app/Classes/Bundle.php
@@ -164,11 +164,13 @@ public function exists(): bool
/**
* Load everything
*/
- public function load(): void
+ public function load(): self
{
$this->loadAttachments();
$this->loadMetadata();
$this->loadMarkdown();
+
+ return $this;
}
/**
@@ -208,14 +210,11 @@ public function repair(): bool
public function touch()
{
- $this->metadata()->set('lastModified', now()->toIso8601String());
- $this->saveMetadata();
+ // $parent = $this->getParent();
- $parent = $this->getParent();
-
- if (!empty($parent)) {
- $parent->touch();
- }
+ // if (!empty($parent)) {
+ // $parent->touch();
+ // }
}
public function render()
@@ -223,9 +222,6 @@ public function render()
$renderer = BundleRenderer::getBundleRendererFor($this);
$result = $renderer->render();
- $this->metadata()->set('lastRendered', now()->toIso8601String());
- $this->metadata()->save();
-
return $result;
}
@@ -275,6 +271,33 @@ public static function findBundles(FilesystemAdapter $disk, ?string $path = '/',
return $bundles;
}
+ public static function getFeedItems(FilesystemAdapter $disk)
+ {
+ $subBundles = Bundle::findBundles($disk, '/', true);
+
+ $subBundles = collect($subBundles)
+ ->filter(fn (Bundle $bundle) => !empty($bundle->metadata()->get('date')))
+ ->sort(function (Bundle $bundleA, Bundle $bundleB) {
+ return Carbon::parse($bundleA->metadata()->get('date'))->lt(Carbon::parse($bundleB->metadata()->get('date')));
+ })
+ ->map(fn (Bundle $bundle) => $bundle->load())
+ ->take(10);
+
+ return $subBundles;
+ }
+
+ public static function renderFeed(FilesystemAdapter $disk)
+ {
+ $lastBundles = static::getFeedItems($disk);
+
+ return [
+ '/index.xml' => (string) view('feed', [
+ 'bundles' => $lastBundles,
+ 'lastBuildDate' => now()->toRssString(),
+ ]),
+ ];
+ }
+
private function repairCover()
{
$cover = $this->metadata()->get('cover');
diff --git a/app/Console/Commands/Bundle/Render.php b/app/Console/Commands/Bundle/Render.php
index 01ecc25..fa4d1cd 100644
--- a/app/Console/Commands/Bundle/Render.php
+++ b/app/Console/Commands/Bundle/Render.php
@@ -36,6 +36,7 @@ public function __construct()
{ --r|recursive : Also render sub-bundles }
{ --source-disk= : Use specified content disk - Defaults to ' . env('CONTENT_DISK') . ' }
{ --target-disk= : Use specified rendering disk - Defaults to ' . env('DIST_DISK') . ' }
+ { --validate : Perform validations before rendering }
{ path? : Path to a specific bundle to render - Default to / }
';
@@ -51,6 +52,8 @@ public function handle()
->selectDisk()
->renderAssets()
->clearCache()
+ ->validate()
+ ->renderFeed()
->render()
->deploy();
}
@@ -137,6 +140,23 @@ private function clearCache(): self
return $this;
}
+ private function validate(): self
+ {
+ if (!$this->option('validate')) {
+ return $this;
+ }
+
+ $result = $this->call('bundle:validate', ['--recursive' => true]);
+
+ if (!empty($result)) {
+ if (confirm('Validation errors have occurred. Cancel process?')) {
+ exit(1);
+ }
+ }
+
+ return $this;
+ }
+
/**
* Collect a list of bundles to render
*/
@@ -153,7 +173,7 @@ private function getBundles()
$this->output->write('Collecting bundles... ');
if ($this->option('recursive')) {
- $bundles = Bundle::findBundles($this->sourceDisk, $path, true);
+ $bundles = Bundle::findBundles($this->sourceDisk, $path, true, false);
} else {
$bundles = [new Bundle($path, $this->sourceDisk)];
}
@@ -163,6 +183,19 @@ private function getBundles()
return $bundles;
}
+ private function renderFeed(): self
+ {
+ $result = Bundle::renderFeed($this->sourceDisk);
+
+ foreach ($result as $path => $content) {
+ $this->output->write(sprintf('Rendering %s as feed... ', $path));
+ $this->targetDisk->put($path, $content);
+ $this->info('OK');
+ }
+
+ return $this;
+ }
+
/**
* Perform rendering
*/
@@ -185,7 +218,6 @@ private function render(): self
private function handleBundle(Bundle $bundle, $progress)
{
$progress->label(sprintf('Rendering %s ...', $bundle->getPath()));
- $progress->hint('Rendering the bundle');
if ($this->option('dry-run')) {
return;
@@ -198,8 +230,6 @@ private function handleBundle(Bundle $bundle, $progress)
$path = Str::finish($path, '/') . 'index.html';
}
- $progress->hint(sprintf('Storing %s', $path));
-
$this->targetDisk->put($path, $content);
}
}
diff --git a/app/Services/BundleRenderers/Renderers/BaseRenderer.php b/app/Services/BundleRenderers/Renderers/BaseRenderer.php
index 99e4cb5..8020e80 100644
--- a/app/Services/BundleRenderers/Renderers/BaseRenderer.php
+++ b/app/Services/BundleRenderers/Renderers/BaseRenderer.php
@@ -7,7 +7,11 @@
use App\Classes\Link;
use App\Services\BundleRenderers\Contracts\RendersBundle;
use Carbon\Carbon;
+use DOMXPath;
use Exception;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Str;
+use Masterminds\HTML5;
abstract class BaseRenderer implements RendersBundle
{
@@ -44,17 +48,17 @@ public function render()
$this->prepareRender($currentPage);
if ($currentPage === 1) {
- $result[$this->bundle->getPath()] = $this->renderView();
+ $result[$this->bundle->getPath()] = $this->renderPage();
}
$page = sprintf('%spage/%s/', $this->bundle->getPath(), $currentPage);
- $result[$page] = $this->renderView();
+ $result[$page] = $this->renderPage();
}
} else {
$this->prepareRender(1);
- $result[$this->bundle->getPath()] = $this->renderView();
+ $result[$this->bundle->getPath()] = $this->renderPage();
}
return $result;
@@ -100,6 +104,18 @@ protected function prepareRender(int $currentPage = 1)
data_set($this->viewData, 'cover', $cover ? $cover->render() : null);
data_set($this->viewData, 'body', $this->bundle->markdown()->render());
+ data_set($this->viewData, 'feedIsValid', Cache::get('feed_is_valid', false));
+ data_set($this->viewData, 'cssIsValid', Cache::get('css_is_valid', false));
+
+ if ($currentPage === 1) {
+ $path = $this->bundle->getPath();
+ } else {
+ $path = sprintf('%spage/%s/', $this->bundle->getPath(), $currentPage);
+ }
+
+ $cacheKey = sprintf('html_is_valid_%s', Str::slug($path));
+
+ data_set($this->viewData, 'htmlIsValid', Cache::get($cacheKey, false));
$this->handlePagination($currentPage);
}
@@ -215,10 +231,39 @@ protected function collectSubBundles()
return $subBundles;
}
- protected function renderView(?string $view = 'article')
+ /**
+ * Renders a full-page. Validates HTML
+ */
+ protected function renderPage(?string $view = 'article')
{
- $html = (string) view($view, $this->viewData);
+ $html = $this->renderView($view);
+
+ $html5 = new HTML5([
+ // Required tu use xpath, see
+ // https://github.com/Masterminds/html5-php/issues/123
+ 'disable_html_ns' => true,
+ ]);
+
+ $dom = $html5->loadHTML($html);
+ $xpath = new DOMXPath($dom);
+ $spans = $xpath->query('//pre/code/span[@class="line" and normalize-space(.) = ""]');
+
+ foreach ($spans as $span) {
+ if ($span->isSameNode($span->parentNode->lastChild)) {
+ $span->parentNode->removeChild($span);
+ }
+ }
+
+ $html = $html5->saveHTML($dom);
return $html;
}
+
+ /**
+ * Render specific view
+ */
+ protected function renderView(string $view)
+ {
+ return (string) view($view, $this->viewData);
+ }
}
diff --git a/app/Services/BundleRenderers/Renderers/DossierRenderer.php b/app/Services/BundleRenderers/Renderers/DossierRenderer.php
index 1c5b23e..846a0c4 100644
--- a/app/Services/BundleRenderers/Renderers/DossierRenderer.php
+++ b/app/Services/BundleRenderers/Renderers/DossierRenderer.php
@@ -20,7 +20,7 @@ public function render()
data_set($this->viewData, 'showToc', true);
data_set($this->viewData, 'dossier', $dossier);
- return [$this->bundle->getPath() => $this->renderView()];
+ return [$this->bundle->getPath() => $this->renderPage()];
}
/**
diff --git a/app/Services/BundleRenderers/Renderers/TermRenderer.php b/app/Services/BundleRenderers/Renderers/TermRenderer.php
index e46ae22..722fbcb 100644
--- a/app/Services/BundleRenderers/Renderers/TermRenderer.php
+++ b/app/Services/BundleRenderers/Renderers/TermRenderer.php
@@ -27,7 +27,7 @@ public function render()
data_set($this->viewData, 'relations', $relations);
- return [$this->bundle->getPath() => $this->renderView('term')];
+ return [$this->bundle->getPath() => $this->renderPage()];
}
/**