Merge pull request #2260 from HDInnovations/Livewire-Comments

(Feature) Livewire Comments
This commit is contained in:
HDVinnie
2022-09-21 05:55:47 -04:00
committed by GitHub
32 changed files with 1009 additions and 1692 deletions

View File

@@ -1,733 +0,0 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\Article;
use App\Models\Collection;
use App\Models\Comment;
use App\Models\Playlist;
use App\Models\Ticket;
use App\Models\Torrent;
use App\Models\TorrentRequest;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\ChatRepository;
use App\Repositories\TaggedUserRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* @see \Tests\Feature\Http\Controllers\CommentControllerTest
*/
class CommentController extends Controller
{
public $tag;
/**
* CommentController Constructor.
*/
public function __construct(private readonly TaggedUserRepository $taggedUserRepository, private readonly ChatRepository $chatRepository)
{
}
/**
* Add A Comment To A Collection.
*/
public function collection(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$collection = Collection::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('collection-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('collection.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('collection-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('collection.show', ['id' => $collection->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->collection_id = $collection->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'collection_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('collection.show', ['id' => $collection->id])
->withErrors($v->errors());
}
$comment->save();
$collectionUrl = \href_collection($collection);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on collection [url=%s]%s[/url]', $profileUrl, $user->username, $collectionUrl, $collection->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on collection [url=%s]%s[/url]', $collectionUrl, $collection->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$collection->comments()->get()->each(function ($c, $v) use ($users) {
$users->push($c->user);
});
$this->tag->messageCommentUsers(
'collection',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'collection',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('mediahub.collections.show', ['id' => $collection->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Article.
*/
public function article(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$article = Article::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('article-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('articles.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('article-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('articles.show', ['id' => $article->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->article_id = $article->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'article_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('articles.show', ['id' => $article->id])
->withErrors($v->errors());
}
$comment->save();
$articleUrl = \href_article($article);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on article [url=%s]%s[/url]', $profileUrl, $user->username, $articleUrl, $article->title)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on article [url=%s]%s[/url]', $articleUrl, $article->title)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$article->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'article',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'article',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('articles.show', ['id' => $article->id])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Playlist.
*/
public function playlist(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$playlist = Playlist::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('playlist-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('playlists.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('playlist-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('playlists.show', ['id' => $playlist->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->playlist_id = $playlist->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'playlist_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('playlists.show', ['id' => $playlist->id])
->withErrors($v->errors());
}
$comment->save();
$playlistUrl = \href_playlist($playlist);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on playlist [url=%s]%s[/url]', $profileUrl, $user->username, $playlistUrl, $playlist->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on playlist [url=%s]%s[/url]', $playlistUrl, $playlist->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$playlist->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'playlist',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'playlist',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('playlists.show', ['id' => $playlist->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Torrent.
*/
public function torrent(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$torrent = Torrent::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('torrent-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('torrent-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->torrent_id = $torrent->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'torrent_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors($v->errors());
}
$comment->save();
//Notification
if ($user->id != $torrent->user_id) {
$torrent->notifyUploader('comment', $comment);
}
$torrentUrl = \href_torrent($torrent);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Torrent [url=%s]%s[/url]', $profileUrl, $user->username, $torrentUrl, $torrent->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on torrent [url=%s]%s[/url]', $torrentUrl, $torrent->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$torrent->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'torrent',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'torrent',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('torrent', ['id' => $torrent->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Request.
*/
public function request(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$tr = TorrentRequest::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('request-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('request', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('request-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('request', ['id' => $tr->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->requests_id = $tr->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'requests_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('request', ['id' => $tr->id])
->withErrors($v->errors());
}
$comment->save();
$trUrl = \href_request($tr);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Request [url=%s]%s[/url]', $profileUrl, $user->username, $trUrl, $tr->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on Request [url=%s]%s[/url]', $trUrl, $tr->name)
);
}
//Notification
if ($user->id != $tr->user_id) {
$tr->notifyRequester('comment', $comment);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$tr->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'request',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'request',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('request', ['id' => $tr->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Request.
*/
public function ticket(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$ticket = Ticket::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('ticket-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('tickets.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('ticket-comment:'.$user->id);
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = 0;
$comment->user_id = $user->id;
$comment->ticket_id = $ticket->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'ticket_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('tickets.show', ['id' => $id])
->withErrors($v->errors());
}
if ($user->id != $ticket->user_id) {
$ticket->user_read = 0;
}
if ($user->id == $ticket->user_id) {
$ticket->staff_read = 0;
}
$comment->save();
$ticket->save();
return \to_route('tickets.show', ['id' => $ticket->id])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Torrent Via Quick Thanks.
*/
public function quickthanks(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$torrent = Torrent::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('torrent-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('torrent-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
if ($torrent->anon === 1) {
$thankArray = [
'Thanks for the upload! :thumbsup_tone2:',
'Time and effort is much appreciated :thumbsup_tone2:',
'Great upload! :fire:', 'Thank you :smiley:',
];
} else {
$uploader = User::where('id', '=', $torrent->user_id)->first();
$uploaderUrl = \href_profile($uploader);
$thankArray = [
\sprintf('Thanks for the upload [url=%s][color=%s][b]%s[/b][/color][/url] :vulcan_tone2:', $uploaderUrl, $uploader->group->color, $uploader->username),
\sprintf('Beautiful upload [url=%s][color=%s][b]%s[/b][/color][/url] :fire:', $uploaderUrl, $uploader->group->color, $uploader->username),
\sprintf('Cheers [url=%s][color=%s][b]%s[/b][/color][/url] for the upload :beers:', $uploaderUrl, $uploader->group->color, $uploader->username),
];
}
$selected = random_int(0, (\is_countable($thankArray) ? \count($thankArray) : 0) - 1);
$comment->content = $thankArray[$selected];
$comment->user_id = $user->id;
$comment->torrent_id = $torrent->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'torrent_id' => 'required',
]);
if ($v->fails()) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors($v->errors());
}
$comment->save();
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($user->id != $torrent->user_id) {
User::find($torrent->user_id)->notify(new NewComment('torrent', $comment));
}
// Auto Shout
$torrentUrl = \href_torrent($torrent);
$profileUrl = \href_profile($user);
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Torrent [url=%s]%s[/url]', $profileUrl, $user->username, $torrentUrl, $torrent->name)
);
return \to_route('torrent', ['id' => $torrent->id])
->withSuccess(\trans('comment.added'));
}
/**
* Edit A Comment.
*/
public function editComment(Request $request, int $commentId): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$comment = Comment::findOrFail($commentId);
\abort_unless($user->group->is_modo || $user->id == $comment->user_id, 403);
$comment->content = $request->input('comment-edit');
$v = \validator($comment->toArray(), [
'content' => 'required',
]);
if ($v->fails()) {
return \redirect()->back()
->withErrors($v->errors());
}
$comment->save();
return \redirect()->back()->withSuccess(\trans('comment.edited'));
}
/**
* Delete A Comment.
*/
public function deleteComment(Request $request, int $commentId): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$comment = Comment::findOrFail($commentId);
\abort_unless($user->group->is_modo || $user->id == $comment->user_id, 403);
$comment->delete();
return \redirect()->back()->withSuccess(\trans('comment.deleted'));
}
}

View File

@@ -0,0 +1,190 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\TaggedUserRepository;
use Livewire\Component;
use voku\helper\AntiXSS;
class Comment extends Component
{
public $comment;
public $anon = false;
private TaggedUserRepository $taggedUserRepository;
public \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User $user;
protected $listeners = [
'refresh' => '$refresh',
];
protected $validationAttributes = [
'replyState.content' => 'reply',
];
public $isReplying = false;
public $replyState = [
'content' => '',
];
public $isEditing = false;
public $editState = [
'content' => '',
];
final public function mount(TaggedUserRepository $taggedUserRepository): void
{
$this->taggedUserRepository = $taggedUserRepository;
$this->user = \auth()->user();
}
final public function updatedIsEditing($isEditing): void
{
if (! $isEditing) {
return;
}
$this->editState = [
'content' => $this->comment->content,
];
}
final public function editComment(): void
{
if (\auth()->user()->id !== $this->comment->user_id || ! \auth()->user()->group->is_modo) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => 'Permission Denied!']);
return;
}
$this->comment->update((new AntiXSS())->xss_clean($this->editState));
$this->isEditing = false;
}
final public function deleteComment(): void
{
if (\auth()->user()->id !== $this->comment->user_id || ! \auth()->user()->group->is_modo) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => 'Permission Denied!']);
return;
}
$this->comment->delete();
$this->emitUp('refresh');
}
final public function postReply(): void
{
if (\auth()->user()->can_comment === 0) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => \trans('comment.rights-revoked')]);
return;
}
if (! $this->comment->isParent()) {
return;
}
$this->validate([
'replyState.content' => 'required',
]);
$reply = $this->comment->children()->make((new AntiXSS())->xss_clean($this->replyState));
$reply->user()->associate(\auth()->user());
$reply->commentable()->associate($this->comment->commentable);
$reply->anon = $this->anon;
$reply->save();
$this->replyState = [
'content' => '',
];
// Achievements
if ($reply->anon === 0) {
$this->user->unlock(new UserMadeComment(), 1);
$this->user->addProgress(new UserMadeTenComments(), 1);
$this->user->addProgress(new UserMade50Comments(), 1);
$this->user->addProgress(new UserMade100Comments(), 1);
$this->user->addProgress(new UserMade200Comments(), 1);
$this->user->addProgress(new UserMade300Comments(), 1);
$this->user->addProgress(new UserMade400Comments(), 1);
$this->user->addProgress(new UserMade500Comments(), 1);
$this->user->addProgress(new UserMade600Comments(), 1);
$this->user->addProgress(new UserMade700Comments(), 1);
$this->user->addProgress(new UserMade800Comments(), 1);
$this->user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($this->user->id !== $this->comment->user_id) {
User::find($this->comment->user_id)->notify(new NewComment($this->comment, $reply));
}
// Tagging
if ($this->taggedUserRepository->hasTags($this->replyState)) {
if ($this->user->group->is_modo && $this->taggedUserRepository->contains($this->replyState, '@here')) {
$users = \collect([]);
$this->comment->children()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
$this->comment,
$users,
$this->user,
'Staff',
$reply
);
} else {
$sender = $reply->anon !== 0 ? $this->user->username : 'Anonymous';
$this->taggedUserRepository->messageTaggedCommentUsers(
$this->comment,
$this->replyState[],
$this->user,
$sender,
$reply
);
}
}
$this->isReplying = false;
$this->emitSelf('refresh');
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return \view('livewire.comment');
}
}

View File

@@ -0,0 +1,160 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\TaggedUserRepository;
use Livewire\Component;
use Livewire\WithPagination;
use voku\helper\AntiXSS;
class Comments extends Component
{
use WithPagination;
private TaggedUserRepository $taggedUserRepository;
public \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User $user;
public $model;
public $anon = false;
public int $perPage = 10;
protected $listeners = [
'refresh' => '$refresh',
];
public $newCommentState = [
'content' => '',
];
protected $validationAttributes = [
'newCommentState.content' => 'comment',
];
final public function mount(TaggedUserRepository $taggedUserRepository): void
{
$this->taggedUserRepository = $taggedUserRepository;
$this->user = \auth()->user();
}
final public function loadMore()
{
$this->perPage += 10;
}
final public function postComment(): void
{
if ($this->user->can_comment === 0) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => \trans('comment.rights-revoked')]);
return;
}
$this->validate([
'newCommentState.content' => 'required',
]);
$comment = $this->model->comments()->make((new AntiXSS())->xss_clean($this->newCommentState));
$comment->user()->associate($this->user);
$comment->anon = $this->anon;
$comment->save();
$this->newCommentState = [
'content' => '',
];
// Achievements
if ($comment->anon === 0) {
$this->user->unlock(new UserMadeComment(), 1);
$this->user->addProgress(new UserMadeTenComments(), 1);
$this->user->addProgress(new UserMade50Comments(), 1);
$this->user->addProgress(new UserMade100Comments(), 1);
$this->user->addProgress(new UserMade200Comments(), 1);
$this->user->addProgress(new UserMade300Comments(), 1);
$this->user->addProgress(new UserMade400Comments(), 1);
$this->user->addProgress(new UserMade500Comments(), 1);
$this->user->addProgress(new UserMade600Comments(), 1);
$this->user->addProgress(new UserMade700Comments(), 1);
$this->user->addProgress(new UserMade800Comments(), 1);
$this->user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($this->user->id !== $this->model->user_id) {
User::find($this->model->user_id)->notify(new NewComment($this->model, $comment));
}
// Tagging
if ($this->taggedUserRepository->hasTags($this->newCommentState)) {
if ($this->user->group->is_modo && $this->taggedUserRepository->contains($this->newCommentState, '@here')) {
$users = \collect([]);
$this->model->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
$this->model,
$users,
$this->user,
'Staff',
$comment
);
} else {
$sender = $comment->anon !== 0 ? $this->user->username : 'Anonymous';
$this->taggedUserRepository->messageTaggedCommentUsers(
$this->model,
$this->newCommentState[],
$this->user,
$sender,
$comment
);
}
}
$this->gotoPage(1);
}
final public function getCommentsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator
{
return $this->model
->comments()
->with('user', 'children.user', 'children.children')
->parent()
->latest()
->paginate($this->perPage);
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return \view('livewire.comments', [
'comments' => $this->comments,
]);
}
}

