Captures the current working tree after theme slots, ArticleAccess, and the payment / paid-content plugins so subsequent work has a reviewable git history.
76 lines
2.1 KiB
PHP
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');
|
|
}
|
|
}
|