#4: Added a basic template for the comment design. Comments now display nested. Renamed the columns in the database table so the default validation error messages look better. Corrected a few issues with the TinyMCE implementation.

This commit is contained in:
Andy Heathershaw 2018-09-19 09:44:20 +01:00
parent 60e747bd75
commit 1802aa84d8
9 changed files with 98 additions and 35 deletions

View File

@ -74,7 +74,7 @@ class PhotoCommentController extends Controller
if (is_null($parentComment)) if (is_null($parentComment))
{ {
$request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully')); //TODO $request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully'));
return redirect($photo->url()); return redirect($photo->url());
} }
} }
@ -82,14 +82,14 @@ class PhotoCommentController extends Controller
try try
{ {
$this->validate($request, [ $this->validate($request, [
'commentor_name' => 'required|max:255', 'name' => 'required|max:255',
'commentor_email' => 'sometimes|max:255|email', 'email' => 'sometimes|max:255|email',
'comment_text' => 'required' 'comment' => 'required'
]); ]);
$comment = new PhotoComment(); $comment = new PhotoComment();
$comment->photo_id = $photo->id; $comment->photo_id = $photo->id;
$comment->fill($request->only(['commentor_email', 'commentor_name', 'comment_text'])); $comment->fill($request->only(['name', 'email', 'comment']));
if (!is_null($parentComment)) if (!is_null($parentComment))
{ {

View File

@ -12,9 +12,9 @@ class PhotoComment extends Model
* @var array * @var array
*/ */
protected $fillable = [ protected $fillable = [
'commentor_name', 'name',
'commentor_email', 'email',
'comment_text' 'comment'
]; ];
public function approvedBy() public function approvedBy()
@ -22,6 +22,21 @@ class PhotoComment extends Model
return $this->belongsTo(User::class, 'approved_user_id'); return $this->belongsTo(User::class, 'approved_user_id');
} }
public function approvedChildren()
{
return $this->children()->whereNotNull('approved_at');
}
public function authorDisplayName()
{
return is_null($this->createdBy) ? $this->name : $this->createdBy->name;
}
public function children()
{
return $this->hasMany(PhotoComment::class, 'parent_comment_id');
}
public function createdBy() public function createdBy()
{ {
return $this->belongsTo(User::class, 'created_user_id'); return $this->belongsTo(User::class, 'created_user_id');
@ -45,4 +60,17 @@ class PhotoComment extends Model
{ {
return $this->belongsTo(PhotoComment::class, 'parent_comment_id'); return $this->belongsTo(PhotoComment::class, 'parent_comment_id');
} }
public function textAsHtml()
{
$start = '<p>';
$end = '</p>';
$isHtml = (
strlen($this->comment) > (strlen($start) + strlen($end)) && // text contains both our start + end string
strtolower(substr($this->comment, 0, strlen($start))) == strtolower($start) && // text starts with our start string
strtolower(substr($this->comment, strlen($this->comment) - strlen($end))) == strtolower($end) // text ends with our end string
);
return $isHtml ? $this->comment : sprintf('<p>%s</p>', $this->comment);
}
} }

View File

@ -16,9 +16,9 @@ class CreatePhotoCommentsTable extends Migration
Schema::create('photo_comments', function (Blueprint $table) { Schema::create('photo_comments', function (Blueprint $table) {
$table->bigIncrements('id'); $table->bigIncrements('id');
$table->unsignedBigInteger('photo_id'); $table->unsignedBigInteger('photo_id');
$table->string('commentor_name'); $table->string('name');
$table->string('commentor_email'); $table->string('email');
$table->text('comment_text'); $table->text('comment');
$table->unsignedInteger('created_user_id')->nullable(true); $table->unsignedInteger('created_user_id')->nullable(true);
$table->unsignedInteger('approved_user_id')->nullable(true); $table->unsignedInteger('approved_user_id')->nullable(true);
$table->dateTime('approved_at')->nullable(true); $table->dateTime('approved_at')->nullable(true);

View File

@ -41569,12 +41569,21 @@ function PhotoViewModel(urls) {
self.is_reply_form_loading = false; self.is_reply_form_loading = false;
}); });
}); });
$('#comment-reply-modal').on('hide.bs.modal', function (event) {
tinymce.remove('#comment-text-reply');
});
}, },
postCommentReply: function() { postCommentReply: function() {
var form = $('form', '#comment-reply-form-content'); var form = $('form', '#comment-reply-form-content');
var formUrl = $(form).attr('action'); var formUrl = $(form).attr('action');
// Seems like the TinyMCE editor in the BS modal does not persist back to the textarea correctly - so do
// this manually (bit of a hack!)
$('#comment-text-reply', form).html(tinymce.get('comment-text-reply').getContent());
var formData = form.serialize(); var formData = form.serialize();
$.post(formUrl, formData, function(result) $.post(formUrl, formData, function(result)
{ {
if (result.redirect_url) if (result.redirect_url)
@ -41583,7 +41592,9 @@ function PhotoViewModel(urls) {
} }
else else
{ {
tinymce.remove('#comment-text-reply');
$('#comment-reply-form-content').html(result); $('#comment-reply-form-content').html(result);
initTinyMce('#comment-text-reply');
} }
}); });
}, },
@ -41593,6 +41604,9 @@ function PhotoViewModel(urls) {
this.is_reply_form_loading = true; this.is_reply_form_loading = true;
$('#comment-reply-modal').modal('show'); $('#comment-reply-modal').modal('show');
e.preventDefault();
return false;
} }
}; };
} }