View File

@@ -36,12 +36,9 @@ class Article extends Model
]);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
/**

View File

@@ -23,12 +23,9 @@ class Collection extends Model
protected $table = 'collection';
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'collection_id');
return $this->morphMany(Comment::class, 'commentable');
}
public function movie(): \Illuminate\Database\Eloquent\Relations\BelongsToMany

View File

@@ -17,15 +17,24 @@ use App\Events\TicketWentStale;
use App\Helpers\Bbcode;
use App\Helpers\Linkify;
use App\Traits\Auditable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use voku\helper\AntiXSS;
class Comment extends Model
{
use HasFactory;
use Auditable;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'content',
'user_id',
'anon',
];
/**
* Belongs To A User.
*/
@@ -37,52 +46,24 @@ class Comment extends Model
]);
}
/**
* Belongs To A Torrent.
*/
public function torrent(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function commentable(): \Illuminate\Database\Eloquent\Relations\MorphTo
{
return $this->belongsTo(Torrent::class);
return $this->morphTo();
}
/**
* Belongs To A Article.
*/
public function article(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function children(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->belongsTo(Article::class);
return $this->hasMany(__CLASS__, 'parent_id')->oldest();
}
/**
* Belongs To A Request.
*/
public function request(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function isParent(): bool
{
return $this->belongsTo(TorrentRequest::class, 'requests_id', 'id');
return is_null($this->parent_id);
}
/**
* Belongs To A Playlist.
*/
public function playlist(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function scopeParent(Builder $builder): void
{
return $this->belongsTo(Playlist::class);
}
/**
* Belongs To A Ticket.
*/
public function ticket(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Ticket::class);
}
/**
* Set The Comments Content After Its Been Purified.
*/
public function setContentAttribute(?string $value): void
{
$this->attributes['content'] = \htmlspecialchars((new AntiXSS())->xss_clean($value), ENT_NOQUOTES);
$builder->whereNull('parent_id');
}
/**

View File

@@ -41,11 +41,8 @@ class Playlist extends Model
return $this->hasMany(PlaylistTorrent::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'playlist_id');
return $this->morphMany(Comment::class, 'commentable');
}
}

View File

@@ -95,11 +95,8 @@ class Ticket extends Model
return $this->hasMany(TicketAttachment::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
}

View File

@@ -182,12 +182,9 @@ class Torrent extends Model
return $this->hasMany(TorrentFile::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
/**

View File

@@ -108,12 +108,9 @@ class TorrentRequest extends Model
return $this->belongsTo(Torrent::class, 'filled_hash', 'info_hash');
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'requests_id', 'id');
return $this->morphMany(Comment::class, 'commentable');
}
/**

View File

@@ -0,0 +1,71 @@
<?php
use App\Models\Comment;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('comments', function (Blueprint $table) {
$table->bigIncrements('id')->change();
$table->morphs('commentable');
$table->foreignId('parent_id')->after('user_id')->nullable()->constrained('comments')->onDelete('cascade');
});
$comments = Comment::all();
foreach ($comments as $comment) {
if ($comment->torrent_id !== null) {
$comment->commentable_id = $comment->torrent_id;
$comment->commentable_type = 'App\Models\Torrent';
$comment->save();
}
if ($comment->article_id !== null) {
$comment->commentable_id = $comment->article_id;
$comment->commentable_type = 'App\Models\Article';
$comment->save();
}
if ($comment->requests_id !== null) {
$comment->commentable_id = $comment->requests_id;
$comment->commentable_type = 'App\Models\TorrentRequest';
$comment->save();
}
if ($comment->collection_id !== null) {
$comment->commentable_id = $comment->collection_id;
$comment->commentable_type = 'App\Models\Collection';
$comment->save();
}
if ($comment->playlist_id !== null) {
$comment->commentable_id = $comment->playlist_id;
$comment->commentable_type = 'App\Models\Playlist';
$comment->save();
}
if ($comment->ticket_id !== null) {
$comment->commentable_id = $comment->ticket_id;
$comment->commentable_type = 'App\Models\Ticket';
$comment->save();
}
}
Schema::table('comments', function (Blueprint $table) {
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
$table->dropForeign('fk_comments_articles_1');
$table->dropIndex('fk_comments_torrents_1');
$table->dropIndex('comments_playlist_id_index');
$table->dropIndex('comments_collection_id_index');
$table->dropIndex('comments_ticket_id_index');
$table->dropColumn('torrent_id', 'article_id', 'requests_id', 'collection_id', 'playlist_id', 'ticket_id');
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
});
}
};

View File

@@ -19,7 +19,7 @@
@import 'components/achievement';
@import 'components/article-preview';
@import 'components/bbcode-input';
@import 'components/chip';
@import 'components/comment';
@import 'components/comparison';
@import 'components/data-table';
@import 'components/dialog';

View File

@@ -1,10 +0,0 @@
.chip--anonymous__link {
opacity: 0.8;
font-weight: 200 !important;
white-space: nowrap;
}
.chip--user__link {
font-family: var(--font);
white-space: nowrap;
}

View File

@@ -0,0 +1,122 @@
.comments,
.comment__reply-list {
padding: 0;
margin-top: 10px;
list-style-type: none;
background-color: inherit !important; /* Overrides theming */
display: flex;
flex-direction: column;
align-items: flex-start;
row-gap: 10px;
> li {
background-color: inherit;
max-width: 100%;
}
}
.comment {
display: grid;
grid-template-columns: 0 auto auto 1fr auto;
grid-template-rows: min-content auto;
grid-template-areas:
'avatar author timestamp . actions'
'avatar content content content content';
gap: 0;
align-items: center;
padding: 12px 16px;
margin-left: 58px;
border-radius: 20px;
font-size: 13px;
color: var(--message-bubble-fg, currentColor);
background-color: var(--message-bubble-bg, inherit);
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12), 0 1px 3px 0 rgba(0, 0, 0, 0.2);
width: fit-content;
}
.comment__header {
display: contents;
}
.comment__avatar {
grid-area: avatar;
position: relative;
left: -58px;
bottom: -12px;
border-radius: 50%;
width: 36px;
align-self: self-end;
background-color: transparent;
}
.comment__author {
grid-area: author;
margin: 0;
font-size: 13px;
}
.comment__author-link {
font-weight: 600;
}
.comment__timestamp {
grid-area: timestamp;
font-size: 11px;
margin: 0 8px;
white-space: nowrap;
}
.comment__actions {
grid-area: actions;
display: flex;
justify-content: flex-end;
gap: 12px;
padding: 0;
margin: 0;
}
.comment__edit,
.comment__reply,
.comment__delete {
background-color: transparent;
border-radius: 50%;
line-height: 1;
width: 20px;
height: 20px;
flex: 1;
border: none;
font-size: 13px;
&:hover {
filter: brightness(0.8);
}
}
/* Can be removed once `abbr[title]` underlined styling is removed site-wide */
.comment__edit-abbr,
.comment__reply-abbr,
.comment__delete-abbr {
border-bottom: none !important;
text-decoration: none !important;
cursor: unset !important;
}
.comment__content {
grid-area: content;
position: relative;
margin-top: 0 !important; /* can be removed once site-wide `p` styling is removed */
}
.edit-comment {
grid-area: content;
width: 9999px;
}
.comment__replies {
background-color: inherit;
margin-left: 54px;
}
.reply-comment {
width: 9999px;
}

View File

@@ -0,0 +1,149 @@
.form {
display: flex;
flex-direction: column;
gap: 12px;
padding-top: 12px;
background-color: inherit;
max-width: 100%;
}
/* Form groups (inputs + labels) */
.form__group {
background-color: inherit;
position: relative;
margin-top: 0 !important; /* Can be removed once site-wide <p> styles are removed */
}
/* Form labels */
.form__label {
user-select: none;
font-size: 14px;
font-weight: normal !important; /* Needed until site-wide <label> styles are removed */
}
.form__label--floating {
background-color: inherit;
border-radius: 50% 50% 0 0 / 8px;
cursor: text;
height: auto;
line-height: 1;
position: absolute;
padding: 8px 4px 0 4px;
display: inline-block;
top: 6px;
left: 8px;
color: var(--textarea-border);
@media screen and (prefers-reduced-motion: no-preference) {
transition: top 400ms cubic-bezier(0.25, 0.8, 0.25, 1), font-size 400ms cubic-bezier(0.25, 0.8, 0.25, 1),
color 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
}
/* Buttons */
.form__button {
font-size: 13px;
font-weight: 600;
line-height: 1;
margin: 6px;
padding: 8px 12px;
border-radius: 9999px;
align-self: flex-start;
transition: filter 300ms;
&:hover {
filter: brightness(1.1);
}
&:active {
filter: brightness(0.9);
transition: none;
}
}
.form__button--filled {
background: var(--button-filled-bg);
color: var(--button-filled-fg);
border: 0;
}
.form__button--text {
background: var(--button-text-bg);
color: var(--button-text-fg);
border: 0;
}
/* Textareas */
.form__textarea {
background-color: inherit;
border: 1px solid var(--textarea-border);
width: 100%;
max-width: 100%;
height: 40px;
padding: 12px;
border-radius: 5px;
outline: none;
font-size: 14px;
font-weight: normal;
@media screen and (prefers-reduced-motion: no-preference) {
transition: border-color 600ms cubic-bezier(0.25, 0.8, 0.25, 1), height 600ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
&:hover {
border: 2px solid var(--textarea-border-hover);
}
&:focus,
&:focus:hover {
border: 2px solid var(--textarea-border-active);
height: 140px;
& + .form__label--floating {
top: -11px;
font-size: 11px;
color: #2196f3;
}
}
&:valid {
height: 140px;
& + .form__label--floating {
top: -11px;
font-size: 11px;
}
}
&:invalid:focus {
border: 1px solid #ba1b1b;
& + .form__label--floating {
color: #ba1b1b;
> strong {
color: inherit; /* Can be removed once site-wide <strong> styling is removed */
}
}
& ~ .form__hint {
color: #ba1b1b;
font-size: 12px;
margin-left: 16px;
> strong {
color: inherit; /* Can be removed once site-wide <strong> styling is removed */
}
}
}
}
/* Checkboxes */
.form__checkbox {
all: revert !important; /* Can be removed once site-wide checkbox styling is removed */
}

View File

@@ -1,4 +1,14 @@
:root {
--message-bubble-bg: white;
--message-bubble-fg: currentColor;
--button-filled-bg: #5cb579 linear-gradient(to bottom right, #0ba360, #2bb673);
--button-filled-fg: white;
--button-text-bg: inherit;
--button-text-fg: currentColor;
--textarea-border: #555;
--textarea-border-hover: #999;
--textarea-border-active: #2196f3;
--achievement-fg: inherit;
--achievement-bg: #e7e7e7;
@@ -3174,6 +3184,7 @@ button.list-group-item-danger.active:hover {
.panel-body {
padding: 15px;
background-color: #e2e2e2;
}
.panel-body:after,
@@ -10796,7 +10807,7 @@ div.single_column {
display: block !important;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
background-color: #ffffff;
background-color: var(--message-bubble-bg);
padding: 4px 12px 0 8px;
margin: 4px 12px 0 48px;
}
@@ -10804,7 +10815,7 @@ div.single_column {
.sent > div {
display: block !important;
border-bottom-right-radius: 15px;
background-color: #ffffff;
background-color: var(--message-bubble-bg);
padding: 0 12px 4px 8px !important;
margin: 0 12px 4px 48px !important;
}
@@ -10819,7 +10830,7 @@ div.single_column {
bottom: 4px;
left: 38px;
width: 0;
border-right: 10px solid #ffffff;
border-right: 10px solid var(--message-bubble-bg);
border-top: 10px solid transparent;
/* border-bottom: 10px solid transparent; */
}

View File

@@ -1,4 +1,6 @@
:root {
--message-bubble-bg: #363636;
--achievement-fg: inherit;
--achievement-bg: #272727;

View File

@@ -20,6 +20,10 @@
--color-bright-white: hsl(0, 0%, 100%);
--color-light-orange: hsl(20, 20%, 50%);
--message-bubble-bg: #222;
--message-bubble-fg: currentColor;
--button-filled-bg: var(--color-green);
--achievement-fg: inherit;
--achievement-bg: #2c2c2c;
@@ -1109,7 +1113,7 @@ fieldset[disabled] {
display: block !important;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
background-color: #363636 !important;
background-color: var(--message-bubble-bg) !important;
padding: 4px 12px 0 8px;
margin: 4px 12px 0 48px;
}
@@ -1117,7 +1121,7 @@ fieldset[disabled] {
.sent > div {
display: block !important;
border-bottom-right-radius: 15px;
background-color: #363636 !important;
background-color: var(--message-bubble-bg) !important;
padding: 0 12px 4px 8px !important;
margin: 0 12px 4px 48px !important;
}
@@ -1132,7 +1136,7 @@ fieldset[disabled] {
bottom: 4px;
left: 38px;
width: 0;
border-right: 10px solid #363636 !important;
border-right: 10px solid var(--message-bubble-bg) !important;
border-top: 10px solid transparent;
/* border-bottom: 10px solid transparent; */
}

View File

@@ -22,148 +22,26 @@
@section('page', 'page__articles--show')
@section('main')
<section class="panelV2">
<header class="panel__header">
<h1 class="panel__heading">{{ $article->title }}</h1>
<div class="panel__actions">
<time class="panel__action page__published" datetime="{{ $article->created_at }}">
{{ $article->created_at->toDayDateTimeString() }}
</time>
</div>
</header>
<div class="panel__body">
@joypixels($article->getContentHtml())
<section class="panelV2">
<header class="panel__header">
<h1 class="panel__heading">{{ $article->title }}</h1>
<div class="panel__actions">
<time class="panel__action page__published" datetime="{{ $article->created_at }}">
{{ $article->created_at->toDayDateTimeString() }}
</time>
</div>
</section>
<section class="panelV2">
<h4 class="panel__heading">
<i class="{{ config('other.font-awesome') }} fa-comment"></i>
{{ __('common.comments') }}
</h4>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($article->comments) == 0)
<div class="text-center">
<h4 class="text-bold text-danger">
<i class="{{ config('other.font-awesome') }} fa-frown"></i>
{{ __('common.no-comments') }}!
</h4>
</div>
@else
@foreach ($article->comments as $comment)
<li class="media" style="border-left: 5px solid #01bc8c;">
<div class="media-body">
@if ($comment->anon == 1)
<a href="#" class="pull-left" style="padding-right: 10px;">
<img src="{{ url('img/profile.png') }}" class="img-avatar-48">
<strong>{{ strtoupper(__('common.anonymous')) }}</strong>
</a>
@if (auth()->user()->id == $comment->user->id || auth()->user()->group->is_modo)
<a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"
>
(
<span>
<i class="{{ $comment->user->group->icon }}"></i>
{{ $comment->user->username }}
</span>
)
</a>
@endif
@else
<a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;"
>
@if ($comment->user->image != null)
<img
src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"
>
</a>
@else
<img
src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"
>
</a>
@endif
<strong>
<a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"
>
<span>
<i class="{{ $comment->user->group->icon }}"></i>
{{ $comment->user->username }}
</span>
</a>
</strong>
@endif
<span class="text-muted">
<small>
<em>{{$comment->created_at->diffForHumans() }}</em>
</small>
</span>
@if ($comment->user_id == auth()->id() || auth()->user()->group->is_modo)
<div class="pull-right" style="display: inline-block;">
<a data-toggle="modal" data-target="#modal-comment-edit-{{ $comment->id }}">
<button class="btn btn-circle btn-info">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
</button>
</a>
<form
action="{{ route('comment_delete', ['comment_id' => $comment->id]) }}"
method="POST"
style="display: inline-block;"
>
@csrf
@method('DELETE')
<button type="submit" class="btn btn-circle btn-danger">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
</button>
</form>
</div>
@endif
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@include('partials.modals', ['comment' => $comment])
@endforeach
@endif
</ul>
</div>
</section>
<section class="panelV2">
<header class="panel__header">
<h2 class="panel__heading">{{ __('common.your-comment') }}</h2>
<div class="panel__actions">
<span class="panel__action" x-data="{ emoji: false }">
<img src="{{ url('img/emoji-add.png') }}" width="32px" x-on:click="emoji = ! emoji">
<div style="position: absolute; z-index: 1; width: 240px; right: 0;" x-show="emoji" x-on:click.away="emoji = false">
<emoji-picker></emoji-picker>
</div>
</span>
</div>
</header>
<div class="panel__body">
<form class="form" method="POST" action="{{ route('comment_article', ['id' => $article->id]) }}">
@csrf
<p class="form__group">
<textarea id="editor" name="content" cols="30" rows="5" class="form-control"></textarea>
</p>
<p class="form__group">
<input type="hidden" value="0" name="anonymous">
<input type="checkbox" id="anon-comment" class="form__checkbox" value="1" name="anonymous">
<label for="anon-comment">{{ __('common.anonymous') }} {{ __('common.comment') }}</label>
</p>
<p class="form__group">
<button type="submit" class="form__button form__button--filled">{{ __('common.submit') }}</button>
</p>
</form>
</div>
</section>
</header>
<div class="panel__body">
@joypixels($article->getContentHtml())
</div>
</section>
<section class="panelV2">
<h4 class="panel__heading">
<i class="{{ config('other.font-awesome') }} fa-comment"></i>
{{ __('common.comments') }}
</h4>
<div class="panel-body no-padding">
<livewire:comments :model="$article"/>
</div>
</section>
@endsection

View File

@@ -0,0 +1,139 @@
<li>
<article class="comment">
<header class="comment__header">
<img
class="comment__avatar"
src="{{ url((!$comment->anon && $comment->user->image !== null) ? 'files/img/'.$comment->user->image : '/img/profile.png') }}"
alt=""
>
<address class="comment__author">
<x-user_tag :anon="$comment->anon" :user="$comment->user"/>
</address>
<time
class="comment__timestamp"
datetime="{{ $comment->created_at }}"
title="{{ $comment->created_at }}"
>
{{ $comment->created_at->diffForHumans() }}
</time>
<menu class="comment__actions">
@if ($comment->isParent())
<button wire:click="$toggle('isReplying')" class="comment__reply">
<abbr class="comment__reply-abbr" title="Reply to this comment">
<i class="{{ config('other.font-awesome') }} fa-reply"></i>
<span class="sr-only">__('pm.reply')</span>
</abbr>
</button>
@endif
@if ($comment->user_id === auth()->id() || auth()->user()->group->is_modo)
<button wire:click="$toggle('isEditing')" class="comment__edit">
<abbr class="comment__edit-abbr" title="{{ __('common.edit-your-comment') }}">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
<span class="sr-only">__('common.edit')</span>
</abbr>
</button>
<button
class="comment__delete"
x-on:click="confirmCommentDeletion"
x-data="{
confirmCommentDeletion () {
if (window.confirm('You sure?')) {
@this.call('deleteComment')
}
}
}"
>
<abbr class="comment__delete-abbr" title="{{ __('common.delete-your-comment') }}">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
<span class="sr-only">__('common.delete')</span>
</abbr>
</button>
@endif
</menu>
</header>
@if ($isEditing)
<form wire:submit.prevent="editComment" class="form edit-comment">
<p class="form__group">
<textarea
name="comment"
id="edit-comment"
class="form__textarea"
aria-describedby="edit-comment__textarea-hint"
wire:model.defer="editState.content"
required
></textarea>
<label for="edit-comment" class="form__label form__label--floating">
@error('editState.content')
<strong>{{ __('common.error') }}: </strong>
@enderror
Edit your comment...
</label>
@error('editState.content')
<span class="form__hint" id="edit-comment__textarea-hint">{{ $message }}</p>
@enderror
</p>
<p class="form__group">
<button type="submit" class="form__button form__button--filled">
{{ __('common.edit') }}
</button>
<button type="button" wire:click="$toggle('isEditing')" class="form__button form__button--text">
{{ __('common.cancel') }}
</button>
</p>
</form>
@else
<p class="comment__content">
@joypixels($comment->getContentHtml())
</p>
@endif
</article>
@if ($comment->isParent())
<section class="comment__replies">
<h5 class="sr-only">Replies</h5>
@if ($isReplying)
<form wire:submit.prevent="postReply" class="form reply-comment">
<p class="form__group">
<textarea
name="comment"
id="reply-comment"
class="form__textarea"
aria-describedby="reply-comment__textarea-hint"
wire:model.defer="replyState.content"
required
></textarea>
<label for="reply-comment" class="form__label form__label--floating">
@error('editState.content')
<strong>{{ __('common.error') }}: </strong>
@enderror
Reply to parent comment...
</label>
@error('replyState.content')
<span class="form__hint" id="reply-comment__textarea-hint">{{ $message }}</p>
@enderror
</p>
<p class="form__group">
<input type="checkbox" id="reply-anon" class="form__checkbox" wire:modal="anon">
<label for="reply-anon" class="form__label">{{ __('common.anonymous') }}?</label>
</p>
<p class="form__group">
<button type="submit" class="form__button form__button--filled">
{{ __('common.comment') }}
</button>
<button type="button" wire:click="$toggle('isReplying')"
class="form__button form__button--text">
{{ __('common.cancel') }}
</button>
</p>
</form>
@endif
@if ($comment->children->count() > 0)
<ul class="comment__reply-list">
@foreach ($comment->children as $child)
<livewire:comment :comment="$child" :key="$child->id"/>
@endforeach
</ul>
@endif
</section>
@endif
</li>

View File

@@ -0,0 +1,56 @@
<section class="panelV2">
<h4 class="panel__heading">
<i class="{{ config('other.font-awesome') }} fa-comment"></i>
{{ __('common.comments') }}
</h4>
<div class="panel-body no-padding">
<form wire:submit.prevent="postComment" class="form new-comment">
<p class="form__group">
<textarea
name="comment"
id="new-comment__textarea"
class="form__textarea"
aria-describedby="new-comment__textarea-hint"
wire:model.defer="newCommentState.content"
required
></textarea>
<label for="new-comment__textarea" class="form__label form__label--floating">
@error('newCommentState.content')
<strong>{{ __('common.error') }}: </strong>
@enderror
Add a comment...
</label>
@error('newCommentState.content')
<span class="form__hint" id="new-comment__textarea-hint">{{ $message }}</p>
@enderror
</p>
<p class="form__group">
<input type="checkbox" id="anon" class="form__checkbox" wire:modal="anon">
<label for="anon" class="form__label">{{ __('common.anonymous') }}?</label>
</p>
<p class="form__group">
<button type="submit" class="form__button form__button--filled">
Comment
</button>
<button type="reset" class="form__button form__button--text">
{{ __('common.cancel') }}
</button>
</p>
</form>
<ul class="comments">
@forelse($comments as $comment)
<livewire:comment :comment="$comment" :key="$comment->id"/>
@empty
<li>
<i class="{{ config('other.font-awesome') }} fa-frown"></i>
{{ __('common.no-comments') }}!
</li>
@endforelse
</ul>
@if ($comments->hasMorePages())
<div class="text-center">
<button class="btn btn-md btn-primary" wire:click.prevent="loadMore">Load More Comments</button>
</div>
@endif
</div>
</section>

View File

@@ -107,105 +107,8 @@
</div>
<div class="torrent box container" id="comments">
<!-- Comments -->
<div class="clearfix"></div>
<div class="row ">
<div class="col-md-12 col-sm-12">
<div class="panel panel-chat shoutbox">
<div class="panel-heading">
<h4>
<i class="{{ config('other.font-awesome') }} fa-comment"></i> {{ __('common.comments') }}
</h4>
</div>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($collection->comments) == 0)
<div class="text-center"><h4 class="text-bold text-danger"><i
class="{{ config('other.font-awesome') }} fa-frown"></i> {{ __('common.no-comments') }}
!</h4>
</div>
@else
@foreach ($collection->comments as $comment)
<li class="media" style="border-left: 5px solid #01BC8C;">
<div class="media-body">
@if ($comment->anon == 1)
<a href="#" class="pull-left" style="padding-right: 10px;">
<img src="{{ url('img/profile.png') }}" class="img-avatar-48">
<strong>{{ strtoupper(__('common.anonymous')) }}</strong></a> @if (auth()->user()->id == $comment->user->id || auth()->user()->group->is_modo)
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};">(<span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span>)</a> @endif
@else
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;">
@if ($comment->user->image != null)
<img src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"></a>
@else
<img src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"></a>
@endif
<strong><a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"><span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span></a></strong> @endif
<span class="text-muted"><small><em>{{ $comment->created_at->toDayDateTimeString() }} ({{ $comment->created_at->diffForHumans() }})</em></small></span>
@if ($comment->user_id == auth()->id() || auth()->user()->group->is_modo)
<div class="pull-right" style="display: inline-block;">
<a data-toggle="modal"
data-target="#modal-comment-edit-{{ $comment->id }}">
<button class="btn btn-circle btn-info">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
</button>
</a>
<form action="{{ route('comment_delete', ['comment_id' => $comment->id]) }}"
method="POST" style="display: inline-block;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-circle btn-danger">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
</button>
</form>
</div>
@endif
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@include('partials.modals', ['comment' => $comment])
@endforeach
@endif
</ul>
</div>
</div>
</div>
<!-- /Comments -->
<br>
<!-- Add comment -->
<div class="col-md-12">
<form role="form" method="POST" action="{{ route('comment_collection', ['id' => $collection->id]) }}">
@csrf
<div class="form-group">
<label for="content">{{ __('common.your-comment') }}:</label>
<span class="badge-extra">BBCode {{ __('common.is-allowed') }}</span>
<span class="pull-right" x-data="{ emoji: false }">
<img src="{{ url('img/emoji-add.png') }}" width="32px" x-on:click="emoji = ! emoji">
<div style="position: absolute; z-index: 1;" x-show="emoji" @click.away="emoji = false">
<emoji-picker></emoji-picker>
</div>
</span>
<textarea id="editor" name="content" cols="30" rows="5" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-danger">{{ __('common.submit') }}</button>
<label class="radio-inline"><strong>{{ __('common.anonymous') }} {{ __('common.comment') }}
:</strong></label>
<input type="radio" value="1" name="anonymous"> {{ __('common.yes') }}
<input type="radio" value="0" checked="checked" name="anonymous"> {{ __('common.no') }}
</form>
</div>
<!-- /Add comment -->
<div class="col-md-12 col-sm-12">
<livewire:comments :model="$collection"/>
</div>
</div>
@endsection

View File

@@ -1,32 +0,0 @@
<div class="modal fade" id="modal-comment-edit-{{ $comment->id }}" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog{{ modal_style() }}">
<div class="modal-content">
<meta charset="utf-8">
<title>{{ __('common.edit-your-comment') }}</title>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">{{ __('common.edit-your-comment') }}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" role="form" method="POST"
action="{{ route('comment_edit', ['comment_id' => $comment->id]) }}">
@csrf
<div class="form-group">
<div class="col-sm-12">
<label for="comment-edit"></label>
<textarea class="form-control" rows="5" name="comment-edit" cols="50"
id="comment-edit">{{ $comment->content }}</textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<input style="float:right;" class="btn btn-primary" type="submit"
value="{{ __('common.submit') }}">
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -233,102 +233,8 @@
</div>
<div class="block" id="comments">
<div class="row ">
<div class="col-md-12 col-sm-12">
<div class="panel panel-chat shoutbox">
<div class="panel-heading">
<h4>
<i class="{{ config('other.font-awesome') }} fa-comment"></i> {{ __('common.comments') }}
</h4>
</div>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($playlist->comments) == 0)
<div class="text-center"><h4 class="text-bold text-danger">
<i class="{{ config('other.font-awesome') }} fa-frown"></i> {{ __('common.no-comments') }}
!</h4>
</div>
@else
@foreach ($playlist->comments as $comment)
<li class="media" style="border-left: 5px solid #01BC8C;">
<div class="media-body">
@if ($comment->anon == 1)
<a href="#" class="pull-left" style="padding-right: 10px;">
<img src="{{ url('img/profile.png') }}" class="img-avatar-48">
<strong>{{ strtoupper(__('common.anonymous')) }}</strong></a> @if (auth()->user()->id == $comment->user->id || auth()->user()->group->is_modo)
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};">(<span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span>)</a> @endif
@else
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;">
@if ($comment->user->image != null)
<img src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}"
class="img-avatar-48"></a>
@else
<img src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}"
class="img-avatar-48"></a>
@endif
<strong><a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"><span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span></a></strong> @endif
<span class="text-muted"><small><em>{{ $comment->created_at->toDayDateTimeString() }} ({{ $comment->created_at->diffForHumans() }})</em></small></span>
@if ($comment->user_id == auth()->id() || auth()->user()->group->is_modo)
<div class="pull-right" style="display: inline-block;">
<a data-toggle="modal"
data-target="#modal-comment-edit-{{ $comment->id }}">
<button class="btn btn-circle btn-info">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
</button>
</a>
<form action="{{ route('comment_delete', ['comment_id' => $comment->id]) }}"
method="POST" style="display: inline-block;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-circle btn-danger">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
</button>
</form>
</div>
@endif
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@include('partials.modals', ['comment' => $comment])
@endforeach
@endif
</ul>
</div>
</div>
</div>
<br>
<div class="col-md-12">
<form role="form" method="POST" action="{{ route('comment_playlist', ['id' => $playlist->id]) }}">
@csrf
<div class="form-group">
<label for="content">{{ __('common.your-comment') }}:</label>
<span class="badge-extra">BBCode {{ __('common.is-allowed') }}</span>
<span class="pull-right" x-data="{ emoji: false }">
<img src="{{ url('img/emoji-add.png') }}" width="32px" x-on:click="emoji = ! emoji">
<div style="position: absolute; z-index: 1;" x-show="emoji" @click.away="emoji = false">
<emoji-picker></emoji-picker>
</div>
</span>
<textarea id="editor" name="content" cols="30" rows="5" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-danger">{{ __('common.submit') }}</button>
<label class="radio-inline"><strong>{{ __('common.anonymous') }} {{ __('common.comment') }}
:</strong></label>
<input type="radio" value="1" name="anonymous"> {{ __('common.yes') }}
<input type="radio" value="0" checked="checked" name="anonymous"> {{ __('common.no') }}
</form>
</div>
<div class="col-md-12 col-sm-12">
<livewire:comments :model="$playlist"/>
</div>
</div>
</div>

