Initial baseline: LaraBlog core with plugin commerce surface.

Captures the current working tree after theme slots, ArticleAccess, and the payment / paid-content plugins so subsequent work has a reviewable git history.
This commit is contained in:
ak
2026-08-12 01:15:38 +08:00
commit 263b98b218
337 changed files with 31393 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace App\Domain\Theme;
use App\Domain\Plugin\Hook;
/**
* Named injection points for themes / plugins / site snippets.
*
* Themes call ThemeSlot::render('head') (or @themeslot('head')).
* Plugins listen on Hook event "theme.{slot}".
* Admin SnippetSettings also appends into these slots at boot.
*/
final class ThemeSlot
{
public const HEAD = 'head';
public const BODY_START = 'body_start';
public const BODY_END = 'body_end';
public const HEADER_AFTER = 'header_after';
public const NAV_AFTER = 'nav_after';
public const CONTENT_BEFORE = 'content_before';
public const CONTENT_AFTER = 'content_after';
public const ARTICLE_TOP = 'article_top';
public const ARTICLE_BOTTOM = 'article_bottom';
public const SIDEBAR_BEFORE = 'sidebar_before';
public const SIDEBAR = 'sidebar';
public const SIDEBAR_AFTER = 'sidebar_after';
public const FOOTER_BEFORE = 'footer_before';
public const FOOTER_AFTER = 'footer_after';
/** @return list<string> */
public static function all(): array
{
return [
self::HEAD,
self::BODY_START,
self::BODY_END,
self::HEADER_AFTER,
self::NAV_AFTER,
self::CONTENT_BEFORE,
self::CONTENT_AFTER,
self::ARTICLE_TOP,
self::ARTICLE_BOTTOM,
self::SIDEBAR_BEFORE,
self::SIDEBAR,
self::SIDEBAR_AFTER,
self::FOOTER_BEFORE,
self::FOOTER_AFTER,
];
}
public static function event(string $slot): string
{
return 'theme.'.$slot;
}
public static function render(string $slot): string
{
return Hook::gather(self::event($slot), '');
}
/**
* Soft contract for theme authors & admin UI.
* Sizes are recommendations only the active theme's CSS wins.
*
* @return array<string, array{label: string, place: string, size: string, multi: bool}>
*/
public static function catalog(): array
{
$catalog = [];
foreach (self::all() as $slot) {
$catalog[$slot] = [
'label' => __('admin.slots.'.$slot.'.label'),
'place' => __('admin.slots.'.$slot.'.place'),
'size' => __('admin.slots.'.$slot.'.size'),
'multi' => true,
];
}
return $catalog;
}
public static function hint(string $slot): string
{
$meta = self::catalog()[$slot] ?? null;
if ($meta === null) {
return __('admin.slots.custom', ['slot' => $slot]);
}
return __('admin.slots.hint', [
'place' => $meta['place'],
'size' => $meta['size'],
'slot' => $slot,
]);
}
}