File diff suppressed because one or more lines are too long

View File

@ -33,12 +33,21 @@ function PhotoViewModel(urls) {
self.is_reply_form_loading = false; self.is_reply_form_loading = false;
}); });
}); });
$('#comment-reply-modal').on('hide.bs.modal', function (event) {
tinymce.remove('#comment-text-reply');
});
}, },
postCommentReply: function() { postCommentReply: function() {
var form = $('form', '#comment-reply-form-content'); var form = $('form', '#comment-reply-form-content');
var formUrl = $(form).attr('action'); var formUrl = $(form).attr('action');
// Seems like the TinyMCE editor in the BS modal does not persist back to the textarea correctly - so do
// this manually (bit of a hack!)
$('#comment-text-reply', form).html(tinymce.get('comment-text-reply').getContent());
var formData = form.serialize(); var formData = form.serialize();
$.post(formUrl, formData, function(result) $.post(formUrl, formData, function(result)
{ {
if (result.redirect_url) if (result.redirect_url)
@ -47,7 +56,9 @@ function PhotoViewModel(urls) {
} }
else else
{ {
tinymce.remove('#comment-text-reply');
$('#comment-reply-form-content').html(result); $('#comment-reply-form-content').html(result);
initTinyMce('#comment-text-reply');
} }
}); });
}, },
@ -57,6 +68,9 @@ function PhotoViewModel(urls) {
this.is_reply_form_loading = true; this.is_reply_form_loading = true;
$('#comment-reply-modal').modal('show'); $('#comment-reply-modal').modal('show');
e.preventDefault();
return false;
} }
}; };
} }

View File

@ -8,11 +8,9 @@
@include(Theme::viewName('partials.photo_comments_reply_form')) @include(Theme::viewName('partials.photo_comments_reply_form'))
@if ($photo->approvedComments()->count() > 0) @if ($photo->approvedComments()->count() > 0)
<ul>
@foreach ($photo->approvedComments as $comment) @foreach ($photo->approvedComments as $comment)
@include(Theme::viewName('partials.photo_single_comment')) @include(Theme::viewName('partials.photo_single_comment'))
@endforeach @endforeach
</ul>
@endif @endif
</div> </div>
</div> </div>

View File

