272 lines
9.5 KiB
PHP
272 lines
9.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Domain\Blog\AccessDecision;
|
|
use App\Domain\Blog\ArticleAccess;
|
|
use App\Domain\Seo\SeoPresenter;
|
|
use App\Models\Article;
|
|
use App\Models\Category;
|
|
use App\Models\Comment;
|
|
use App\Models\Link;
|
|
use App\Models\Tag;
|
|
use App\Settings\BlogSettings;
|
|
use App\Settings\GeneralSettings;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
|
|
class BlogController extends Controller
|
|
{
|
|
public function index(Request $request, GeneralSettings $settings, SeoPresenter $seo): View|RedirectResponse
|
|
{
|
|
// /index.php is often rewritten to / by PHP built-in server / nginx try_files.
|
|
$action = $request->query('action');
|
|
if ($action === 'tags' && $request->filled('item')) {
|
|
return redirect('/tag/'.rawurlencode((string) $request->query('item')), 301);
|
|
}
|
|
|
|
if (is_string($action) && $action !== '') {
|
|
return match ($action) {
|
|
'login' => app(AuthController::class)->showLogin($settings),
|
|
'reg' => app(AuthController::class)->showRegister($settings),
|
|
'profile' => app(AuthController::class)->showProfile($settings),
|
|
'links' => $this->links(),
|
|
'comments' => $this->comments(),
|
|
'tagslist' => $this->tagsList(),
|
|
'search' => $this->search($request),
|
|
'show' => $this->show($request, (int) $request->query('id'), $settings, $seo),
|
|
'tags' => $this->tagsList(),
|
|
default => $this->indexWithoutAction($request, $settings, $seo),
|
|
};
|
|
}
|
|
|
|
return $this->indexWithoutAction($request, $settings, $seo);
|
|
}
|
|
|
|
protected function indexWithoutAction(Request $request, GeneralSettings $settings, SeoPresenter $seo): View
|
|
{
|
|
$cid = $request->integer('cid') ?: null;
|
|
$setdate = $request->query('setdate');
|
|
$perPage = max(1, (int) app(BlogSettings::class)->posts_per_page);
|
|
|
|
$query = Article::query()
|
|
->with(['category', 'user', 'tags'])
|
|
->visible()
|
|
->published()
|
|
->orderByDesc('stick')
|
|
->orderByDesc('published_at');
|
|
|
|
if ($cid) {
|
|
$query->where('category_id', $cid);
|
|
}
|
|
|
|
if (is_string($setdate) && preg_match('/^\d{6}$/', $setdate)) {
|
|
$year = (int) substr($setdate, 0, 4);
|
|
$month = (int) substr($setdate, 4, 2);
|
|
$query->whereYear('published_at', $year)->whereMonth('published_at', $month);
|
|
}
|
|
|
|
$articles = $query->paginate($perPage)->withQueryString();
|
|
|
|
if ($cid) {
|
|
$category = Category::query()->find($cid);
|
|
if ($category !== null) {
|
|
return view('theme::category', [
|
|
'category' => $category,
|
|
'articles' => $articles,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forCategory($category),
|
|
]);
|
|
}
|
|
}
|
|
|
|
return view('theme::home', [
|
|
'articles' => $articles,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forHome(),
|
|
]);
|
|
}
|
|
|
|
public function bySlug(string $slug, GeneralSettings $settings, SeoPresenter $seo): View|RedirectResponse
|
|
{
|
|
$article = Article::query()
|
|
->visible()
|
|
->published()
|
|
->where('slug', $slug)
|
|
->firstOrFail();
|
|
|
|
return redirect('/show-'.$article->id.'.shtml', 301);
|
|
}
|
|
|
|
public function show(Request $request, int $id, GeneralSettings $settings, SeoPresenter $seo, ArticleAccess $access): View|RedirectResponse
|
|
{
|
|
$article = Article::query()
|
|
->with(['category', 'user', 'tags', 'comments' => fn ($q) => $q->visible()->orderBy('published_at')])
|
|
->visible()
|
|
->published()
|
|
->findOrFail($id);
|
|
|
|
$unlocked = (array) $request->session()->get('unlocked_articles', []);
|
|
$passwordError = null;
|
|
|
|
if (filled($article->read_password) && ! in_array($article->id, $unlocked, true)) {
|
|
if ($request->isMethod('post') && hash_equals((string) $article->read_password, (string) $request->input('password'))) {
|
|
$unlocked[] = $article->id;
|
|
$request->session()->put('unlocked_articles', $unlocked);
|
|
} elseif ($request->isMethod('post')) {
|
|
$passwordError = __('frontend.article.password_error');
|
|
}
|
|
}
|
|
|
|
$decision = $access->resolve($article, $request->user(), $unlocked);
|
|
|
|
if ($decision->status === AccessDecision::NEED_PASSWORD) {
|
|
return view('theme::password', [
|
|
'article' => $article,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forArticle($article),
|
|
'error' => $passwordError,
|
|
]);
|
|
}
|
|
|
|
if (! $decision->isAllow()) {
|
|
$article->increment('views');
|
|
|
|
return view('theme::paywall', [
|
|
'article' => $article,
|
|
'access' => $decision,
|
|
'bodyHtml' => $access->publicHtml($article, $decision),
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forArticle($article),
|
|
]);
|
|
}
|
|
|
|
$article->increment('views');
|
|
|
|
return view('theme::article', [
|
|
'article' => $article,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forArticle($article),
|
|
]);
|
|
}
|
|
|
|
public function archives(Request $request, ?string $date = null): View
|
|
{
|
|
$request->merge(['setdate' => $date === 'all' ? null : $date]);
|
|
|
|
return $this->index($request, app(GeneralSettings::class), app(SeoPresenter::class));
|
|
}
|
|
|
|
public function category(Request $request, int $cid, GeneralSettings $settings, SeoPresenter $seo): View
|
|
{
|
|
$category = Category::query()->findOrFail($cid);
|
|
$perPage = max(1, (int) app(BlogSettings::class)->posts_per_page);
|
|
|
|
$articles = Article::query()
|
|
->with(['category', 'user', 'tags'])
|
|
->visible()
|
|
->published()
|
|
->where('category_id', $category->id)
|
|
->orderByDesc('stick')
|
|
->orderByDesc('published_at')
|
|
->paginate($perPage)
|
|
->withQueryString();
|
|
|
|
return view('theme::category', [
|
|
'category' => $category,
|
|
'articles' => $articles,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => $settings,
|
|
'seo' => $seo->forCategory($category),
|
|
]);
|
|
}
|
|
|
|
public function tagsList(): View
|
|
{
|
|
$tags = Tag::query()->orderByDesc('use_count')->paginate(100);
|
|
|
|
return view('theme::tags', [
|
|
'tags' => $tags,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => app(GeneralSettings::class),
|
|
]);
|
|
}
|
|
|
|
public function tag(Request $request, ?string $name = null): View
|
|
{
|
|
$name = $name ?: (string) $request->query('item', '');
|
|
$tag = Tag::query()->where('name', $name)->firstOrFail();
|
|
|
|
$articles = $tag->articles()
|
|
->visible()
|
|
->published()
|
|
->orderByDesc('published_at')
|
|
->paginate(10);
|
|
|
|
return view('theme::tag', [
|
|
'tag' => $tag,
|
|
'articles' => $articles,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => app(GeneralSettings::class),
|
|
]);
|
|
}
|
|
|
|
public function comments(): View
|
|
{
|
|
$comments = Comment::query()
|
|
->with('article')
|
|
->visible()
|
|
->orderByDesc('published_at')
|
|
->paginate(20);
|
|
|
|
return view('theme::comments', [
|
|
'comments' => $comments,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => app(GeneralSettings::class),
|
|
]);
|
|
}
|
|
|
|
public function search(Request $request): View
|
|
{
|
|
$q = trim((string) $request->query('keywords', $request->input('keywords', '')));
|
|
|
|
$articles = Article::query()
|
|
->visible()
|
|
->published()
|
|
->when($q !== '', function ($query) use ($q) {
|
|
$query->where(function ($inner) use ($q) {
|
|
$inner->where('title', 'like', "%{$q}%")
|
|
->orWhere('content', 'like', "%{$q}%")
|
|
->orWhere('description', 'like', "%{$q}%");
|
|
});
|
|
})
|
|
->orderByDesc('published_at')
|
|
->paginate(10)
|
|
->withQueryString();
|
|
|
|
return view('theme::search', [
|
|
'articles' => $articles,
|
|
'keywords' => $q,
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => app(GeneralSettings::class),
|
|
]);
|
|
}
|
|
|
|
public function links(): View
|
|
{
|
|
return view('theme::links', [
|
|
'links' => Link::query()->where('visible', true)->orderBy('display_order')->get(),
|
|
'categories' => Category::query()->orderBy('display_order')->get(),
|
|
'settings' => app(GeneralSettings::class),
|
|
]);
|
|
}
|
|
}
|