From 9702366d1100fd9e80e418015bc6b2d3ea9b0cc7 Mon Sep 17 00:00:00 2001 From: Andy Heathershaw Date: Tue, 18 Sep 2018 14:28:59 +0100 Subject: [PATCH] #4: It's now possible to reply to a comment in threaded comments. Also started implementing validation. --- .../Gallery/PhotoCommentController.php | 58 ++++++++++++++++++- .../Requests/StorePhotoCommentRequest.php | 32 ++++++++++ app/Photo.php | 18 +++++- resources/assets/js/gallery.js | 34 ++++++++++- resources/lang/en/forms.php | 1 + .../views/themes/base/gallery/photo.blade.php | 8 ++- .../base/partials/photo_comments.blade.php | 7 +-- .../photo_comments_reply_form.blade.php | 45 ++++++++++++-- .../partials/photo_single_comment.blade.php | 6 +- routes/web.php | 3 + 10 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 app/Http/Requests/StorePhotoCommentRequest.php diff --git a/app/Http/Controllers/Gallery/PhotoCommentController.php b/app/Http/Controllers/Gallery/PhotoCommentController.php index c4b0804..be02f06 100644 --- a/app/Http/Controllers/Gallery/PhotoCommentController.php +++ b/app/Http/Controllers/Gallery/PhotoCommentController.php @@ -2,9 +2,11 @@ namespace App\Http\Controllers\Gallery; +use App\Facade\Theme; use App\Facade\UserConfig; use App\Helpers\DbHelper; use App\Http\Controllers\Controller; +use App\Http\Requests\StorePhotoCommentRequest; use App\PhotoComment; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; @@ -12,7 +14,38 @@ use Illuminate\Support\Facades\Auth; class PhotoCommentController extends Controller { - public function store(Request $request, $albumUrlAlias, $photoFilename) + public function reply(Request $request, $albumUrlAlias, $photoFilename, $commentID) + { + $album = DbHelper::getAlbumByPath($albumUrlAlias); + if (is_null($album)) + { + App::abort(404); + return null; + } + + $this->authorizeForUser($this->getUser(), 'view', $album); + + $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename); + + if (!UserConfig::get('allow_photo_comments')) + { + // Not allowed to post comments + App::abort(404); + } + + $comment = $photo->comments()->where('id', $commentID)->first(); + if (is_null($comment)) + { + App::abort(404); + } + + return Theme::render('partials.photo_comments_reply_form', [ + 'photo' => $photo, + 'reply_comment' => $comment + ]); + } + + public function store(StorePhotoCommentRequest $request, $albumUrlAlias, $photoFilename) { $album = DbHelper::getAlbumByPath($albumUrlAlias); if (is_null($album)) @@ -35,6 +68,20 @@ class PhotoCommentController extends Controller $comment->photo_id = $photo->id; $comment->fill($request->only(['commentor_email', 'commentor_name', 'comment_text'])); + // Validate and link the parent comment, if provided + if ($request->has('parent_comment_id')) + { + $parentComment = $photo->comments()->where('id', intval($request->get('parent_comment_id')))->first(); + + if (is_null($parentComment)) + { + $request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully')); + return redirect($photo->url()); + } + + $comment->parent_comment_id = $parentComment->id; + } + $user = $this->getUser(); if (!is_null($user) && !$user->isAnonymous()) { @@ -45,6 +92,13 @@ class PhotoCommentController extends Controller $request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully')); - return redirect($photo->url()); + if ($request->isXmlHttpRequest()) + { + return response()->json(['redirect_url' => $photo->url()]); + } + else + { + return redirect($photo->url()); + } } } \ No newline at end of file diff --git a/app/Http/Requests/StorePhotoCommentRequest.php b/app/Http/Requests/StorePhotoCommentRequest.php new file mode 100644 index 0000000..d131c21 --- /dev/null +++ b/app/Http/Requests/StorePhotoCommentRequest.php @@ -0,0 +1,32 @@ + 'required|max:255', + 'commentor_email' => 'sometimes|max:255|email', + 'comment_text' => 'required' + ]; + } +} \ No newline at end of file diff --git a/app/Photo.php b/app/Photo.php index 37e90a6..8b4da40 100644 --- a/app/Photo.php +++ b/app/Photo.php @@ -54,7 +54,14 @@ class Photo extends Model public function approvedComments() { - return $this->hasMany(PhotoComment::class)->whereNotNull('approved_at'); + return $this->hasMany(PhotoComment::class) + ->whereNull('parent_comment_id') + ->whereNotNull('approved_at'); + } + + public function comments() + { + return $this->hasMany(PhotoComment::class); } public function exifUrl() @@ -90,6 +97,15 @@ class Photo extends Model ]); } + public function replyToCommentFormUrl($commentID = -1) + { + return route('replyPhotoComment', [ + 'albumUrlAlias' => $this->album->url_path, + 'photoFilename' => $this->storage_file_name, + 'commentID' => $commentID + ]); + } + public function thumbnailUrl($thumbnailName = null, $cacheBust = true) { $url = $this->album->getAlbumSource()->getUrlToPhoto($this, $thumbnailName); diff --git a/resources/assets/js/gallery.js b/resources/assets/js/gallery.js index 5ce3145..5355647 100644 --- a/resources/assets/js/gallery.js +++ b/resources/assets/js/gallery.js @@ -2,7 +2,7 @@ * This model is used by gallery/photo.blade.php, to handle comments and individual photo actions. * @constructor */ -function PhotoViewModel() { +function PhotoViewModel(urls) { this.el = '#photo-app'; this.data = { @@ -10,7 +10,39 @@ function PhotoViewModel() { reply_comment_id: 0 }; + this.computed = { + replyFormStyle: function() + { + return { + 'display': this.is_reply_form_loading ? 'none' : 'block' + }; + } + }; + this.methods = { + init: function() { + var self = this; + + // Load the right comment reply form + $('#comment-reply-modal').on('show.bs.modal', function (event) { + var url = urls.reply_comment_form.replace(/-1$/, self.reply_comment_id); + $.get(url, function(result) + { + $('#comment-reply-form-content').html(result); + self.is_reply_form_loading = false; + }); + }); + }, + postCommentReply: function() { + var form = $('form', '#comment-reply-form-content'); + var formUrl = $(form).attr('action'); + + var formData = form.serialize(); + $.post(formUrl, formData, function(result) + { + window.location = result.redirect_url; + }); + }, replyToComment: function(e) { var replyButton = $(e.target).closest('.photo-comment'); this.reply_comment_id = replyButton.data('comment-id'); diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index 0b45424..9961921 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -36,6 +36,7 @@ return [ 'parent_album_placeholder' => 'None (top-level album)', 'password_label' => 'Password:', 'password_confirm_label' => 'Confirm password:', + 'photo_comment_reply_action' => 'Post reply', 'photo_comment_submit_action' => 'Post comment', 'photo_comment_text_label' => 'Your message/comments:', 'please_select' => '- Select an Option -', diff --git a/resources/views/themes/base/gallery/photo.blade.php b/resources/views/themes/base/gallery/photo.blade.php index af75a89..a45f19b 100644 --- a/resources/views/themes/base/gallery/photo.blade.php +++ b/resources/views/themes/base/gallery/photo.blade.php @@ -161,10 +161,14 @@ @push('scripts') @endpush \ No newline at end of file diff --git a/resources/views/themes/base/partials/photo_comments.blade.php b/resources/views/themes/base/partials/photo_comments.blade.php index 9b6c9a2..b70c4b9 100644 --- a/resources/views/themes/base/partials/photo_comments.blade.php +++ b/resources/views/themes/base/partials/photo_comments.blade.php @@ -31,10 +31,9 @@

