Added comment editing support

In other news, editing a post doesn't reload page anymore
(yay for editing tags for Youtube posts)
This commit is contained in:
Marcin Kurczewski 2014-01-25 16:34:20 +01:00
parent fd9433a2e3
commit 100303173e
16 changed files with 310 additions and 184 deletions

View file

@ -117,6 +117,8 @@ listComments=anonymous
addComment=registered
deleteComment.own=registered
deleteComment.all=moderator
editComment.own=registered
editComment.all=admin
listTags=anonymous
mergeTags=moderator

View file

@ -0,0 +1,12 @@
.preview {
border: 1px solid yellow;
background: url('../img/preview.png') lemonchiffon;
padding: 0.5em;
display: none;
}
form.edit-comment textarea,
form.add-comment textarea {
width: 50em;
height: 8em;
}

View file

@ -31,18 +31,22 @@
}
.comment .date,
.comment .edit,
.comment .delete {
font-size: small;
}
.comment .edit:before,
.comment .delete:before {
margin-left: 0.2em;
content: ' [';
color: silver;
}
.comment .edit:after,
.comment .delete:after {
content: ']';
color: silver;
}
.comment .edit a,
.comment .delete a {
color: silver;
}

View file

@ -133,7 +133,7 @@ i.icon-dl {
margin: 2px;
}
form.edit-post {
.unit.edit-post {
display: none;
}
form.edit-post .safety label:not(.left) {
@ -145,15 +145,3 @@ ul.tagit {
margin: 0;
font-size: 1em;
}
.preview {
border: 1px solid yellow;
background: url('../img/preview.png') lemonchiffon;
padding: 0.5em;
display: none;
}
form.add-comment textarea {
width: 50em;
height: 8em;
}

View file

@ -0,0 +1,105 @@
$(function()
{
function onDomUpdate()
{
$('form.edit-comment, form.add-comment').submit(function(e)
{
e.preventDefault();
rememberLastSearchQuery();
var formDom = $(this);
if (formDom.hasClass('inactive'))
return;
formDom.addClass('inactive');
formDom.find(':input').attr('readonly', true);
var url = formDom.attr('action') + '?json';
var fd = new FormData(formDom[0]);
var preview = false;
$.each(formDom.serializeArray(), function(i, x)
{
if (x.name == 'sender' && x.value == 'preview')
preview = true;
});
var ajaxData =
{
url: url,
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data)
{
if (data['success'])
{
if (preview)
{
formDom.find('.preview').html(data['textPreview']).show();
}
else
{
formDom.find('.preview').hide();
var cb = function()
{
$.get(window.location.href, function(data)
{
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
$('body').trigger('dom-update');
});
}
if (formDom.hasClass('add-comment'))
{
cb();
formDom.find('textarea').val('');
}
else
{
formDom.slideUp(function()
{
cb();
$(this).remove();
});
}
}
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
else
{
alert(data['message']);
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
},
error: function()
{
alert('Fatal error');
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
};
$.ajax(ajaxData);
});
$('.comment .edit a').click(function(e)
{
e.preventDefault();
var commentDom = $(this).parents('.comment');
$.get($(this).attr('href'), function(data)
{
commentDom.find('form.edit-comment').remove();
var otherForm = $(data).find('form.edit-comment');
otherForm.hide();
commentDom.find('.body').append(otherForm);
otherForm.slideDown();
$('body').trigger('dom-update');
});
});
}
$('body').bind('dom-update', onDomUpdate);
});

View file

@ -1,56 +1,58 @@
function onDomUpdate()
{
$('li.edit a').click(function(e)
{
e.preventDefault();
var aDom = $(this);
if (aDom.hasClass('inactive'))
return;
aDom.addClass('inactive');
var tags = [];
$.getJSON('/tags?json', {filter: 'order:popularity,desc'}, function(data)
{
aDom.removeClass('inactive');
var formDom = $('form.edit-post');
tags = data['tags'];
if (!$(formDom).is(':visible'))
{
var tagItOptions = getTagItOptions();
tagItOptions.availableTags = tags;
tagItOptions.placeholderText = $('.tags input').attr('placeholder');
$('.tags input').tagit(tagItOptions);
formDom.show().css('height', formDom.height()).hide().slideDown();
}
formDom.find('input[type=text]:visible:eq(0)').focus();
$('html, body').animate({ scrollTop: $(formDom).offset().top + 'px' }, 'fast');
});
});
$('.comments.unit a.simple-action').data('callback', function()
{
$.get(window.location.href, function(data)
{
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
$('body').trigger('dom-update');
});
});
$('#sidebar a.simple-action').data('callback', function()
{
$.get(window.location.href, function(data)
{
$('#sidebar').replaceWith($(data).find('#sidebar'));
$('body').trigger('dom-update');
});
});
}
$(function()
{
function onDomUpdate()
{
$('#sidebar .edit a').click(function(e)
{
e.preventDefault();
var aDom = $(this);
if (aDom.hasClass('inactive'))
return;
aDom.addClass('inactive');
var formDom = $('form.edit-post');
if (formDom.find('.tagit').length == 0)
{
$.getJSON('/tags?json', {filter: 'order:popularity,desc'}, function(data)
{
aDom.removeClass('inactive');
var tags = data['tags'];
var tagItOptions = getTagItOptions();
tagItOptions.availableTags = tags;
tagItOptions.placeholderText = $('.tags input').attr('placeholder');
$('.tags input').tagit(tagItOptions);
});
}
else
aDom.removeClass('inactive');
if (!$(formDom).is(':visible'))
formDom.parents('.unit').show().css('height', formDom.height()).hide().slideDown();
$('html, body').animate({ scrollTop: $(formDom).offset().top + 'px' }, 'fast');
formDom.find('input[type=text]:visible:eq(0)').focus();
});
$('.comments.unit a.simple-action').data('callback', function()
{
$.get(window.location.href, function(data)
{
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
$('body').trigger('dom-update');
});
});
$('#sidebar a.simple-action').data('callback', function()
{
$.get(window.location.href, function(data)
{
$('#sidebar').replaceWith($(data).find('#sidebar'));
$('body').trigger('dom-update');
});
});
}
$('body').bind('dom-update', onDomUpdate);
$('form.edit-post').submit(function(e)
@ -79,82 +81,20 @@ $(function()
{
if (data['success'])
{
window.location.reload();
$.get(window.location.href, function(data)
{
$('#sidebar').replaceWith($(data).find('#sidebar'));
$('#edit-token').replaceWith($(data).find('#edit-token'));
$('body').trigger('dom-update');
});
formDom.parents('.unit').hide();
}
else
{
alert(data['message']);
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
},
error: function()
{
alert('Fatal error');
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
};
$.ajax(ajaxData);
});
$('form.add-comment').submit(function(e)
{
e.preventDefault();
rememberLastSearchQuery();
var formDom = $(this);
if (formDom.hasClass('inactive'))
return;
formDom.addClass('inactive');
formDom.find(':input').attr('readonly', true);
var url = formDom.attr('action') + '?json';
var fd = new FormData(formDom[0]);
var preview = false;
$.each(formDom.serializeArray(), function(i, x)
{
if (x.name == 'sender' && x.value == 'preview')
preview = true;
});
var ajaxData =
{
url: url,
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data)
{
if (data['success'])
{
if (preview)
{
formDom.find('.preview').html(data['textPreview']).show();
}
else
{
formDom.find('.preview').hide();
$.get(window.location.href, function(data)
{
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
$('body').trigger('dom-update');
});
formDom.find('textarea').val('');
}
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
else
{
alert(data['message']);
formDom.find(':input').attr('readonly', false);
formDom.removeClass('inactive');
}
},
error: function()
{

View file

@ -11,9 +11,11 @@ class CommentController
$this->context->stylesheets []= 'post-small.css';
$this->context->stylesheets []= 'comment-list.css';
$this->context->stylesheets []= 'comment-small.css';
$this->context->stylesheets []= 'comment-edit.css';
$this->context->stylesheets []= 'paginator.css';
if ($this->context->user->hasEnabledEndlessScrolling())
$this->context->scripts []= 'paginator-endless.js';
$this->context->scripts []= 'comment-edit.js';
$page = intval($page);
$commentsPerPage = intval($this->config->comments->commentsPerPage);
@ -52,6 +54,7 @@ class CommentController
PrivilegesHelper::confirmEmail($this->context->user);
$post = PostModel::findById($postId);
$this->context->transport->post = $post;
if (InputHelper::get('submit'))
{
@ -66,6 +69,7 @@ class CommentController
$comment->setCommenter(null);
$comment->commentDate = time();
$comment->text = $text;
if (InputHelper::get('sender') != 'preview')
{
CommentModel::save($comment);
@ -78,6 +82,36 @@ class CommentController
/**
* @route /comment/{id}/edit
* @validate id [0-9]+
*/
public function editAction($id)
{
$comment = CommentModel::findById($id);
$this->context->transport->comment = $comment;
PrivilegesHelper::confirmWithException(Privilege::EditComment, PrivilegesHelper::getIdentitySubPrivilege($comment->getCommenter()));
if (InputHelper::get('submit'))
{
$text = InputHelper::get('text');
$text = CommentModel::validateText($text);
$comment->text = $text;
if (InputHelper::get('sender') != 'preview')
{
CommentModel::save($comment);
LogHelper::log('{user} has edited comment in {post}', ['post' => TextHelper::reprPost($comment->getPost())]);
}
$this->context->transport->textPreview = $comment->getText();
StatusHelper::success();
}
}
/**
* @route /comment/{id}/delete
* @validate id [0-9]+

View file

@ -462,7 +462,9 @@ class PostController
$this->context->pageThumb = \Chibi\UrlHelper::route('post', 'thumb', ['name' => $post->name]);
$this->context->stylesheets []= 'post-view.css';
$this->context->stylesheets []= 'comment-small.css';
$this->context->stylesheets []= 'comment-edit.css';
$this->context->scripts []= 'post-view.js';
$this->context->scripts []= 'comment-edit.js';
$this->context->subTitle = 'showing ' . TextHelper::reprPost($post) . ' – ' . TextHelper::reprTags($post->getTags());
$this->context->favorite = $favorite;
$this->context->score = $score;

View file

@ -251,6 +251,13 @@ class TextHelper
return $path;
}
public static function secureWhitespace($text)
{
$text = str_replace(["\r\n", "\r", "\n"], '
', $text);
$text = str_replace(' ', ' ', $text);
return $text;
}
const HTML_OPEN = 1;
const HTML_CLOSE = 2;
const HTML_LEAF = 3;

View file

@ -34,6 +34,7 @@ class Privilege extends Enum
const ListComments = 20;
const AddComment = 23;
const DeleteComment = 24;
const EditComment = 37;
const ListTags = 21;
const MergeTags = 27;

View file

@ -0,0 +1,16 @@
<form action="<?php echo \Chibi\UrlHelper::route('comment', 'add', ['postId' => $this->context->transport->post->id]) ?>" method="post" class="add-comment aligned">
<h1>add comment</h1>
<div class="preview"></div>
<div class="text">
<div class="input-wrapper"><textarea name="text" cols="50" rows="3"></textarea></div>
</div>
<input type="hidden" name="submit" value="1"/>
<div>
<button name="sender" class="submit" type="submit" value="preview">Preview</button>&nbsp;
<button name="sender" class="submit" type="submit" value="submit">Submit</button>
</div>
</form>

View file

@ -0,0 +1,16 @@
<form action="<?php echo \Chibi\UrlHelper::route('comment', 'edit', ['id' => $this->context->transport->comment->id]) ?>" method="post" class="edit-comment aligned">
<h1>edit comment</h1>
<div class="preview"></div>
<div class="text">
<div class="input-wrapper"><textarea name="text" cols="50" rows="3"><?php echo TextHelper::secureWhitespace($this->context->transport->comment->text) ?></textarea></div>
</div>
<input type="hidden" name="submit" value="1"/>
<div>
<button name="sender" class="submit" type="submit" value="preview">Preview</button>&nbsp;
<button name="sender" class="submit" type="submit" value="submit">Submit</button>
</div>
</form>

View file

@ -1,41 +1,43 @@
<?php if (empty($this->context->transport->comments)): ?>
<p class="alert alert-warning">No comments to show.</p>
<?php else: ?>
<div class="comments paginator-content">
<?php
$groups = [];
$posts = [];
$currentGroupPostId = null;
$currentGroup = null;
foreach ($this->context->transport->comments as $comment)
{
if ($comment->postId != $currentGroupPostId)
<div class="comments-wrapper">
<div class="comments paginator-content">
<?php
$groups = [];
$posts = [];
$currentGroupPostId = null;
$currentGroup = null;
foreach ($this->context->transport->comments as $comment)
{
unset($currentGroup);
$currentGroup = [];
$currentGroupPostId = $comment->postId;
$posts[$comment->postId] = $comment->getPost();
$groups[] = &$currentGroup;
if ($comment->postId != $currentGroupPostId)
{
unset($currentGroup);
$currentGroup = [];
$currentGroupPostId = $comment->postId;
$posts[$comment->postId] = $comment->getPost();
$groups[] = &$currentGroup;
}
$currentGroup []= $comment;
}
$currentGroup []= $comment;
}
?>
<?php foreach ($groups as $group): ?>
<div class="comment-group">
<div class="post-wrapper">
<?php $this->context->post = $posts[reset($group)->postId] ?>
<?php echo $this->renderFile('post-small') ?>
?>
<?php foreach ($groups as $group): ?>
<div class="comment-group">
<div class="post-wrapper">
<?php $this->context->post = $posts[reset($group)->postId] ?>
<?php echo $this->renderFile('post-small') ?>
</div>
<div class="comments">
<?php foreach ($group as $comment): ?>
<?php $this->context->comment = $comment ?>
<?php echo $this->renderFile('comment-small') ?>
<?php endforeach ?>
</div>
<div class="clear"></div>
</div>
<div class="comments">
<?php foreach ($group as $comment): ?>
<?php $this->context->comment = $comment ?>
<?php echo $this->renderFile('comment-small') ?>
<?php endforeach ?>
</div>
<div class="clear"></div>
</div>
<?php endforeach ?>
</div>
<?php endforeach ?>
</div>
<?php $this->renderFile('paginator') ?>
<?php $this->renderFile('paginator') ?>
</div>
<?php endif ?>

View file

@ -26,6 +26,14 @@
<?php echo date('Y-m-d H:i', $this->context->comment->commentDate) ?>
</span>
<?php if (PrivilegesHelper::confirm(Privilege::EditComment, PrivilegesHelper::getIdentitySubPrivilege($commenter))): ?>
<span class="edit">
<a href="<?php echo \Chibi\UrlHelper::route('comment', 'edit', ['id' => $this->context->comment->id]) ?>">
edit
</a>
</span>
<?php endif ?>
<?php if (PrivilegesHelper::confirm(Privilege::DeleteComment, PrivilegesHelper::getIdentitySubPrivilege($commenter))): ?>
<span class="delete">
<a class="simple-action confirmable" href="<?php echo \Chibi\UrlHelper::route('comment', 'delete', ['id' => $this->context->comment->id]) ?>" data-confirm-text="Are you sure you want to delete this comment?">

View file

@ -1,4 +1,4 @@
<form action="<?php echo \Chibi\UrlHelper::route('post', 'edit', ['id' => $this->context->transport->post->id]) ?>" method="post" enctype="multipart/form-data" class="edit-post aligned unit">
<form action="<?php echo \Chibi\UrlHelper::route('post', 'edit', ['id' => $this->context->transport->post->id]) ?>" method="post" enctype="multipart/form-data" class="edit-post aligned">
<h1>edit post</h1>
<?php if (PrivilegesHelper::confirm(Privilege::EditPostSafety, PrivilegesHelper::getIdentitySubPrivilege($this->context->transport->post->getUploader()))): ?>
<div class="safety">

View file

@ -312,7 +312,9 @@
</div>
<?php if ($canEditAnything): ?>
<?php $this->renderFile('post-edit') ?>
<div class="edit-post unit">
<?php $this->renderFile('post-edit') ?>
</div>
<?php endif ?>
<div class="comments-wrapper">
@ -330,21 +332,8 @@
</div>
<?php if (PrivilegesHelper::confirm(Privilege::AddComment)): ?>
<form action="<?php echo \Chibi\UrlHelper::route('comment', 'add', ['postId' => $this->context->transport->post->id]) ?>" method="post" class="add-comment aligned unit">
<h1>add comment</h1>
<div class="preview"></div>
<div class="text">
<div class="input-wrapper"><textarea name="text" cols="50" rows="3"></textarea></div>
</div>
<input type="hidden" name="submit" value="1"/>
<div>
<button name="sender" class="submit" type="submit" value="preview">Preview</button>&nbsp;
<button name="sender" class="submit" type="submit" value="submit">Submit</button>
</div>
</form>
<div class="unit">
<?php $this->renderFile('comment-add') ?>
</div>
<?php endif ?>
</div>