mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-28 17:02:04 +03:00
Resolved conflicts
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;
|
||||
}
|
||||
}
|
57
app/Console/Commands/UpgradeDatabaseEncoding.php
Normal file
57
app/Console/Commands/UpgradeDatabaseEncoding.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UpgradeDatabaseEncoding extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'bookstack:db-utf8mb4 {--database= : The database connection to use.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate SQL commands to upgrade the database to UTF8mb4';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$connection = DB::getDefaultConnection();
|
||||
if ($this->option('database') !== null) {
|
||||
DB::setDefaultConnection($this->option('database'));
|
||||
}
|
||||
|
||||
$database = DB::getDatabaseName();
|
||||
$tables = DB::select('SHOW TABLES');
|
||||
$this->line('ALTER DATABASE `'.$database.'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
$this->line('USE `'.$database.'`;');
|
||||
$key = 'Tables_in_' . $database;
|
||||
foreach ($tables as $table) {
|
||||
$tableName = $table->$key;
|
||||
$this->line('ALTER TABLE `'.$tableName.'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
}
|
||||
|
||||
DB::setDefaultConnection($connection);
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@ class Kernel extends ConsoleKernel
|
||||
Commands\ClearActivity::class,
|
||||
Commands\ClearRevisions::class,
|
||||
Commands\RegeneratePermissions::class,
|
||||
Commands\RegenerateSearch::class
|
||||
Commands\RegenerateSearch::class,
|
||||
Commands\UpgradeDatabaseEncoding::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ use BookStack\Exceptions\UserRegistrationException;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use BookStack\Services\EmailConfirmationService;
|
||||
use BookStack\Services\SocialAuthService;
|
||||
use BookStack\SocialAccount;
|
||||
use BookStack\User;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
@ -103,7 +104,7 @@ class RegisterController extends Controller
|
||||
* @param Request|\Illuminate\Http\Request $request
|
||||
* @return Response
|
||||
* @throws UserRegistrationException
|
||||
* @throws \Illuminate\Foundation\Validation\ValidationException
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function postRegister(Request $request)
|
||||
{
|
||||
@ -255,16 +256,13 @@ class RegisterController extends Controller
|
||||
*/
|
||||
public function socialCallback($socialDriver)
|
||||
{
|
||||
if (session()->has('social-callback')) {
|
||||
$action = session()->pull('social-callback');
|
||||
if ($action == 'login') {
|
||||
return $this->socialAuthService->handleLoginCallback($socialDriver);
|
||||
} elseif ($action == 'register') {
|
||||
return $this->socialRegisterCallback($socialDriver);
|
||||
}
|
||||
} else {
|
||||
if (!session()->has('social-callback')) {
|
||||
throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
|
||||
}
|
||||
|
||||
$action = session()->pull('social-callback');
|
||||
if ($action == 'login') return $this->socialAuthService->handleLoginCallback($socialDriver);
|
||||
if ($action == 'register') return $this->socialRegisterCallback($socialDriver);
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
|
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;
|
||||
}
|
||||
}
|
@ -571,7 +571,7 @@ class EntityRepo
|
||||
|
||||
$draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
|
||||
$draftPage->html = $this->formatHtml($input['html']);
|
||||
$draftPage->text = strip_tags($draftPage->html);
|
||||
$draftPage->text = $this->pageToPlainText($draftPage);
|
||||
$draftPage->draft = false;
|
||||
$draftPage->revision_count = 1;
|
||||
|
||||
@ -713,6 +713,17 @@ class EntityRepo
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain text version of a page's content.
|
||||
* @param Page $page
|
||||
* @return string
|
||||
*/
|
||||
public function pageToPlainText(Page $page)
|
||||
{
|
||||
$html = $this->renderPage($page);
|
||||
return strip_tags($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new draft page instance.
|
||||
* @param Book $book
|
||||
@ -816,7 +827,7 @@ class EntityRepo
|
||||
$userId = user()->id;
|
||||
$page->fill($input);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
if (setting('app-editor') !== 'markdown') $page->markdown = '';
|
||||
$page->updated_by = $userId;
|
||||
$page->revision_count++;
|
||||
@ -933,7 +944,7 @@ class EntityRepo
|
||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
$page->fill($revision->toArray());
|
||||
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
$page->updated_by = user()->id;
|
||||
$page->save();
|
||||
$this->searchService->indexEntity($page);
|
||||
@ -953,7 +964,7 @@ class EntityRepo
|
||||
if ($page->draft) {
|
||||
$page->fill($data);
|
||||
if (isset($data['html'])) {
|
||||
$page->text = strip_tags($data['html']);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
}
|
||||
$page->save();
|
||||
return $page;
|
||||
|
@ -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