mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-28 17:02:04 +03:00
Merge branch 'master' of git://github.com/Abijeet/BookStack into Abijeet-master
This commit is contained in:
96
app/Comment.php
Normal file
96
app/Comment.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack;
|
||||
|
||||
class Comment extends Ownable
|
||||
{
|
||||
public $sub_comments = [];
|
||||
protected $fillable = ['text', 'html', 'parent_id'];
|
||||
protected $appends = ['created', 'updated', 'sub_comments'];
|
||||
/**
|
||||
* Get the entity that this comment belongs to
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function entity()
|
||||
{
|
||||
return $this->morphTo('entity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page that this comment is in.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function page()
|
||||
{
|
||||
return $this->belongsTo(Page::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the owner of this comment.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/*
|
||||
* Not being used, but left here because might be used in the future for performance reasons.
|
||||
*/
|
||||
public function getPageComments($pageId) {
|
||||
$query = static::newQuery();
|
||||
$query->join('users AS u', 'comments.created_by', '=', 'u.id');
|
||||
$query->leftJoin('users AS u1', 'comments.updated_by', '=', 'u1.id');
|
||||
$query->leftJoin('images AS i', 'i.id', '=', 'u.image_id');
|
||||
$query->selectRaw('comments.id, text, html, comments.created_by, comments.updated_by, '
|
||||
. 'comments.created_at, comments.updated_at, comments.parent_id, '
|
||||
. 'u.name AS created_by_name, u1.name AS updated_by_name, '
|
||||
. 'i.url AS avatar ');
|
||||
$query->whereRaw('page_id = ?', [$pageId]);
|
||||
$query->orderBy('created_at');
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
public function getAllPageComments($pageId) {
|
||||
return self::where('page_id', '=', $pageId)->with(['createdBy' => function($query) {
|
||||
$query->select('id', 'name', 'image_id');
|
||||
}, 'updatedBy' => function($query) {
|
||||
$query->select('id', 'name');
|
||||
}, 'createdBy.avatar' => function ($query) {
|
||||
$query->select('id', 'path', 'url');
|
||||
}])->get();
|
||||
}
|
||||
|
||||
public function getCommentById($commentId) {
|
||||
return self::where('id', '=', $commentId)->with(['createdBy' => function($query) {
|
||||
$query->select('id', 'name', 'image_id');
|
||||
}, 'updatedBy' => function($query) {
|
||||
$query->select('id', 'name');
|
||||
}, 'createdBy.avatar' => function ($query) {
|
||||
$query->select('id', 'path', 'url');
|
||||
}])->first();
|
||||
}
|
||||
|
||||
public function getCreatedAttribute() {
|
||||
$created = [
|
||||
'day_time_str' => $this->created_at->toDayDateTimeString(),
|
||||
'diff' => $this->created_at->diffForHumans()
|
||||
];
|
||||
return $created;
|
||||
}
|
||||
|
||||
public function getUpdatedAttribute() {
|
||||
if (empty($this->updated_at)) {
|
||||
return null;
|
||||
}
|
||||
$updated = [
|
||||
'day_time_str' => $this->updated_at->toDayDateTimeString(),
|
||||
'diff' => $this->updated_at->diffForHumans()
|
||||
];
|
||||
return $updated;
|
||||
}
|
||||
|
||||
public function getSubCommentsAttribute() {
|
||||
return $this->sub_comments;
|
||||
}
|
||||
}
|
99
app/Http/Controllers/CommentController.php
Normal file
99
app/Http/Controllers/CommentController.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Repos\CommentRepo;
|
||||
use BookStack\Repos\EntityRepo;
|
||||
use BookStack\Comment;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
protected $entityRepo;
|
||||
|
||||
public function __construct(EntityRepo $entityRepo, CommentRepo $commentRepo, Comment $comment)
|
||||
{
|
||||
$this->entityRepo = $entityRepo;
|
||||
$this->commentRepo = $commentRepo;
|
||||
$this->comment = $comment;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function save(Request $request, $pageId, $commentId = null)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'text' => 'required|string',
|
||||
'html' => 'required|string',
|
||||
]);
|
||||
|
||||
try {
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return response('Not found', 404);
|
||||
}
|
||||
|
||||
if($page->draft) {
|
||||
// cannot add comments to drafts.
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => trans('errors.cannot_add_comment_to_draft'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
if (empty($commentId)) {
|
||||
// create a new comment.
|
||||
$this->checkPermission('comment-create-all');
|
||||
$comment = $this->commentRepo->create($page, $request->only(['text', 'html', 'parent_id']));
|
||||
$respMsg = trans('entities.comment_created');
|
||||
} else {
|
||||
// update existing comment
|
||||
// get comment by ID and check if this user has permission to update.
|
||||
$comment = $this->comment->findOrFail($commentId);
|
||||
$this->checkOwnablePermission('comment-update', $comment);
|
||||
$this->commentRepo->update($comment, $request->all());
|
||||
$respMsg = trans('entities.comment_updated');
|
||||
}
|
||||
|
||||
$comment = $this->commentRepo->getCommentById($comment->id);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => $respMsg,
|
||||
'comment' => $comment
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function destroy($id) {
|
||||
$comment = $this->comment->findOrFail($id);
|
||||
$this->checkOwnablePermission('comment-delete', $comment);
|
||||
$this->commentRepo->delete($comment);
|
||||
$updatedComment = $this->commentRepo->getCommentById($comment->id);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => trans('entities.comment_deleted'),
|
||||
'comment' => $updatedComment
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function getPageComments($pageId) {
|
||||
try {
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return response('Not found', 404);
|
||||
}
|
||||
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
|
||||
$comments = $this->commentRepo->getPageComments($pageId);
|
||||
return response()->json(['status' => 'success', 'comments'=> $comments['comments'],
|
||||
'total' => $comments['total'], 'permissions' => [
|
||||
'comment_create' => $this->currentUser->can('comment-create-all'),
|
||||
'comment_update_own' => $this->currentUser->can('comment-update-own'),
|
||||
'comment_update_all' => $this->currentUser->can('comment-update-all'),
|
||||
'comment_delete_all' => $this->currentUser->can('comment-delete-all'),
|
||||
'comment_delete_own' => $this->currentUser->can('comment-delete-own'),
|
||||
], 'user_id' => $this->currentUser->id]);
|
||||
}
|
||||
}
|
@ -161,7 +161,7 @@ class PageController extends Controller
|
||||
$pageContent = $this->entityRepo->renderPage($page);
|
||||
$sidebarTree = $this->entityRepo->getBookChildren($page->book);
|
||||
$pageNav = $this->entityRepo->getPageNav($pageContent);
|
||||
|
||||
|
||||
Views::add($page);
|
||||
$this->setPageTitle($page->getShortName());
|
||||
return view('pages/show', [
|
||||
@ -376,7 +376,7 @@ class PageController extends Controller
|
||||
|
||||
$page->fill($revision->toArray());
|
||||
$this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
|
||||
|
||||
|
||||
return view('pages/revision', [
|
||||
'page' => $page,
|
||||
'book' => $page->book,
|
||||
|
@ -66,6 +66,10 @@ class Page extends Entity
|
||||
return $this->hasMany(Attachment::class, 'uploaded_to')->orderBy('order', 'asc');
|
||||
}
|
||||
|
||||
public function comments() {
|
||||
return $this->hasMany(Comment::class, 'page_id')->orderBy('created_on', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url for this page.
|
||||
* @param string|bool $path
|
||||
|
105
app/Repos/CommentRepo.php
Normal file
105
app/Repos/CommentRepo.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
use BookStack\Comment;
|
||||
use BookStack\Page;
|
||||
|
||||
/**
|
||||
* Class TagRepo
|
||||
* @package BookStack\Repos
|
||||
*/
|
||||
class CommentRepo {
|
||||
/**
|
||||
*
|
||||
* @var Comment $comment
|
||||
*/
|
||||
protected $comment;
|
||||
|
||||
public function __construct(Comment $comment)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
}
|
||||
|
||||
public function create (Page $page, $data = []) {
|
||||
$userId = user()->id;
|
||||
$comment = $this->comment->newInstance();
|
||||
$comment->fill($data);
|
||||
// new comment
|
||||
$comment->page_id = $page->id;
|
||||
$comment->created_by = $userId;
|
||||
$comment->updated_at = null;
|
||||
$comment->save();
|
||||
return $comment;
|
||||
}
|
||||
|
||||
public function update($comment, $input, $activeOnly = true) {
|
||||
$userId = user()->id;
|
||||
$comment->updated_by = $userId;
|
||||
$comment->fill($input);
|
||||
|
||||
// only update active comments by default.
|
||||
$whereClause = ['active' => 1];
|
||||
if (!$activeOnly) {
|
||||
$whereClause = [];
|
||||
}
|
||||
$comment->update($whereClause);
|
||||
return $comment;
|
||||
}
|
||||
|
||||
public function delete($comment) {
|
||||
$comment->text = trans('entities.comment_deleted');
|
||||
$comment->html = trans('entities.comment_deleted');
|
||||
$comment->active = false;
|
||||
$userId = user()->id;
|
||||
$comment->updated_by = $userId;
|
||||
$comment->save();
|
||||
return $comment;
|
||||
}
|
||||
|
||||
public function getPageComments($pageId) {
|
||||
$comments = $this->comment->getAllPageComments($pageId);
|
||||
$index = [];
|
||||
$totalComments = count($comments);
|
||||
$finalCommentList = [];
|
||||
|
||||
// normalizing the response.
|
||||
for ($i = 0; $i < count($comments); ++$i) {
|
||||
$comment = $this->normalizeComment($comments[$i]);
|
||||
$parentId = $comment->parent_id;
|
||||
if (empty($parentId)) {
|
||||
$finalCommentList[] = $comment;
|
||||
$index[$comment->id] = $comment;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($index[$parentId])) {
|
||||
// weird condition should not happen.
|
||||
continue;
|
||||
}
|
||||
if (empty($index[$parentId]->sub_comments)) {
|
||||
$index[$parentId]->sub_comments = [];
|
||||
}
|
||||
array_push($index[$parentId]->sub_comments, $comment);
|
||||
$index[$comment->id] = $comment;
|
||||
}
|
||||
return [
|
||||
'comments' => $finalCommentList,
|
||||
'total' => $totalComments
|
||||
];
|
||||
}
|
||||
|
||||
public function getCommentById($commentId) {
|
||||
return $this->normalizeComment($this->comment->getCommentById($commentId));
|
||||
}
|
||||
|
||||
private function normalizeComment($comment) {
|
||||
if (empty($comment)) {
|
||||
return;
|
||||
}
|
||||
$comment->createdBy->avatar_url = $comment->createdBy->getAvatar(50);
|
||||
$comment->createdBy->profile_url = $comment->createdBy->getProfileUrl();
|
||||
if (!empty($comment->updatedBy)) {
|
||||
$comment->updatedBy->profile_url = $comment->updatedBy->getProfileUrl();
|
||||
}
|
||||
return $comment;
|
||||
}
|
||||
}
|
@ -468,7 +468,7 @@ class PermissionService
|
||||
$action = end($explodedPermission);
|
||||
$this->currentAction = $action;
|
||||
|
||||
$nonJointPermissions = ['restrictions', 'image', 'attachment'];
|
||||
$nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
|
||||
|
||||
// Handle non entity specific jointPermissions
|
||||
if (in_array($explodedPermission[0], $nonJointPermissions)) {
|
||||
|
Reference in New Issue
Block a user