View File

@@ -377,119 +377,10 @@
</div>
</div>
</div>
<div class="block" id="comments">
<div class="clearfix"></div>
<div class="row ">
<div class="col-md-12 col-sm-12">
<div class="panel panel-danger">
<div class="panel-heading border-light">
<h4 class="panel-title">
<i class="{{ config('other.font-awesome') }} fa-comment"></i> {{ __('common.comments') }}
</h4>
</div>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($comments) == 0)
<div class="text-center">
<h4 class="text-bold text-danger"><i
class="{{ config('other.font-awesome') }} fa-frown"></i> {{ __('common.no-comments') }}
!
</h4></div>
@else @foreach ($comments as $comment)
<li class="media" style="border-left: 5px solid #01bc8c;">
<div class="media-body">
@if ($comment->anon == 1)
<a href="#" class="pull-left" style="padding-right: 10px;">
<img src="{{ url('img/profile.png') }}" class="img-avatar-48">
<strong>{{ strtoupper(__('common.anonymous')) }}</strong></a> @if (auth()->user()->id == $comment->user->id || auth()->user()->group->is_modo)
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};">(<span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span>)</a>
@endif
@else
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;">
@if ($comment->user->image != null)
<img src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}"
class="img-avatar-48"></a>
@else
<img src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}"
class="img-avatar-48"></a>
@endif
<strong><a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"><span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span></a></strong>
@endif
<span class="text-muted"><small><em>{{ $comment->created_at->toDayDateTimeString() }} ({{ $comment->created_at->diffForHumans() }})</em></small></span>
@if ($comment->user_id == auth()->id() || auth()->user()->group->is_modo)
<div class="pull-right" style="display: inline-block;">
<a data-toggle="modal"
data-target="#modal-comment-edit-{{ $comment->id }}">
<button class="btn btn-circle btn-info">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
</button>
</a>
<form action="{{ route('comment_delete', ['comment_id' => $comment->id]) }}"
method="POST" style="display: inline-block;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-circle btn-danger">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
</button>
</form>
</div>
@endif
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@include('partials.modals', ['comment' => $comment])
@endforeach
@endif
</ul>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="col-md-12 home-pagination">
<div class="text-center">{{ $comments->links() }}</div>
</div>
<br>
<div class="col-md-12">
<form role="form" method="POST"
action="{{ route('comment_request',['id' => $torrentRequest->id]) }}">
@csrf
<div class="form-group">
<label for="content">{{ __('common.your-comment') }}:</label>
<span class="badge-extra">BBCode {{ strtolower(__('common.is-allowed')) }}</span>
<span class="pull-right" x-data="{ emoji: false }">
<img src="{{ url('img/emoji-add.png') }}" width="32px" x-on:click="emoji = ! emoji">
<div style="position: absolute; z-index: 1;" x-show="emoji" @click.away="emoji = false">
<emoji-picker></emoji-picker>
</div>
</span>
<textarea id="editor" name="content" cols="30" rows="5"
class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-danger">{{ __('common.submit') }}</button>
<label class="radio-inline"><strong>{{ __('common.anonymous') }} {{ strtolower(__('common.comment')) }}
:</strong></label>
<label>
<input type="radio" value="1" name="anonymous">
</label> {{ __('common.yes') }}
<label>
<input type="radio" value="0" checked="checked" name="anonymous">
</label> {{ __('common.no') }}
</form>
</div>
</div>
<div class="col-md-12 col-sm-12">
<livewire:comments :model="$torrentRequest"/>
</div>
</div>
</div>
@include('requests.request_modals')
@endif
@endsection

