app/Http/Controllers/ContentController.php
<?php
namespace Strimoid\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Strimoid\Contracts\Repositories\ContentRepository;
use Strimoid\Contracts\Repositories\FolderRepository;
use Strimoid\Contracts\Repositories\GroupRepository;
use Strimoid\Handlers\DownloadThumbnail;
use Strimoid\Models\Content;
use Strimoid\Models\Group;
use Strimoid\Settings\Facades\Setting;
class ContentController extends BaseController
{
public function __construct(protected ContentRepository $contents, protected GroupRepository $groups, protected FolderRepository $folders, private readonly \Illuminate\Routing\Router $router, private readonly \Illuminate\Contracts\View\Factory $viewFactory, private readonly \Illuminate\Contracts\Auth\Guard $guard, private readonly \Illuminate\Routing\Redirector $redirector, private readonly \Illuminate\Auth\AuthManager $authManager, private readonly \Illuminate\Contracts\Routing\ResponseFactory $responseFactory, private readonly \Illuminate\Contracts\Auth\Access\Gate $gate, private readonly \Illuminate\Queue\QueueManager $queueManager, private readonly \Illuminate\Validation\Factory $validationFactory)
{
}
/**
* Display contents from given group.
*/
public function showContentsFromGroup(Request $request, string $groupName = null)
{
$routeName = $this->router->currentRouteName();
$tab = Str::contains($routeName, 'new') ? 'new' : 'popular';
// If user is on homepage, then use proper group
if (!$this->router->input('groupname')) {
$groupName = $this->homepageGroup();
}
// Make it possible to browse everything by adding all parameter
if ($request->input('all')) {
$tab = null;
}
$group = $this->groups->requireByName($groupName);
$this->viewFactory->share('group', $group);
if ($group->isPrivate && $this->guard->guest()) {
return $this->redirector->guest('login');
}
$canSortBy = ['comments_count', 'uv', 'created_at', 'frontpage_at'];
$orderBy = in_array($request->input('sort'), $canSortBy) ? $request->input('sort') : null;
$builder = $group->contents($tab, $orderBy);
return $this->showContents($request, $builder);
}
/**
* Display contents from given folder.
*/
public function showContentsFromFolder(Request $request)
{
$tab = Str::contains($this->router->currentRouteName(), 'new') ? 'new' : 'popular';
$user = $this->router->input('user') ?: user();
$folderName = $this->router->input('folder');
$folder = $this->folders->requireByName($user->name, $folderName);
$this->viewFactory->share('folder', $folder);
if (!$folder->canBrowse()) {
abort(404);
}
$canSortBy = ['comments', 'uv', 'created_at', 'frontpage_at'];
$orderBy = in_array($request->input('sort'), $canSortBy) ? $request->input('sort') : null;
$builder = $folder->contents($tab, $orderBy);
return $this->showContents($request, $builder);
}
protected function showContents(Request $request, $builder)
{
$builder->with('group', 'user');
if ($this->authManager->check()) {
$builder->with('userSave', 'vote');
}
$this->filterByTime($builder, $request->input('time'));
// Paginate and attach parameters to paginator links
$perPage = Setting::get('entries_per_page');
$contents = $builder->paginate($perPage);
$contents->appends($request->only(['sort', 'time', 'all']));
// Return RSS feed for some of routes
if (Str::endsWith($this->router->currentRouteName(), '_rss')) {
return $this->generateRssFeed($contents);
}
return $this->viewFactory->make('content.display', compact('contents'));
}
protected function filterByTime($builder, $days): void
{
if (!$days) {
return;
}
$builder->fromDaysAgo($days);
}
/**
* Generate RSS feed from given collection of contents.
*/
protected function generateRssFeed($contents): \Symfony\Component\HttpFoundation\Response
{
return $this->responseFactory
->view('content.rss', compact('contents'))
->header('Content-Type', 'text/xml')
->setTtl(60);
}
public function showComments(Content $content, Request $request): View
{
$sortBy = $request->input('sort');
if (in_array($sortBy, ['uv', 'replies'])) {
$content->comments = $content->comments->sortBy(fn ($comment) => $sortBy === 'uv' ? $comment->uv : $comment->replies->count())->reverse();
}
$this->viewFactory->share('group', $content->group);
return $this->viewFactory->make('content.comments', compact('content'));
}
public function showFrame(Content $content)
{
return $this->viewFactory->make('content.frame', compact('content'));
}
public function showAddForm(): View
{
return $this->viewFactory->make('content.add');
}
public function showEditForm(Content $content)
{
$policyDecision = $this->gate->inspect('edit', $content);
if ($policyDecision->denied()) {
return $this->redirector
->route('content_comments', $content->getKey())
->with('danger_msg', $policyDecision->message());
}
return $this->viewFactory->make('content.edit', compact('content'));
}
public function addContent(Request $request): RedirectResponse
{
$rules = [
'title' => 'required|min:1|max:128|not_in:edit,thumbnail',
'description' => 'max:255',
'groupname' => 'required|exists:groups,urlname',
];
if ($request->input('type') === 'link') {
$rules['url'] = 'required|url_custom|max:2048';
} else {
$rules['text'] = 'required|min:1|max:50000';
}
$this->validate($request, $rules);
$group = Group::name($request->input('groupname'))->firstOrFail();
$group->checkAccess();
if (user()->isBanned($group)) {
return $this->redirector->action('ContentController@showAddForm')
->withInput()
->with('danger_msg', 'Zostałeś zbanowany w wybranej grupie');
}
if ($group->type === 'announcements'
&& !user()->isModerator($group)) {
return $this->redirector->action('ContentController@showAddForm')
->withInput()
->with('danger_msg', 'Nie możesz dodawać treści do wybranej grupy');
}
$content = new Content($request->only([
'title', 'description', 'nsfw', 'eng',
]));
if ($request->input('type') === 'link') {
$content->url = $request->input('url');
} else {
$content->text = $request->input('text');
}
$content->user()->associate(user());
$content->group()->associate($group);
$content->save();
if ($request->input('thumbnail') === 'on') {
$this->queueManager->push(DownloadThumbnail::class, [
'id' => $content->getKey(),
]);
}
return $this->redirector->route('content_comments', $content);
}
public function editContent(Request $request, Content $content): RedirectResponse
{
$policyDecision = $this->gate->inspect('edit', $content);
if ($policyDecision->denied()) {
return $this->redirector
->route('content_comments', $content->getKey())
->with('danger_msg', $policyDecision->message());
}
$rules = [
'title' => 'required|min:1|max:128|not_in:edit,thumbnail',
'description' => 'max:255',
];
if ($content->text) {
$rules['text'] = 'required|min:1|max:50000';
} else {
$rules['url'] = 'required|url_custom|max:2048';
}
$validator = $this->validationFactory->make($request->all(), $rules);
if ($validator->fails()) {
return $this->redirector->action('ContentController@showEditForm', $content->getKey())
->withInput()
->withErrors($validator);
}
$data = $request->only(['title', 'description', 'nsfw', 'eng']);
$content->fill($data);
if ($content->text) {
$content->text = $request->input('text');
} else {
$content->url = $request->input('url');
}
$content->save();
return $this->redirector->route('content_comments', $content);
}
public function removeContent(Request $request): JsonResponse
{
$id = hashids_decode($request->input('id'));
$content = Content::findOrFail($id);
$this->authorize('remove', $content);
if ($content->forceDelete()) {
return $this->responseFactory->json(['status' => 'ok']);
}
return $this->responseFactory->json(['status' => 'error'], 500);
}
public function softRemoveContent(Request $request): JsonResponse
{
$id = hashids_decode($request->input('id'));
$content = Content::findOrFail($id);
$this->authorize('softRemove', $content);
$content->deletedBy()->associate(user());
$content->save();
$content->delete();
return $this->responseFactory->json(['status' => 'ok']);
}
}