Files
larablog/app/Domain/Blog/ArticleAccess.php
T
ak 263b98b218 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.
2026-08-12 01:15:38 +08:00

76 lines
2.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Domain\Blog;
use App\Domain\Plugin\Hook;
use App\Models\Article;
use App\Models\User;
use Illuminate\Support\Facades\Session;
class ArticleAccess
{
/**
* Pure decision — no HTTP side effects.
*
* @param list<int>|null $unlockedArticleIds session unlock list; null = read from session
*/
public function resolve(Article $article, ?User $user = null, ?array $unlockedArticleIds = null): AccessDecision
{
if ($user !== null && $this->isPrivileged($article, $user)) {
return AccessDecision::allow();
}
if (filled($article->read_password)) {
$unlocked = $unlockedArticleIds ?? (array) Session::get('unlocked_articles', []);
if (! in_array($article->id, $unlocked, true)) {
return AccessDecision::needPassword();
}
}
$decision = AccessDecision::allow();
$context = [
'article' => $article,
'user' => $user,
];
// Fold every listener result instead of letting the last one win, so a
// plugin can only tighten access and a broken listener cannot re-open it.
foreach (Hook::listeners('article.access') as $listener) {
$result = $listener($decision, $context);
if ($result instanceof AccessDecision) {
$decision = $decision->tightenWith($result);
}
}
return $decision;
}
/**
* HTML safe to expose for the given decision (full body or teaser/description).
*/
public function publicHtml(Article $article, AccessDecision $decision): string
{
if ($decision->isAllow()) {
return $article->renderedHtml();
}
if (filled($decision->teaserHtml)) {
return $decision->teaserHtml;
}
return e((string) ($article->description ?: ''));
}
protected function isPrivileged(Article $article, User $user): bool
{
if ((int) $article->user_id === (int) $user->id) {
return true;
}
return $user->hasRole('admin');
}
}