View File

@@ -167,67 +167,10 @@
@endif
</div>
</div>
</div>
<div class="col-md-12 col-sm-12">
<div class="panel panel-chat shoutbox">
<div class="panel-heading">
<h4>
<i class="{{ config('other.font-awesome') }} fa-comment"></i> {{ __('common.comments') }}
</h4>
</div>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($ticket->comments) == 0)
<div class="text-center">
<h4 class="text-bold text-danger">
<i class="{{ config('other.font-awesome') }} fa-frown"></i> {{ __('common.no-comments') }}
!
</h4>
</div>
@else
@foreach ($ticket->comments as $comment)
<li class="media" style="border-left: 5px solid rgb(1,188,140);">
<div class="media-body">
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;">
@if ($comment->user->image != null)
<img src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"></a>
@else
<img src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}"
class="img-avatar-48"></a>
@endif
<strong>
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};">
<span><i class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span>
</a>
</strong>
<span class="text-muted"><small><em>{{ $comment->created_at->toDayDateTimeString() }} ({{ $comment->created_at->diffForHumans() }})</em></small></span>
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@endforeach
@endif
</ul>
</div>
</div>
</div>
<div class="col-md-12">
<form role="form" method="POST" action="{{ route('comment_ticket', ['id' => $ticket->id]) }}">
@csrf
<div class="form-group">
<label for="content">{{ __('common.your-comment') }}:</label>
<textarea id="editor" name="content" cols="30" rows="5" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-success">{{ __('common.submit') }}</button>
</form>
<livewire:comments :model="$ticket"/>
</div>
</div>
</div>

