1
0
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:
Dan Brown
2017-08-01 19:24:33 +01:00
42 changed files with 1420 additions and 11 deletions

96
app/Comment.php Normal file
View 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;
}
}

View 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]);
}
}

View File

@ -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,

View File

@ -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
View 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;
}
}

View File

@ -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)) {