@ -4,9 +4,9 @@ $is_reply = isset($reply_comment);
{{-- Show a previous of the comment we're replying to --}} {{-- Show a previous of the comment we're replying to --}}
@if ($is_reply) @if ($is_reply)
<ul class="mb-3"> <div class="mb-3">
@include (Theme::viewName('partials.photo_single_comment'), ['comment' => $reply_comment, 'is_reply' => true]) @include (Theme::viewName('partials.photo_single_comment'), ['comment' => $reply_comment, 'is_reply' => true])
</ul> </div>
@endif @endif
<form action="{{ $photo->postCommentUrl() }}" method="post"> <form action="{{ $photo->postCommentUrl() }}" method="post">
@ -18,33 +18,33 @@ $is_reply = isset($reply_comment);
<div class="form-group"> <div class="form-group">
<label for="commentor-name">@lang('forms.name_label')</label> <label for="commentor-name">@lang('forms.name_label')</label>
<input type="text" class="form-control{{ $errors->has('commentor_name') ? ' is-invalid' : '' }}" id="commentor-name" name="commentor_name" value="{{ old('commentor_name') }}"/> <input type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" id="commentor-name" name="name" value="{{ old('name') }}"/>
@if ($errors->has('commentor_name')) @if ($errors->has('name'))
<div class="invalid-feedback"> <div class="invalid-feedback">
<strong>{{ $errors->first('commentor_name') }}</strong> <strong>{{ $errors->first('name') }}</strong>
</div> </div>
@endif @endif
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="commentor-email">@lang('forms.email_label')</label> <label for="commentor-email">@lang('forms.email_label')</label>
<input type="text" class="form-control{{ $errors->has('commentor_email') ? ' is-invalid' : '' }}" id="commentor-email" name="commentor_email" value="{{ old('commentor_email') }}" placeholder="@lang('forms.email_placeholder')"/> <input type="text" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" id="commentor-email" name="email" value="{{ old('email') }}" placeholder="@lang('forms.email_placeholder')"/>
@if ($errors->has('commentor_email')) @if ($errors->has('email'))
<div class="invalid-feedback"> <div class="invalid-feedback">
<strong>{{ $errors->first('commentor_email') }}</strong> <strong>{{ $errors->first('email') }}</strong>
</div> </div>
@endif @endif
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="comment-text{{ $is_reply ? '-reply' : '' }}">@lang('forms.photo_comment_text_label')</label> <label for="comment-text{{ $is_reply ? '-reply' : '' }}">@lang('forms.photo_comment_text_label')</label>
<textarea class="form-control{{ $errors->has('comment_text') ? ' is-invalid' : '' }}" id="comment-text{{ $is_reply ? '-reply' : '' }}" name="comment_text" rows="10">{{ old('comment_text') }}</textarea> <textarea class="form-control{{ $errors->has('comment') ? ' is-invalid' : '' }}" id="comment-text{{ $is_reply ? '-reply' : '' }}" name="comment" rows="10">{{ old('comment') }}</textarea>
@if ($errors->has('comment_text')) @if ($errors->has('comment'))
<div class="invalid-feedback"> <div class="invalid-feedback">
<strong>{{ $errors->first('comment_text') }}</strong> <strong>{{ $errors->first('comment') }}</strong>
</div> </div>
@endif @endif
</div> </div>

View File

@ -2,11 +2,20 @@
$is_reply = (isset($is_reply) && $is_reply); $is_reply = (isset($is_reply) && $is_reply);
@endphp @endphp
<li class="photo-comment" data-comment-id="{{ $comment->id }}"> <div class="card photo-comment mt-2" data-comment-id="{{ $comment->id }}"@if (!$is_reply) style="margin-left: {{ $comment->depth() * 20 }}px;"@endif>
Comment by <b>{{ $comment->createdBy->name }}</b>:<br/> <div class="card-body">
{{ $comment->comment_text }} <h5 class="card-title"><b>{{ $comment->authorDisplayName() }}</b>:</h5>
<h6 class="card-subtitle mb-2 text-muted">{{ date(UserConfig::get('date_format'), strtotime($comment->created_at)) }}</h6>
<p>{!! $comment->textAsHtml() !!}</p>
@if (!$is_reply && $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> <a href="{{ $photo->replyToCommentFormUrl($comment->id) }}" v-on:click="replyToComment" class="card-link">@lang('gallery.photo_comments_reply_action')</a>
@endif @endif
</li> </div>
</div>
@if (!$is_reply)
@foreach ($comment->approvedChildren as $childComment)
@include(Theme::viewName('partials.photo_single_comment'), ['comment' => $childComment])
@endforeach
@endif