Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
4cc7365
Merge pull request #1 from ssddanbrown/master
Abijeet Sep 25, 2016
d2efc2f
Merge pull request #2 from BookStackApp/master
Abijeet Nov 28, 2016
581881d
Merging gitignore.
Abijeet Nov 28, 2016
cd6572b
Merge pull request #3 from BookStackApp/master
Abijeet Jan 3, 2017
397db04
Added comments controller, model, repo, and the database schema. Modi…
Abijeet Jan 13, 2017
e5c4e0a
Merge pull request #6 from BookStackApp/master
Abijeet Jan 29, 2017
70991fc
Merge branch 'master' of https://github.com/Abijeet/BookStack
Abijeet Jan 29, 2017
1483500
#47 Adds comment permission to each role.
Abijeet Jan 29, 2017
388f2f4
Merge pull request #7 from BookStackApp/master
Abijeet Feb 5, 2017
410e967
Merge branch 'master' of https://github.com/Abijeet/BookStack
Abijeet Feb 5, 2017
4e71a5a
Merge pull request #8 from BookStackApp/master
Abijeet Mar 25, 2017
b3d4c19
Merge branch 'master' of https://github.com/Abijeet/BookStack
Abijeet Apr 18, 2017
9de8528
Merge pull request #9 from BookStackApp/master
Abijeet Apr 18, 2017
8e24374
Merge branch 'master' of https://github.com/Abijeet/BookStack
Abijeet Apr 18, 2017
d447355
Adding the view templates and styles.
Abijeet Apr 18, 2017
c3ea0d3
#47 - Adds functionality to display child comments. Also has some cod…
Abijeet Apr 26, 2017
3368fe4
Merge pull request #10 from BookStackApp/master
Abijeet May 2, 2017
8b82753
#47 - Gets rid of simplemde
Abijeet May 2, 2017
4f231d1
Merge pull request #11 from BookStackApp/master
Abijeet May 15, 2017
03e5d61
#47 Implements the reply and edit functionality for comments.
Abijeet May 15, 2017
0ff5aad
#47 Hides the reply button based if comments are 2 levels deep.
Abijeet May 24, 2017
34802ff
#47 Inserts null for updated_at when the user is creating a comment.
Abijeet May 25, 2017
1a1e71c
#47 Adds two attributes updated and created to display time to user.
Abijeet May 25, 2017
9a97995
#47 Displays the time for comments and border bottom for sub comments.
Abijeet May 25, 2017
860d4d4
#47 - Changes the way we are handling fetching of data for the commen…
Abijeet May 30, 2017
0bda555
Getting the latest changes
Abijeet Jun 4, 2017
6ff440e
Merge branch 'BookStackApp-master'
Abijeet Jun 4, 2017
2fd421b
#47 - Adds comment level permissions to the front-end.
Abijeet Jun 4, 2017
9558f84
#47 - Adds functionality to delete a comment. Also reduces the number…
Abijeet Jun 4, 2017
06d75e1
#47 - Updates the total comments when a comment is added.
Abijeet Jun 4, 2017
689d1eb
#47 - Adds a cancel button for edit and reply button.
Abijeet Jun 4, 2017
7c6fe8c
#47 - Changes the location of the reply and edit comment box.
Abijeet Jun 4, 2017
5bd9da6
#47 - Adds various translations in English, and a few code improvements.
Abijeet Jun 5, 2017
652a67a
Removes some unncessary code.
Abijeet Jun 6, 2017
38fe756
#47 - Fixes a couple of issues found during testing - delete not upda…
Abijeet Jun 7, 2017
e647ec2
#47 - Adds direct linking to comments.
Abijeet Jun 7, 2017
218376a
#47 - Fetching values from language files.
Abijeet Jun 7, 2017
4efe3b4
#47 - Added translations for other language files using Google transl…
Abijeet Jun 10, 2017
552943c
#47 - Undos changes in config files.
Abijeet Jun 10, 2017
c76e7c7
adding a comment on top.
Abijeet Jun 10, 2017
e155c52
#47 - Fixes a few issues with the code.
Abijeet Jun 10, 2017
8dab31b
Merge branch 'master' of https://github.com/Abijeet/BookStack
Abijeet Jun 10, 2017
9dbd7fa
#47 - Adding comments to the dummy content seeder.
Abijeet Jun 11, 2017
fd50efb
#47 - Putting the comments right under the page.
Abijeet Jun 11, 2017
7d02f77
#47 - Added more test cases to test the APIs and permission for comme…
Abijeet Jun 12, 2017
574ee82
#47 - Fixes the issues with the test case.
Abijeet Jun 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ Homestead.yaml
/public/css
/public/js
/public/bower
/public/build/
/storage/images
_ide_helper.php
/storage/debugbar
.phpstorm.meta.php
yarn.lock
/bin
nbproject
.buildpath

.project

.settings/org.eclipse.wst.common.project.facet.core.xml

.settings/org.eclipse.php.core.prefs
96 changes: 96 additions & 0 deletions app/Comment.php
Original file line number Diff line number Diff line change
@@ -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 changes: 99 additions & 0 deletions app/Http/Controllers/CommentController.php
Original file line number Diff line number Diff line change
@@ -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]);
}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public function show($bookSlug, $pageSlug)
$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', [
Expand Down Expand Up @@ -376,7 +376,7 @@ public function showRevision($bookSlug, $pageSlug, $revisionId)

$page->fill($revision->toArray());
$this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));

return view('pages/revision', [
'page' => $page,
'book' => $page->book,
Expand Down
4 changes: 4 additions & 0 deletions app/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public function attachments()
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
Expand Down
105 changes: 105 additions & 0 deletions app/Repos/CommentRepo.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 1 addition & 1 deletion app/Services/PermissionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ public function checkOwnableUserAccess(Ownable $ownable, $permission)
$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)) {
Expand Down
10 changes: 10 additions & 0 deletions database/factories/ModelFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,14 @@
'type' => 'gallery',
'uploaded_to' => 0
];
});

$factory->define(BookStack\Comment::class, function($faker) {
$text = $faker->paragraph(3);
$html = '<p>' . $text. '</p>';
return [
'html' => $html,
'text' => $text,
'active' => 1
];
});
Loading