View File

@@ -38,13 +38,6 @@
</button>
@endif
<form action="{{ route('comment_thanks', ['id' => $torrent->id]) }}" method="POST" style="display: inline;">
@csrf
<button type="submit" class="btn btn-sm btn-primary">
<i class='{{ config("other.font-awesome") }} fa-heart'></i> {{ __('torrent.quick-comment') }}
</button>
</form>
<a data-toggle="modal" href="#myModal" role="button" class="btn btn-sm btn-primary">
<i class='{{ config("other.font-awesome") }} fa-file'></i> {{ __('torrent.show-files') }}
</a>

View File

@@ -1,107 +1 @@
<div class="clearfix"></div>
<div class="row ">
<div class="col-md-12 col-sm-12">
<div class="panel panel-chat shoutbox">
<div class="panel-heading">
<h4>
<i class="{{ config('other.font-awesome') }} fa-comment"></i> {{ __('common.comments') }}
</h4>
</div>
<div class="panel-body no-padding">
<ul class="media-list comments-list">
@if (count($comments) == 0)
<div class="text-center"><h4 class="text-bold text-danger"><i
class="{{ config('other.font-awesome') }} fa-frown"></i> {{ __('common.no-comments') }}
!</h4>
</div>
@else
@foreach ($comments as $comment)
<li class="media" style="border-left: 5px solid rgb(1,188,140);">
<div class="media-body">
@if ($comment->anon == 1)
<a href="#" class="pull-left" style="padding-right: 10px;">
<img src="{{ url('img/profile.png') }}" class="img-avatar-48">
<strong>{{ strtoupper(__('common.anonymous')) }}</strong></a> @if (auth()->user()->id == $comment->user->id || auth()->user()->group->is_modo)
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};">(<span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span>)</a> @endif
@else
<a href="{{ route('users.show', ['username' => $comment->user->username]) }}"
class="pull-left" style="padding-right: 10px;">
@if ($comment->user->image != null)
<img src="{{ url('files/img/' . $comment->user->image) }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"></a>
@else
<img src="{{ url('img/profile.png') }}"
alt="{{ $comment->user->username }}" class="img-avatar-48"></a>
@endif
<strong><a
href="{{ route('users.show', ['username' => $comment->user->username]) }}"
style="color:{{ $comment->user->group->color }};"><span><i
class="{{ $comment->user->group->icon }}"></i> {{ $comment->user->username }}</span></a></strong> @endif
<span class="text-muted"><small><em>{{ $comment->created_at->toDayDateTimeString() }} ({{ $comment->created_at->diffForHumans() }})</em></small></span>
@if ($comment->user_id == auth()->id() || auth()->user()->group->is_modo)
<div class="pull-right" style="display: inline-block;">
<a data-toggle="modal" data-target="#modal-comment-edit-{{ $comment->id }}">
<button class="btn btn-circle btn-info">
<i class="{{ config('other.font-awesome') }} fa-pencil"></i>
</button>
</a>
<form action="{{ route('comment_delete', ['comment_id' => $comment->id]) }}"
method="POST" style="display: inline-block;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-circle btn-danger">
<i class="{{ config('other.font-awesome') }} fa-trash"></i>
</button>
</form>
</div>
@endif
<div class="pt-5">
@joypixels($comment->getContentHtml())
</div>
</div>
</li>
@include('partials.modals', ['comment' => $comment])
@endforeach
@endif
</ul>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="col-md-12 home-pagination">
<div class="text-center">{{ $comments->links() }}</div>
</div>
<br>
<div class="col-md-12">
<form role="form" method="POST"
action="{{ route('comment_torrent', ['id' => $torrent->id]) }}">
@csrf
<div class="form-group">
<label for="content">{{ __('common.your-comment') }}:</label>
<span class="badge-extra">BBCode {{ __('common.is-allowed') }}</span>
<span class="pull-right" x-data="{ emoji: false }">
<img src="{{ url('img/emoji-add.png') }}" width="32px" x-on:click="emoji = ! emoji">
<div style="position: absolute; z-index: 1;" x-show="emoji" @click.away="emoji = false">
<emoji-picker></emoji-picker>
</div>
</span>
<textarea id="editor" name="content" cols="30" rows="5" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-danger">{{ __('common.submit') }}</button>
<label class="radio-inline"><strong>{{ __('common.anonymous') }} {{ __('common.comment') }}
:</strong></label>
<label>
<input type="radio" value="1" name="anonymous">
</label> {{ __('common.yes') }}
<label>
<input type="radio" value="0" checked="checked" name="anonymous">
</label> {{ __('common.no') }}
</form>
</div>
</div>
<livewire:comments :model="$torrent"/>

