#4: It's now possible to reply to a comment in threaded comments. Also started implementing validation.

This commit is contained in:
Andy Heathershaw 2018-09-18 14:28:59 +01:00
parent c9ab590afe
commit 9702366d11
10 changed files with 197 additions and 15 deletions

View File

@ -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());
}
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePhotoCommentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'commentor_name' => 'required|max:255',
'commentor_email' => 'sometimes|max:255|email',
'comment_text' => 'required'
];
}
}

View File

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

View File

@ -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');

View File

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

View File

@ -161,10 +161,14 @@
@push('scripts')
<script type="text/javascript">
var photoViewModel = new PhotoViewModel();
var app = null;
var photoViewModel = new PhotoViewModel({
reply_comment_form: '{{ $photo->replyToCommentFormUrl() }}'
});
$(document).ready(function() {
var app = new Vue(photoViewModel);
app = new Vue(photoViewModel);
app.init();
});
</script>
@endpush

View File

@ -31,10 +31,9 @@
<p class="m-2"><img src="{{ asset('ripple.svg') }}" alt="@lang('global.please_wait')" title="@lang('global.please_wait')"></p>
<p>@lang('global.please_wait')</p>
</div>
</div>
<div class="modal-footer" v-if="!is_reply_form_loading">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
<div id="comment-reply-form-content" v-bind:style="replyFormStyle">
</div>
</div>
</div>
</div>

View File

@ -1,22 +1,59 @@
@php
$is_reply = isset($reply_comment);
@endphp
{{-- Show a previous of the comment we're replying to --}}
@if ($is_reply)
<ul class="mb-3">
@include (Theme::viewName('partials.photo_single_comment'), ['comment' => $reply_comment, 'is_reply' => true])
</ul>
@endif
<form action="{{ $photo->postCommentUrl() }}" method="post">
{{ csrf_field() }}
@if ($is_reply)
<input type="hidden" name="parent_comment_id" value="{{ $reply_comment->id }}"/>
@endif
<div class="form-group">
<label for="commentor-name">@lang('forms.name_label')</label>
<input type="text" class="form-control" id="commentor-name" name="commentor_name"/>
<input type="text" class="form-control{{ $errors->has('commentor_name') ? ' is-invalid' : '' }}" id="commentor-name" name="commentor_name"/>
@if ($errors->has('commentor_name'))
<div class="invalid-feedback">
<strong>{{ $errors->first('commentor_name') }}</strong>
</div>
@endif
</div>
<div class="form-group">
<label for="commentor-email">@lang('forms.email_label')</label>
<input type="email" class="form-control" id="commentor-email" name="commentor_email" placeholder="@lang('forms.email_placeholder')"/>
<input type="text" class="form-control{{ $errors->has('commentor_email') ? ' is-invalid' : '' }}" id="commentor-email" name="commentor_email" placeholder="@lang('forms.email_placeholder')"/>
@if ($errors->has('commentor_email'))
<div class="invalid-feedback">
<strong>{{ $errors->first('commentor_email') }}</strong>
</div>
@endif
</div>
<div class="form-group">
<label for="comment-text">@lang('forms.photo_comment_text_label')</label>
<textarea class="form-control" id="comment-text" name="comment_text" rows="10"></textarea>
<textarea class="form-control{{ $errors->has('comment_text') ? ' is-invalid' : '' }}" id="comment-text" name="comment_text" rows="10"></textarea>
@if ($errors->has('comment_text'))
<div class="invalid-feedback">
<strong>{{ $errors->first('comment_text') }}</strong>
</div>
@endif
</div>
<div class="form-group text-right">
<button type="submit" class="btn btn-success">@lang('forms.photo_comment_submit_action')</button>
@if ($is_reply)
<button type="button" class="btn btn-success" onclick="app.postCommentReply();">@lang('forms.photo_comment_reply_action')</button>
@else
<button type="submit" class="btn btn-success">@lang('forms.photo_comment_submit_action')</button>
@endif
</div>
</form>

View File

@ -1,8 +1,12 @@
@php
$is_reply = (isset($is_reply) && $is_reply);
@endphp
<li class="photo-comment" data-comment-id="{{ $comment->id }}">
Comment by <b>{{ $comment->createdBy->name }}</b>:<br/>
{{ $comment->comment_text }}
@if ($comment->depth() < UserConfig::get('photo_comments_thread_depth'))
@if (!$is_reply && $comment->depth() < UserConfig::get('photo_comments_thread_depth'))
<p><button v-on:click="replyToComment" class="btn btn-sm btn-outline-primary">@lang('gallery.photo_comments_reply_action')</button></p>
@endif
</li>

View File

@ -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', '.*');