';
+ return [
+ 'html' => $html,
+ 'text' => $text,
+ 'active' => 1
+ ];
});
\ No newline at end of file
diff --git a/database/migrations/2017_01_01_130541_create_comments_table.php b/database/migrations/2017_01_01_130541_create_comments_table.php
new file mode 100644
index 000000000..f4ece31a7
--- /dev/null
+++ b/database/migrations/2017_01_01_130541_create_comments_table.php
@@ -0,0 +1,112 @@
+increments('id')->unsigned();
+ $table->integer('page_id')->unsigned();
+ $table->longText('text')->nullable();
+ $table->longText('html')->nullable();
+ $table->integer('parent_id')->unsigned()->nullable();
+ $table->integer('created_by')->unsigned();
+ $table->integer('updated_by')->unsigned()->nullable();
+ $table->index(['page_id', 'parent_id']);
+ $table->timestamps();
+
+ // Get roles with permissions we need to change
+ $adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
+
+ // Create & attach new entity permissions
+ $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
+ $entity = 'Comment';
+ foreach ($ops as $op) {
+ $permissionId = DB::table('role_permissions')->insertGetId([
+ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
+ 'display_name' => $op . ' ' . $entity . 's',
+ 'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
+ 'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
+ ]);
+ DB::table('permission_role')->insert([
+ 'role_id' => $adminRoleId,
+ 'permission_id' => $permissionId
+ ]);
+ }
+
+ // Get roles with permissions we need to change
+ /*
+ $editorRole = DB::table('roles')->where('name', '=', 'editor')->first();
+ if (!empty($editorRole)) {
+ $editorRoleId = $editorRole->id;
+ // Create & attach new entity permissions
+ $ops = ['Create All', 'Create Own', 'Update Own', 'Delete Own'];
+ $entity = 'Comment';
+ foreach ($ops as $op) {
+ $permissionId = DB::table('role_permissions')->insertGetId([
+ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
+ 'display_name' => $op . ' ' . $entity . 's',
+ 'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
+ 'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
+ ]);
+ DB::table('permission_role')->insert([
+ 'role_id' => $editorRoleId,
+ 'permission_id' => $permissionId
+ ]);
+ }
+ }
+
+ // Get roles with permissions we need to change
+ $viewerRole = DB::table('roles')->where('name', '=', 'viewer')->first();
+ if (!empty($viewerRole)) {
+ $viewerRoleId = $viewerRole->id;
+ // Create & attach new entity permissions
+ $ops = ['Create All'];
+ $entity = 'Comment';
+ foreach ($ops as $op) {
+ $permissionId = DB::table('role_permissions')->insertGetId([
+ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
+ 'display_name' => $op . ' ' . $entity . 's',
+ 'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
+ 'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
+ ]);
+ DB::table('permission_role')->insert([
+ 'role_id' => $viewerRoleId,
+ 'permission_id' => $permissionId
+ ]);
+ }
+ }
+ */
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('comments');
+ // Create & attach new entity permissions
+ $ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
+ $entity = 'Comment';
+ foreach ($ops as $op) {
+ $permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
+ DB::table('role_permissions')->where('name', '=', $permName)->delete();
+ }
+ }
+}
diff --git a/database/migrations/2017_06_04_060012_comments_add_active_col.php b/database/migrations/2017_06_04_060012_comments_add_active_col.php
new file mode 100644
index 000000000..3c6dd1f33
--- /dev/null
+++ b/database/migrations/2017_06_04_060012_comments_add_active_col.php
@@ -0,0 +1,38 @@
+boolean('active')->default(true);
+ $table->dropIndex('comments_page_id_parent_id_index');
+ $table->index(['page_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('comments', function (Blueprint $table) {
+ // reversing the schema
+ $table->dropIndex('comments_page_id_index');
+ $table->dropColumn('active');
+ $table->index(['page_id', 'parent_id']);
+ });
+ }
+}
diff --git a/database/seeds/DummyContentSeeder.php b/database/seeds/DummyContentSeeder.php
index 3d92efab1..996cd178d 100644
--- a/database/seeds/DummyContentSeeder.php
+++ b/database/seeds/DummyContentSeeder.php
@@ -20,7 +20,10 @@ class DummyContentSeeder extends Seeder
->each(function($book) use ($user) {
$chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
->each(function($chapter) use ($user, $book){
- $pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
+ $pages = factory(\BookStack\Page::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id])->each(function($page) use ($user) {
+ $comments = factory(\BookStack\Comment::class, 3)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'page_id' => $page->id]);
+ $page->comments()->saveMany($comments);
+ });
$chapter->pages()->saveMany($pages);
});
$pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
diff --git a/gulpfile.js b/gulpfile.js
index 08c8886bd..f851dd7d6 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,3 +1,5 @@
+'use strict';
+
const argv = require('yargs').argv;
const gulp = require('gulp'),
plumber = require('gulp-plumber');
diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js
index 9337ea889..e1d838bb6 100644
--- a/resources/assets/js/controllers.js
+++ b/resources/assets/js/controllers.js
@@ -675,4 +675,225 @@ module.exports = function (ngApp, events) {
}]);
+ // Controller used to reply to and add new comments
+ ngApp.controller('CommentReplyController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
+ const MarkdownIt = require("markdown-it");
+ const md = new MarkdownIt({html: true});
+ let vm = this;
+
+ vm.saveComment = function () {
+ let pageId = $scope.comment.pageId || $scope.pageId;
+ let comment = $scope.comment.text;
+ if (!comment) {
+ return events.emit('warning', trans('errors.empty_comment'));
+ }
+ let commentHTML = md.render($scope.comment.text);
+ let serviceUrl = `/ajax/page/${pageId}/comment/`;
+ let httpMethod = 'post';
+ let reqObj = {
+ text: comment,
+ html: commentHTML
+ };
+
+ if ($scope.isEdit === true) {
+ // this will be set when editing the comment.
+ serviceUrl = `/ajax/page/${pageId}/comment/${$scope.comment.id}`;
+ httpMethod = 'put';
+ } else if ($scope.isReply === true) {
+ // if its reply, get the parent comment id
+ reqObj.parent_id = $scope.parentId;
+ }
+ $http[httpMethod](window.baseUrl(serviceUrl), reqObj).then(resp => {
+ if (!isCommentOpSuccess(resp)) {
+ return;
+ }
+ // hide the comments first, and then retrigger the refresh
+ if ($scope.isEdit) {
+ updateComment($scope.comment, resp.data);
+ $scope.$emit('evt.comment-success', $scope.comment.id);
+ } else {
+ $scope.comment.text = '';
+ if ($scope.isReply === true && $scope.parent.sub_comments) {
+ $scope.parent.sub_comments.push(resp.data.comment);
+ } else {
+ $scope.$emit('evt.new-comment', resp.data.comment);
+ }
+ $scope.$emit('evt.comment-success', null, true);
+ }
+ $scope.comment.is_hidden = true;
+ $timeout(function() {
+ $scope.comment.is_hidden = false;
+ });
+
+ events.emit('success', trans(resp.data.message));
+
+ }, checkError);
+
+ };
+
+ function checkError(response) {
+ let msg = null;
+ if (isCommentOpSuccess(response)) {
+ // all good
+ return;
+ } else if (response.data) {
+ msg = response.data.message;
+ } else {
+ msg = trans('errors.comment_add');
+ }
+ if (msg) {
+ events.emit('success', msg);
+ }
+ }
+ }]);
+
+ // Controller used to delete comments
+ ngApp.controller('CommentDeleteController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
+ let vm = this;
+
+ vm.delete = function(comment) {
+ $http.delete(window.baseUrl(`/ajax/comment/${comment.id}`)).then(resp => {
+ if (!isCommentOpSuccess(resp)) {
+ return;
+ }
+ updateComment(comment, resp.data, $timeout, true);
+ }, function (resp) {
+ if (isCommentOpSuccess(resp)) {
+ events.emit('success', trans('entities.comment_deleted'));
+ } else {
+ events.emit('error', trans('error.comment_delete'));
+ }
+ });
+ };
+ }]);
+
+ // Controller used to fetch all comments for a page
+ ngApp.controller('CommentListController', ['$scope', '$http', '$timeout', '$location', function ($scope, $http, $timeout, $location) {
+ let vm = this;
+ $scope.errors = {};
+ // keep track of comment levels
+ $scope.level = 1;
+ vm.totalCommentsStr = trans('entities.comments_loading');
+ vm.permissions = {};
+ vm.trans = window.trans;
+
+ $scope.$on('evt.new-comment', function (event, comment) {
+ // add the comment to the comment list.
+ vm.comments.push(comment);
+ ++vm.totalComments;
+ setTotalCommentMsg();
+ event.stopPropagation();
+ event.preventDefault();
+ });
+
+ vm.canEditDelete = function (comment, prop) {
+ if (!comment.active) {
+ return false;
+ }
+ let propAll = prop + '_all';
+ let propOwn = prop + '_own';
+
+ if (vm.permissions[propAll]) {
+ return true;
+ }
+
+ if (vm.permissions[propOwn] && comment.created_by.id === vm.current_user_id) {
+ return true;
+ }
+
+ return false;
+ };
+
+ vm.canComment = function () {
+ return vm.permissions.comment_create;
+ };
+
+ // check if there are is any direct linking
+ let linkedCommentId = $location.search().cm;
+
+ $timeout(function() {
+ $http.get(window.baseUrl(`/ajax/page/${$scope.pageId}/comments/`)).then(resp => {
+ if (!isCommentOpSuccess(resp)) {
+ // just show that no comments are available.
+ vm.totalComments = 0;
+ setTotalCommentMsg();
+ return;
+ }
+ vm.comments = resp.data.comments;
+ vm.totalComments = +resp.data.total;
+ vm.permissions = resp.data.permissions;
+ vm.current_user_id = resp.data.user_id;
+ setTotalCommentMsg();
+ if (!linkedCommentId) {
+ return;
+ }
+ $timeout(function() {
+ // wait for the UI to render.
+ focusLinkedComment(linkedCommentId);
+ });
+ }, checkError);
+ });
+
+ function setTotalCommentMsg () {
+ if (vm.totalComments === 0) {
+ vm.totalCommentsStr = trans('entities.no_comments');
+ } else if (vm.totalComments === 1) {
+ vm.totalCommentsStr = trans('entities.one_comment');
+ } else {
+ vm.totalCommentsStr = trans('entities.x_comments', {
+ numComments: vm.totalComments
+ });
+ }
+ }
+
+ function focusLinkedComment(linkedCommentId) {
+ let comment = angular.element('#' + linkedCommentId);
+ if (comment.length === 0) {
+ return;
+ }
+
+ window.setupPageShow.goToText(linkedCommentId);
+ }
+
+ function checkError(response) {
+ let msg = null;
+ if (isCommentOpSuccess(response)) {
+ // all good
+ return;
+ } else if (response.data) {
+ msg = response.data.message;
+ } else {
+ msg = trans('errors.comment_list');
+ }
+ if (msg) {
+ events.emit('success', msg);
+ }
+ }
+ }]);
+
+ function updateComment(comment, resp, $timeout, isDelete) {
+ comment.text = resp.comment.text;
+ comment.updated = resp.comment.updated;
+ comment.updated_by = resp.comment.updated_by;
+ comment.active = resp.comment.active;
+ if (isDelete && !resp.comment.active) {
+ comment.html = trans('entities.comment_deleted');
+ } else {
+ comment.html = resp.comment.html;
+ }
+ if (!$timeout) {
+ return;
+ }
+ comment.is_hidden = true;
+ $timeout(function() {
+ comment.is_hidden = false;
+ });
+ }
+
+ function isCommentOpSuccess(resp) {
+ if (resp && resp.data && resp.data.status === 'success') {
+ return true;
+ }
+ return false;
+ }
};
diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js
index 8d7d89cee..d8745462d 100644
--- a/resources/assets/js/directives.js
+++ b/resources/assets/js/directives.js
@@ -863,4 +863,128 @@ module.exports = function (ngApp, events) {
}
};
}]);
+
+ ngApp.directive('commentReply', [function () {
+ return {
+ restrict: 'E',
+ templateUrl: 'comment-reply.html',
+ scope: {
+ pageId: '=',
+ parentId: '=',
+ parent: '='
+ },
+ link: function (scope, element) {
+ scope.isReply = true;
+ element.find('textarea').focus();
+ scope.$on('evt.comment-success', function (event) {
+ // no need for the event to do anything more.
+ event.stopPropagation();
+ event.preventDefault();
+ scope.closeBox();
+ });
+
+ scope.closeBox = function () {
+ element.remove();
+ scope.$destroy();
+ };
+ }
+ };
+ }]);
+
+ ngApp.directive('commentEdit', [function () {
+ return {
+ restrict: 'E',
+ templateUrl: 'comment-reply.html',
+ scope: {
+ comment: '='
+ },
+ link: function (scope, element) {
+ scope.isEdit = true;
+ element.find('textarea').focus();
+ scope.$on('evt.comment-success', function (event, commentId) {
+ // no need for the event to do anything more.
+ event.stopPropagation();
+ event.preventDefault();
+ if (commentId === scope.comment.id && !scope.isNew) {
+ scope.closeBox();
+ }
+ });
+
+ scope.closeBox = function () {
+ element.remove();
+ scope.$destroy();
+ };
+ }
+ };
+ }]);
+
+
+ ngApp.directive('commentReplyLink', ['$document', '$compile', function ($document, $compile) {
+ return {
+ scope: {
+ comment: '='
+ },
+ link: function (scope, element, attr) {
+ element.on('$destroy', function () {
+ element.off('click');
+ scope.$destroy();
+ });
+
+ element.on('click', function (e) {
+ e.preventDefault();
+ var $container = element.parents('.comment-actions').first();
+ if (!$container.length) {
+ console.error('commentReplyLink directive should be placed inside a container with class comment-box!');
+ return;
+ }
+ if (attr.noCommentReplyDupe) {
+ removeDupe();
+ }
+
+ compileHtml($container, scope, attr.isReply === 'true');
+ });
+ }
+ };
+
+ function compileHtml($container, scope, isReply) {
+ let lnkFunc = null;
+ if (isReply) {
+ lnkFunc = $compile('');
+ } else {
+ lnkFunc = $compile('');
+ }
+ var compiledHTML = lnkFunc(scope);
+ $container.append(compiledHTML);
+ }
+
+ function removeDupe() {
+ let $existingElement = $document.find('.comments-list comment-reply, .comments-list comment-edit');
+ if (!$existingElement.length) {
+ return;
+ }
+
+ $existingElement.remove();
+ }
+ }]);
+
+ ngApp.directive('commentDeleteLink', ['$window', function ($window) {
+ return {
+ controller: 'CommentDeleteController',
+ scope: {
+ comment: '='
+ },
+ link: function (scope, element, attr, ctrl) {
+
+ element.on('click', function(e) {
+ e.preventDefault();
+ var resp = $window.confirm(trans('entities.comment_delete_confirm'));
+ if (!resp) {
+ return;
+ }
+
+ ctrl.delete(scope.comment);
+ });
+ }
+ };
+ }]);
};
diff --git a/resources/assets/js/pages/page-show.js b/resources/assets/js/pages/page-show.js
index 67d339d63..020229d2f 100644
--- a/resources/assets/js/pages/page-show.js
+++ b/resources/assets/js/pages/page-show.js
@@ -161,6 +161,8 @@ let setupPageShow = window.setupPageShow = function (pageId) {
}
});
+ // in order to call from other places.
+ window.setupPageShow.goToText = goToText;
};
module.exports = setupPageShow;
\ No newline at end of file
diff --git a/resources/assets/sass/_comments.scss b/resources/assets/sass/_comments.scss
new file mode 100644
index 000000000..5da53a14d
--- /dev/null
+++ b/resources/assets/sass/_comments.scss
@@ -0,0 +1,82 @@
+.comments-list {
+ .comment-box {
+ border-bottom: 1px solid $comment-border;
+ }
+
+ .comment-box:last-child {
+ border-bottom: 0px;
+ }
+}
+.page-comment {
+ .comment-container {
+ margin-left: 42px;
+ }
+
+ .comment-actions {
+ font-size: 0.8em;
+ padding-bottom: 2px;
+
+ ul {
+ padding-left: 0px;
+ margin-bottom: 2px;
+ }
+ li {
+ float: left;
+ list-style-type: none;
+ }
+
+ li:after {
+ content: '•';
+ color: #707070;
+ padding: 0 5px;
+ font-size: 1em;
+ }
+
+ li:last-child:after {
+ content: none;
+ }
+ }
+
+ .comment-actions {
+ border-bottom: 1px solid #DDD;
+ }
+
+ .comment-actions:last-child {
+ border-bottom: 0px;
+ }
+
+ .comment-header {
+ font-size: 1.25em;
+ margin-top: 0.6em;
+ }
+
+ .comment-body p {
+ margin-bottom: 1em;
+ }
+
+ .comment-inactive {
+ font-style: italic;
+ font-size: 0.85em;
+ padding-top: 5px;
+ }
+
+ .user-image {
+ float: left;
+ margin-right: 10px;
+ width: 32px;
+ img {
+ width: 100%;
+ }
+ }
+}
+
+.comment-editor {
+ margin-top: 2em;
+
+ textarea {
+ display: block;
+ width: 100%;
+ max-width: 100%;
+ min-height: 120px;
+ }
+}
diff --git a/resources/assets/sass/_pages.scss b/resources/assets/sass/_pages.scss
index e5334c69c..b06892c1d 100755
--- a/resources/assets/sass/_pages.scss
+++ b/resources/assets/sass/_pages.scss
@@ -310,4 +310,8 @@
background-color: #EEE;
}
}
+}
+
+.comment-editor .CodeMirror, .comment-editor .CodeMirror-scroll {
+ min-height: 175px;
}
\ No newline at end of file
diff --git a/resources/assets/sass/_variables.scss b/resources/assets/sass/_variables.scss
index 23bf2b219..3e864aaa4 100644
--- a/resources/assets/sass/_variables.scss
+++ b/resources/assets/sass/_variables.scss
@@ -56,3 +56,6 @@ $text-light: #EEE;
$bs-light: 0 0 4px 1px #CCC;
$bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
$bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
+
+// comments
+$comment-border: #DDD;
\ No newline at end of file
diff --git a/resources/assets/sass/export-styles.scss b/resources/assets/sass/export-styles.scss
index 60450f3e2..72b5b16b5 100644
--- a/resources/assets/sass/export-styles.scss
+++ b/resources/assets/sass/export-styles.scss
@@ -10,6 +10,7 @@
@import "header";
@import "lists";
@import "pages";
+@import "comments";
table {
border-spacing: 0;
diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss
index afb9d531b..3b279b8bd 100644
--- a/resources/assets/sass/styles.scss
+++ b/resources/assets/sass/styles.scss
@@ -16,6 +16,7 @@
@import "header";
@import "lists";
@import "pages";
+@import "comments";
[v-cloak], [v-show] {
display: none; opacity: 0;
diff --git a/resources/lang/de/entities.php b/resources/lang/de/entities.php
index c9feb8497..5d7d5cdde 100644
--- a/resources/lang/de/entities.php
+++ b/resources/lang/de/entities.php
@@ -213,4 +213,27 @@ return [
'profile_not_created_pages' => ':userName hat bisher keine Seiten angelegt.',
'profile_not_created_chapters' => ':userName hat bisher keine Kapitel angelegt.',
'profile_not_created_books' => ':userName hat bisher keine Bücher angelegt.',
+
+ /**
+ * Comnents
+ */
+ 'comment' => 'Kommentar',
+ 'comments' => 'Kommentare',
+ 'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein, Markdown unterstützt ...',
+ 'no_comments' => 'Keine Kommentare',
+ 'x_comments' => ':numComments Kommentare',
+ 'one_comment' => '1 Kommentar',
+ 'comments_loading' => 'Laden ...',
+ 'comment_save' => 'Kommentar speichern',
+ 'comment_reply' => 'Antworten',
+ 'comment_edit' => 'Bearbeiten',
+ 'comment_delete' => 'Löschen',
+ 'comment_cancel' => 'Abbrechen',
+ 'comment_created' => 'Kommentar hinzugefügt',
+ 'comment_updated' => 'Kommentar aktualisiert',
+ 'comment_deleted' => 'Kommentar gelöscht',
+ 'comment_updated_text' => 'Aktualisiert vor :updateDiff von',
+ 'comment_delete_confirm' => 'Damit wird der Inhalt des Kommentars entfernt. Bist du sicher, dass du diesen Kommentar löschen möchtest?',
+ 'comment_create' => 'Erstellt'
+
];
\ No newline at end of file
diff --git a/resources/lang/de/errors.php b/resources/lang/de/errors.php
index e085d9915..ff045d628 100644
--- a/resources/lang/de/errors.php
+++ b/resources/lang/de/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Es ist ein Fehler aufgetreten',
'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.',
+
+ // Comments
+ 'comment_list' => 'Beim Abrufen der Kommentare ist ein Fehler aufgetreten.',
+ 'cannot_add_comment_to_draft' => 'Du kannst keine Kommentare zu einem Entwurf hinzufügen.',
+ 'comment_add' => 'Beim Hinzufügen des Kommentars ist ein Fehler aufgetreten.',
+ 'comment_delete' => 'Beim Löschen des Kommentars ist ein Fehler aufgetreten.',
+ 'empty_comment' => 'Kann keinen leeren Kommentar hinzufügen',
];
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index 450f4ce48..43053df10 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -234,4 +234,27 @@ return [
'profile_not_created_pages' => ':userName has not created any pages',
'profile_not_created_chapters' => ':userName has not created any chapters',
'profile_not_created_books' => ':userName has not created any books',
+
+ /**
+ * Comments
+ */
+ 'comment' => 'Comment',
+ 'comments' => 'Comments',
+ 'comment_placeholder' => 'Enter your comments here, markdown supported...',
+ 'no_comments' => 'No Comments',
+ 'x_comments' => ':numComments Comments',
+ 'one_comment' => '1 Comment',
+ 'comments_loading' => 'Loading...',
+ 'comment_save' => 'Save Comment',
+ 'comment_reply' => 'Reply',
+ 'comment_edit' => 'Edit',
+ 'comment_delete' => 'Delete',
+ 'comment_cancel' => 'Cancel',
+ 'comment_created' => 'Comment added',
+ 'comment_updated' => 'Comment updated',
+ 'comment_deleted' => 'Comment deleted',
+ 'comment_updated_text' => 'Updated :updateDiff by',
+ 'comment_delete_confirm' => 'This will remove the contents of the comment. Are you sure you want to delete this comment?',
+ 'comment_create' => 'Created'
+
];
\ No newline at end of file
diff --git a/resources/lang/en/errors.php b/resources/lang/en/errors.php
index c4578a37a..71bcd1f9a 100644
--- a/resources/lang/en/errors.php
+++ b/resources/lang/en/errors.php
@@ -60,6 +60,13 @@ return [
'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+ // Comments
+ 'comment_list' => 'An error occurred while fetching the comments.',
+ 'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
+ 'comment_add' => 'An error occurred while adding the comment.',
+ 'comment_delete' => 'An error occurred while deleting the comment.',
+ 'empty_comment' => 'Cannot add an empty comment.',
+
// Error pages
'404_page_not_found' => 'Page Not Found',
'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
diff --git a/resources/lang/es/entities.php b/resources/lang/es/entities.php
index d6b2810bc..2ca55a786 100644
--- a/resources/lang/es/entities.php
+++ b/resources/lang/es/entities.php
@@ -214,4 +214,26 @@ return [
'profile_not_created_pages' => ':userName no ha creado ninguna página',
'profile_not_created_chapters' => ':userName no ha creado ningún capítulo',
'profile_not_created_books' => ':userName no ha creado ningún libro',
+
+ /**
+ * Comments
+ */
+ 'comment' => 'Comentario',
+ 'comments' => 'Comentarios',
+ 'comment_placeholder' => 'Introduzca sus comentarios aquí, markdown supported ...',
+ 'no_comments' => 'No hay comentarios',
+ 'x_comments' => ':numComments Comentarios',
+ 'one_comment' => '1 Comentario',
+ 'comments_loading' => 'Cargando ...',
+ 'comment_save' => 'Guardar comentario',
+ 'comment_reply' => 'Responder',
+ 'comment_edit' => 'Editar',
+ 'comment_delete' => 'Eliminar',
+ 'comment_cancel' => 'Cancelar',
+ 'comment_created' => 'Comentario añadido',
+ 'comment_updated' => 'Comentario actualizado',
+ 'comment_deleted' => 'Comentario eliminado',
+ 'comment_updated_text' => 'Actualizado hace :updateDiff por',
+ 'comment_delete_confirm' => 'Esto eliminará el contenido del comentario. ¿Estás seguro de que quieres eliminar este comentario?',
+ 'comment_create' => 'Creado'
];
diff --git a/resources/lang/es/errors.php b/resources/lang/es/errors.php
index 1e39a3cb8..e488b6a1b 100644
--- a/resources/lang/es/errors.php
+++ b/resources/lang/es/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Ha ocurrido un error',
'app_down' => 'La aplicación :appName se encuentra caída en este momento',
'back_soon' => 'Volverá a estar operativa en corto tiempo.',
+
+ // Comments
+ 'comment_list' => 'Se ha producido un error al buscar los comentarios.',
+ 'cannot_add_comment_to_draft' => 'No puedes añadir comentarios a un borrador.',
+ 'comment_add' => 'Se ha producido un error al añadir el comentario.',
+ 'comment_delete' => 'Se ha producido un error al eliminar el comentario.',
+ 'empty_comment' => 'No se puede agregar un comentario vacío.',
];
diff --git a/resources/lang/fr/entities.php b/resources/lang/fr/entities.php
index 17b4ea913..0d89993e9 100644
--- a/resources/lang/fr/entities.php
+++ b/resources/lang/fr/entities.php
@@ -213,4 +213,26 @@ return [
'profile_not_created_pages' => ':userName n\'a pas créé de page',
'profile_not_created_chapters' => ':userName n\'a pas créé de chapitre',
'profile_not_created_books' => ':userName n\'a pas créé de livre',
+
+ /**
+ * Comments
+ */
+ 'comment' => 'Commentaire',
+ 'comments' => 'Commentaires',
+ 'comment_placeholder' => 'Entrez vos commentaires ici, merci supporté ...',
+ 'no_comments' => 'No Comments',
+ 'x_comments' => ':numComments Commentaires',
+ 'one_comment' => '1 Commentaire',
+ 'comments_loading' => 'Loading ...',
+ 'comment_save' => 'Enregistrer le commentaire',
+ 'comment_reply' => 'Répondre',
+ 'comment_edit' => 'Modifier',
+ 'comment_delete' => 'Supprimer',
+ 'comment_cancel' => 'Annuler',
+ 'comment_created' => 'Commentaire ajouté',
+ 'comment_updated' => 'Commentaire mis à jour',
+ 'comment_deleted' => 'Commentaire supprimé',
+ 'comment_updated_text' => 'Mis à jour il y a :updateDiff par',
+ 'comment_delete_confirm' => 'Cela supprime le contenu du commentaire. Êtes-vous sûr de vouloir supprimer ce commentaire?',
+ 'comment_create' => 'Créé'
];
diff --git a/resources/lang/fr/errors.php b/resources/lang/fr/errors.php
index 4197b1708..9e20147b6 100644
--- a/resources/lang/fr/errors.php
+++ b/resources/lang/fr/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Une erreur est survenue',
'app_down' => ':appName n\'est pas en service pour le moment',
'back_soon' => 'Nous serons bientôt de retour.',
+
+ // comments
+ 'comment_list' => 'Une erreur s\'est produite lors de la récupération des commentaires.',
+ 'cannot_add_comment_to_draft' => 'Vous ne pouvez pas ajouter de commentaires à un projet.',
+ 'comment_add' => 'Une erreur s\'est produite lors de l\'ajout du commentaire.',
+ 'comment_delete' => 'Une erreur s\'est produite lors de la suppression du commentaire.',
+ 'empty_comment' => 'Impossible d\'ajouter un commentaire vide.',
];
diff --git a/resources/lang/nl/entities.php b/resources/lang/nl/entities.php
index d6975e130..6df9e5dd9 100644
--- a/resources/lang/nl/entities.php
+++ b/resources/lang/nl/entities.php
@@ -214,4 +214,26 @@ return [
'profile_not_created_pages' => ':userName heeft geen pagina\'s gemaakt',
'profile_not_created_chapters' => ':userName heeft geen hoofdstukken gemaakt',
'profile_not_created_books' => ':userName heeft geen boeken gemaakt',
+
+ /**
+ * Comments
+ */
+ 'comment' => 'Commentaar',
+ 'comments' => 'Commentaren',
+ 'comment_placeholder' => 'Vul hier uw reacties in, markdown ondersteund ...',
+ 'no_comments' => 'No Comments',
+ 'x_comments' => ':numComments Opmerkingen',
+ 'one_comment' => '1 commentaar',
+ 'comments_loading' => 'Loading ...',
+ 'comment_save' => 'Opslaan opslaan',
+ 'comment_reply' => 'Antwoord',
+ 'comment_edit' => 'Bewerken',
+ 'comment_delete' => 'Verwijderen',
+ 'comment_cancel' => 'Annuleren',
+ 'comment_created' => 'Opmerking toegevoegd',
+ 'comment_updated' => 'Opmerking bijgewerkt',
+ 'comment_deleted' => 'Opmerking verwijderd',
+ 'comment_updated_text' => 'Bijgewerkt :updateDiff geleden door',
+ 'comment_delete_confirm' => 'Hiermee verwijdert u de inhoud van de reactie. Weet u zeker dat u deze reactie wilt verwijderen?',
+ 'comment_create' => 'Gemaakt'
];
\ No newline at end of file
diff --git a/resources/lang/nl/errors.php b/resources/lang/nl/errors.php
index f8b635bce..b8fab59fd 100644
--- a/resources/lang/nl/errors.php
+++ b/resources/lang/nl/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Er Ging Iets Fout',
'app_down' => ':appName is nu niet beschikbaar',
'back_soon' => 'Komt snel weer online.',
+
+ // Comments
+ 'comment_list' => 'Er is een fout opgetreden tijdens het ophalen van de reacties.',
+ 'cannot_add_comment_to_draft' => 'U kunt geen reacties toevoegen aan een ontwerp.',
+ 'comment_add' => 'Er is een fout opgetreden tijdens het toevoegen van de reactie.',
+ 'comment_delete' => 'Er is een fout opgetreden tijdens het verwijderen van de reactie.',
+ 'empty_comment' => 'Kan geen lege reactie toevoegen.',
];
\ No newline at end of file
diff --git a/resources/lang/pt_BR/entities.php b/resources/lang/pt_BR/entities.php
index 5a965fe62..e6b900fdd 100644
--- a/resources/lang/pt_BR/entities.php
+++ b/resources/lang/pt_BR/entities.php
@@ -214,4 +214,26 @@ return [
'profile_not_created_pages' => ':userName não criou páginas',
'profile_not_created_chapters' => ':userName não criou capítulos',
'profile_not_created_books' => ':userName não criou livros',
+
+ /**
+ * Comments
+ */
+ 'comentário' => 'Comentário',
+ 'comentários' => 'Comentários',
+ 'comment_placeholder' => 'Digite seus comentários aqui, markdown suportado ...',
+ 'no_comments' => 'No Comments',
+ 'x_comments' => ':numComments Comentários',
+ 'one_comment' => '1 comentário',
+ 'comments_loading' => 'Carregando ....',
+ 'comment_save' => 'Salvar comentário',
+ 'comment_reply' => 'Responder',
+ 'comment_edit' => 'Editar',
+ 'comment_delete' => 'Excluir',
+ 'comment_cancel' => 'Cancelar',
+ 'comment_created' => 'Comentário adicionado',
+ 'comment_updated' => 'Comentário atualizado',
+ 'comment_deleted' => 'Comentário eliminado',
+ 'comment_updated_text' => 'Atualizado :updatedDiff atrás por',
+ 'comment_delete_confirm' => 'Isso removerá o conteúdo do comentário. Tem certeza de que deseja excluir esse comentário?',
+ 'comment_create' => 'Criada'
];
\ No newline at end of file
diff --git a/resources/lang/pt_BR/errors.php b/resources/lang/pt_BR/errors.php
index 91b85e3ef..16fc78ff5 100644
--- a/resources/lang/pt_BR/errors.php
+++ b/resources/lang/pt_BR/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Um erro ocorreu',
'app_down' => ':appName está fora do ar no momento',
'back_soon' => 'Voltaremos em seguida.',
+
+ // comments
+ 'comment_list' => 'Ocorreu um erro ao buscar os comentários.',
+ 'cannot_add_comment_to_draft' => 'Você não pode adicionar comentários a um rascunho.',
+ 'comment_add' => 'Ocorreu um erro ao adicionar o comentário.',
+ 'comment_delete' => 'Ocorreu um erro ao excluir o comentário.',
+ 'empty_comment' => 'Não é possível adicionar um comentário vazio.',
];
\ No newline at end of file
diff --git a/resources/lang/sk/entities.php b/resources/lang/sk/entities.php
index e70864753..7c8f34368 100644
--- a/resources/lang/sk/entities.php
+++ b/resources/lang/sk/entities.php
@@ -223,4 +223,26 @@ return [
'profile_not_created_pages' => ':userName nevytvoril žiadne stránky',
'profile_not_created_chapters' => ':userName nevytvoril žiadne kapitoly',
'profile_not_created_books' => ':userName nevytvoril žiadne knihy',
+
+ /**
+ * Comments
+ */
+ 'comment' => 'Komentár',
+ 'comments' => 'Komentáre',
+ 'comment_placeholder' => 'Tu zadajte svoje pripomienky, podporované označenie ...',
+ 'no_comments' => 'No Comments',
+ 'x_comments' => ':numComments komentárov',
+ 'one_comment' => '1 komentár',
+ 'comments_loading' => 'Loading ..',
+ 'comment_save' => 'Uložiť komentár',
+ 'comment_reply' => 'Odpovedať',
+ 'comment_edit' => 'Upraviť',
+ 'comment_delete' => 'Odstrániť',
+ 'comment_cancel' => 'Zrušiť',
+ 'comment_created' => 'Pridaný komentár',
+ 'comment_updated' => 'Komentár aktualizovaný',
+ 'comment_deleted' => 'Komentár bol odstránený',
+ 'comment_updated_text' => 'Aktualizované pred :updateDiff',
+ 'comment_delete_confirm' => 'Tým sa odstráni obsah komentára. Naozaj chcete odstrániť tento komentár?',
+ 'comment_create' => 'Vytvorené'
];
diff --git a/resources/lang/sk/errors.php b/resources/lang/sk/errors.php
index e3420852a..d4c7b7a3a 100644
--- a/resources/lang/sk/errors.php
+++ b/resources/lang/sk/errors.php
@@ -67,4 +67,11 @@ return [
'error_occurred' => 'Nastala chyba',
'app_down' => ':appName je momentálne nedostupná',
'back_soon' => 'Čoskoro bude opäť dostupná.',
+
+ // comments
+ 'comment_list' => 'Pri načítaní komentárov sa vyskytla chyba',
+ 'cannot_add_comment_to_draft' => 'Do konceptu nemôžete pridávať komentáre.',
+ 'comment_add' => 'Počas pridávania komentára sa vyskytla chyba',
+ 'comment_delete' => 'Pri odstraňovaní komentára došlo k chybe',
+ 'empty_comment' => 'Nelze pridať prázdny komentár.',
];
diff --git a/resources/views/comments/comment-reply.blade.php b/resources/views/comments/comment-reply.blade.php
new file mode 100644
index 000000000..02535341c
--- /dev/null
+++ b/resources/views/comments/comment-reply.blade.php
@@ -0,0 +1,12 @@
+
+
+
+
+@if($errors->has('markdown'))
+
{{ $errors->first('markdown') }}
+@endif
\ No newline at end of file
diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php
new file mode 100644
index 000000000..ffa75cfed
--- /dev/null
+++ b/resources/views/comments/comments.blade.php
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/resources/views/comments/list-item.blade.php b/resources/views/comments/list-item.blade.php
new file mode 100644
index 000000000..f274d2ed2
--- /dev/null
+++ b/resources/views/comments/list-item.blade.php
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php
index 221ed4476..0d75a534a 100644
--- a/resources/views/pages/show.blade.php
+++ b/resources/views/pages/show.blade.php
@@ -46,13 +46,13 @@
-