View File

@@ -371,17 +371,17 @@
<ul class="list-inline mb-0">
<li>
<span class="badge-extra"><strong>{{ __('user.article-comments') }}:</strong>
<span class="text-green text-bold">{{ $user->comments()->where('article_id', '>', 0)->count() }}</span>
<span class="text-green text-bold">{{ $user->comments()->whereHasMorph('commentable', [App\Models\Article::class])->count() }}</span>
</span>
</li>
<li>
<span class="badge-extra"><strong>{{ __('user.torrent-comments') }}:</strong>
<span class="text-green text-bold">{{ $user->comments()->where('torrent_id', '>', 0)->count() }}</span>
<span class="text-green text-bold">{{ $user->comments()->whereHasMorph('commentable', [App\Models\Torrent::class])->count() }}</span>
</span>
</li>
<li>
<span class="badge-extra"><strong>{{ __('user.request-comments') }}:</strong>
<span class="text-green text-bold">{{ $user->comments()->where('requests_id', '>', 0)->count() }}</span>
<span class="text-green text-bold">{{ $user->comments()->whereHasMorph('commentable', [App\Models\TorrentRequest::class])->count() }}</span>
</span>
</li>
</ul>

View File

@@ -133,19 +133,6 @@ Route::group(['middleware' => 'language'], function () {
Route::get('/{id}', [App\Http\Controllers\PageController::class, 'show'])->where('id', '[0-9]+')->name('pages.show');
});
// Comments System
Route::group(['prefix' => 'comments'], function () {
Route::post('/article/{id}', [App\Http\Controllers\CommentController::class, 'article'])->name('comment_article');
Route::post('/torrent/{id}', [App\Http\Controllers\CommentController::class, 'torrent'])->name('comment_torrent');
Route::post('/thanks/{id}', [App\Http\Controllers\CommentController::class, 'quickthanks'])->name('comment_thanks');
Route::post('/request/{id}', [App\Http\Controllers\CommentController::class, 'request'])->name('comment_request');
Route::post('/playlist/{id}', [App\Http\Controllers\CommentController::class, 'playlist'])->name('comment_playlist');
Route::post('/collection/{id}', [App\Http\Controllers\CommentController::class, 'collection'])->name('comment_collection');
Route::post('/ticket/{id}', [App\Http\Controllers\CommentController::class, 'ticket'])->name('comment_ticket');
Route::post('/edit/{comment_id}', [App\Http\Controllers\CommentController::class, 'editComment'])->name('comment_edit');
Route::delete('/delete/{comment_id}', [App\Http\Controllers\CommentController::class, 'deleteComment'])->name('comment_delete');
});
// Extra-Stats System
Route::group(['prefix' => 'stats'], function () {
Route::get('/', [App\Http\Controllers\StatsController::class, 'index'])->name('stats');

View File

@@ -1,205 +0,0 @@
<?php
namespace Tests\Feature\Http\Controllers;
use App\Models\Article;
use App\Models\Comment;
use App\Models\Playlist;
use App\Models\Torrent;
use App\Models\TorrentRequest;
use App\Models\User;
use Database\Seeders\BotsTableSeeder;
use Database\Seeders\ChatroomTableSeeder;
use Database\Seeders\GroupsTableSeeder;
use Database\Seeders\UsersTableSeeder;
use Tests\TestCase;
/**
* @see \App\Http\Controllers\CommentController
*/
class CommentControllerTest extends TestCase
{
/**
* @test
*/
public function article_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$article = Article::factory()->create([
'user_id' => $user->id,
]);
$response = $this->actingAs($user)->post(route('comment_article', ['id' => $article->id]), [
'content' => 'foo',
'anonymous' => '0',
]);
$response->assertRedirect(route('articles.show', $article->id))
->assertSessionHas('success', 'Your Comment Has Been Added!');
}
/**
* @test
*/
public function delete_comment_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$comment = Comment::factory()->create([
'user_id' => $user->id,
]);
$response = $this->actingAs($user)->delete(route('comment_delete', ['comment_id' => $comment->id]));
$response->assertRedirect(route('home.index'))
->assertSessionHas('success', 'Comment Has Been Deleted.');
}
/**
* @test
*/
public function edit_comment_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$comment = Comment::factory()->create([
'user_id' => $user->id,
]);
$response = $this->actingAs($user)->post(route('comment_edit', ['comment_id' => $comment->id]), [
'comment-edit' => 'bar',
]);
$response->assertRedirect()
->assertSessionHas('success', 'Comment Has Been Edited.');
}
/**
* @test
*/
public function playlist_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$playlist = Playlist::factory()->create([
'user_id' => $user->id,
]);
$response = $this->actingAs($user)->post(route('comment_playlist', ['id' => $playlist->id]), [
'content' => 'foo',
'anonymous' => 0,
]);
$response->assertRedirect(route('playlists.show', [
'id' => $playlist->id,
'hash' => '#comments',
]))->assertSessionHas('success', 'Your Comment Has Been Added!');
}
/**
* @test
*/
public function quickthanks_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$torrent = Torrent::factory()->create([
'user_id' => $user->id,
'status' => 1,
]);
$response = $this->actingAs($user)->post(route('comment_thanks', ['id' => $torrent->id]));
$response->assertRedirect(route('torrent', ['id' => $torrent->id]))
->assertSessionHas('success', 'Your Comment Has Been Added!');
}
/**
* @test
*/
public function request_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$torrentRequest = TorrentRequest::factory()->create([
'user_id' => $user->id,
]);
$response = $this->actingAs($user)->post(route('comment_request', ['id' => $torrentRequest->id]), [
'content' => 'foo',
'anonymous' => 0,
]);
$response->assertRedirect(route('request', ['id' => $torrentRequest->id, 'hash' => '#comments']))
->assertSessionHas('success', 'Your Comment Has Been Added!');
}
/**
* @test
*/
public function torrent_returns_an_ok_response(): void
{
$this->seed(UsersTableSeeder::class);
$this->seed(GroupsTableSeeder::class);
$this->seed(BotsTableSeeder::class);
$this->seed(ChatroomTableSeeder::class);
$user = User::factory()->create([
'can_comment' => true,
]);
$torrent = Torrent::factory()->create([
'user_id' => $user->id,
'status' => 1,
]);
$response = $this->actingAs($user)->post(route('comment_torrent', ['id' => $torrent->id]), [
'content' => 'foo',
'anonymous' => 0,
]);
$response->assertRedirect(route('torrent', ['id' => $torrent->id, 'hash' => '#comments']))
->assertSessionHas('success', 'Your Comment Has Been Added!');
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Tests\Todo\Feature\Http\Controllers;
use Tests\TestCase;
/**
* @see \App\Http\Controllers\CommentController
*/
class CommentControllerTest extends TestCase
{
/**
* @test
*/
public function store_comment_returns_an_ok_response(): void
{
$this->markTestIncomplete('This test case was generated by Shift. When you are ready, remove this line and complete this test case.');
}
/**
* @test
*/
public function delete_comment_returns_an_ok_response(): void
{
$this->markTestIncomplete('This test case was generated by Shift. When you are ready, remove this line and complete this test case.');
}
/**
* @test
*/
public function edit_comment_returns_an_ok_response(): void
{
$this->markTestIncomplete('This test case was generated by Shift. When you are ready, remove this line and complete this test case.');
}
}