@lang('global.please_wait')

@lang('global.please_wait')

- - diff --git a/resources/views/themes/base/partials/photo_comments_reply_form.blade.php b/resources/views/themes/base/partials/photo_comments_reply_form.blade.php index 37bd616..1680e8e 100644 --- a/resources/views/themes/base/partials/photo_comments_reply_form.blade.php +++ b/resources/views/themes/base/partials/photo_comments_reply_form.blade.php @@ -1,22 +1,59 @@ +@php +$is_reply = isset($reply_comment); +@endphp + +{{-- Show a previous of the comment we're replying to --}} +@if ($is_reply) + +@endif +
{{ csrf_field() }} + @if ($is_reply) + + @endif +
- + + + @if ($errors->has('commentor_name')) +
+ {{ $errors->first('commentor_name') }} +
+ @endif
- + + + @if ($errors->has('commentor_email')) +
+ {{ $errors->first('commentor_email') }} +
+ @endif
- + + + @if ($errors->has('comment_text')) +
+ {{ $errors->first('comment_text') }} +
+ @endif
- + @if ($is_reply) + + @else + + @endif
\ No newline at end of file diff --git a/resources/views/themes/base/partials/photo_single_comment.blade.php b/resources/views/themes/base/partials/photo_single_comment.blade.php index f4e4a6d..72b8b8a 100644 --- a/resources/views/themes/base/partials/photo_single_comment.blade.php +++ b/resources/views/themes/base/partials/photo_single_comment.blade.php @@ -1,8 +1,12 @@ +@php + $is_reply = (isset($is_reply) && $is_reply); +@endphp +
  • Comment by {{ $comment->createdBy->name }}:
    {{ $comment->comment_text }} - @if ($comment->depth() < UserConfig::get('photo_comments_thread_depth')) + @if (!$is_reply && $comment->depth() < UserConfig::get('photo_comments_thread_depth'))

    @endif
  • \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 12888f4..e18556c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -109,6 +109,9 @@ Route::get('exif/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show Route::post('p/{albumUrlAlias}/{photoFilename}/comments', 'Gallery\PhotoCommentController@store') ->name('postPhotoComment') ->where('albumUrlAlias', '.*'); +Route::get('p/{albumUrlAlias}/{photoFilename}/comments/reply/{commentID}', 'Gallery\PhotoCommentController@reply') + ->name('replyPhotoComment') + ->where('albumUrlAlias', '.*'); Route::get('p/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show') ->name('viewPhoto') ->where('albumUrlAlias', '.*');