summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2013-03-06 20:50:21 -0300
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2013-03-06 20:50:21 -0300
commitd13ee8aa889304ca95cbd8cc777ab5e957fbed54 (patch)
treeb2f484651b068e5df9c204fdff459633f7cd00e3
parentfb0df1a738d3a9dc01c5b54619f286148532c6ce (diff)
downloadaskbot-d13ee8aa889304ca95cbd8cc777ab5e957fbed54.tar.gz
askbot-d13ee8aa889304ca95cbd8cc777ab5e957fbed54.tar.bz2
askbot-d13ee8aa889304ca95cbd8cc777ab5e957fbed54.zip
added function to repost answer as comment under the latest previously posted answer
-rw-r--r--askbot/doc/source/changelog.rst1
-rw-r--r--askbot/media/images/delete.pngbin434 -> 431 bytes
-rw-r--r--askbot/media/images/edit2.pngbin498 -> 571 bytes
-rw-r--r--askbot/media/images/flag.pngbin515 -> 519 bytes
-rw-r--r--askbot/media/images/link.pngbin601 -> 513 bytes
-rw-r--r--askbot/media/images/sprites.pngbin15025 -> 18549 bytes
-rw-r--r--askbot/media/style/style.less53
-rw-r--r--askbot/models/post.py30
-rw-r--r--askbot/models/question.py7
-rw-r--r--askbot/templates/question.html92
-rw-r--r--askbot/templates/question/answer_controls.html92
-rw-r--r--askbot/templates/question/javascript.html1
-rw-r--r--askbot/templates/question/question_controls.html28
-rw-r--r--askbot/urls.py15
-rw-r--r--askbot/views/readers.py1
-rw-r--r--askbot/views/writers.py27
16 files changed, 257 insertions, 90 deletions
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst
index 031a3e64..888ad286 100644
--- a/askbot/doc/source/changelog.rst
+++ b/askbot/doc/source/changelog.rst
@@ -7,6 +7,7 @@ Development version
Danish, Dutch, English, Finnish, French, German, Hungarian,
Italian, Japanese (requires package textsearch_ja), Norwegian,
Portugese, Romanian, Russian, Spanish, Swedish, Turkish.
+* repost answer as a comment under the previous (older) answer
0.7.48 (Jan 28, 2013)
diff --git a/askbot/media/images/delete.png b/askbot/media/images/delete.png
index 9263eae3..90349e69 100644
--- a/askbot/media/images/delete.png
+++ b/askbot/media/images/delete.png
Binary files differ
diff --git a/askbot/media/images/edit2.png b/askbot/media/images/edit2.png
index f142a68c..e683fddf 100644
--- a/askbot/media/images/edit2.png
+++ b/askbot/media/images/edit2.png
Binary files differ
diff --git a/askbot/media/images/flag.png b/askbot/media/images/flag.png
index fc302335..f9d8f0c0 100644
--- a/askbot/media/images/flag.png
+++ b/askbot/media/images/flag.png
Binary files differ
diff --git a/askbot/media/images/link.png b/askbot/media/images/link.png
index 6ad60f5e..7dd90f10 100644
--- a/askbot/media/images/link.png
+++ b/askbot/media/images/link.png
Binary files differ
diff --git a/askbot/media/images/sprites.png b/askbot/media/images/sprites.png
index 78eea747..4f23e088 100644
--- a/askbot/media/images/sprites.png
+++ b/askbot/media/images/sprites.png
Binary files differ
diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less
index a0813f1a..0ce1b849 100644
--- a/askbot/media/style/style.less
+++ b/askbot/media/style/style.less
@@ -2086,7 +2086,8 @@ ul#related-tags li {
margin-top:10px;
margin-bottom:8px;
- a {
+ a,
+ span.dropdown-toggle {
color: #777;
padding: 0px 7px 3px 18px;
cursor: pointer;
@@ -2095,17 +2096,51 @@ ul#related-tags li {
font-family:@body-font;
text-decoration: none;
height:18px;
- display:block;
- float:right;
line-height:18px;
margin-top:-2px;
margin-left:4px;
}
- a:hover {
+ a:hover,
+ span.dropdown-toggle:hover {
background-color: #f5f0c9;
+ }
+ span.dropdown-toggle {
+ background: url(../images/sprites.png) no-repeat -7px -242px;
.rounded-corners(3px);
-
+ position: relative;
+ }
+ span.dropdown-toggle:hover {
+ padding-right: 0;
+ background: url(../images/sprites.png) no-repeat -7px -274px;
+ form {
+ margin: 0;
+ }
+ input {
+ display: block !important;
+ height: 20px !important;
+ line-height: 20px !important;
+ margin: 0;
+ padding: 0 5px;
+ .rounded-corners(0);
+ width: 100% !important;
+ }
+ .dropdown-menu {
+ display: block;
+ padding: 5px 0;
+ right: -5px !important;
+ left: auto;
+ li,
+ li:hover {
+ display: block !important;
+ margin: 0;
+ padding: 0;
+ width: 100% !important;
+ }
+ li:hover {
+ background-color: #f5f0c9;
+ }
+ }
}
.sep {
color: #ccc;
@@ -2116,11 +2151,11 @@ ul#related-tags li {
}
.post-controls, .answer-controls{
.question-delete{
- background: url(../images/delete.png) no-repeat left 2px;
+ background: url(../images/delete.png) no-repeat left -1px;
padding-left:11px;
}
.question-flag{
- background: url(../images/flag.png) no-repeat center left;
+ background: url(../images/flag.png) no-repeat 2px 0;
}
.answer-publish{
background: url(../images/publish.png) no-repeat center left;
@@ -2129,7 +2164,7 @@ ul#related-tags li {
background: url(../images/unpublish.png) no-repeat 2px center;
}
.question-edit{
- background: url(../images/edit2.png) no-repeat 2px center;
+ background: url(../images/edit2.png) no-repeat 3px 1px;
}
.question-retag{
background: url(../images/retag.png) no-repeat center left;
@@ -2138,7 +2173,7 @@ ul#related-tags li {
background: url(../images/close.png) no-repeat center left;
}
.permant-link{
- background: url(../images/link.png) no-repeat center left;
+ background: url(../images/link.png) no-repeat 2px 1px;
}
.answer-convert{
diff --git a/askbot/models/post.py b/askbot/models/post.py
index 66004ce4..322a3759 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -581,6 +581,11 @@ class Post(models.Model):
def is_reject_reason(self):
return self.post_type == 'reject_reason'
+ def get_last_edited_date(self):
+ """returns date of last edit or date of creation
+ if there were no edits"""
+ return self.last_edited_at or self.added_at
+
def get_moderators(self):
"""returns query set of users who are site administrators
and moderators"""
@@ -589,10 +594,27 @@ class Post(models.Model):
user_filter = user_filter & models.Q(groups__in=self.groups.all())
return User.objects.filter(user_filter)
- def get_last_edited_date(self):
- """returns date of last edit or date of creation
- if there were no edits"""
- return self.last_edited_at or self.added_at
+ def get_previous_answer(self, user=None):
+ """returns a previous answer to a given answer;
+ only works on the "answer" post types"""
+ assert(self.post_type == 'answer')
+ all_answers = self.thread.get_answers(user=user)
+
+ matching_answers = all_answers.filter(
+ added_at__lt=self.added_at,
+ ).order_by('-added_at')
+
+ if len(matching_answers) == 0:
+ return None
+
+ answer = matching_answers[0]
+
+ if answer.id == self.id:
+ return None
+ if answer.added_at > self.added_at:
+ return None
+
+ return answer
def has_group(self, group):
"""true if post belongs to the group"""
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 2d282fbc..d1fa7cae 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -600,6 +600,13 @@ class Thread(models.Model):
else:
return self.get_answers(user).count()
+ def get_oldest_answer_id(self, user=None):
+ """give oldest visible answer id for the user"""
+ answers = self.get_answers(user=user).order_by('added_at')
+ if len(answers) > 0:
+ return answers[0].id
+ return None
+
def get_sharing_info(self, visitor=None):
"""returns a dictionary with abbreviated thread sharing info:
* users - up to a certain number of users, excluding the visitor
diff --git a/askbot/templates/question.html b/askbot/templates/question.html
index e2e6f394..5fcea3a9 100644
--- a/askbot/templates/question.html
+++ b/askbot/templates/question.html
@@ -15,7 +15,43 @@
/*<![CDATA[*/
//below is pure cross-browser javascript, no jQuery
askbot['data']['userIsThreadModerator'] = {% if user_is_thread_moderator %}true{% else %}false{% endif %};
+ askbot['data']['oldestAnswerId'] = {% if oldest_answer_id %}{{ oldest_answer_id }}{% else %}-1{% endif %};
(function(){
+
+ var hasClass = function(node, selector) {
+ var classes = (" " + node.className + " ").split(' ');
+ for (var i = 0; i < classes.length; i++) {
+ if (classes[i] === selector) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var findChildrenByClassName = function(node, className) {
+ var nodes = [];
+ var walk = function(node) {
+ if (hasClass(node, className)) {
+ nodes.push(node);
+ }
+ if (node.childNodes) {
+ for (var i=0; i < node.childNodes.length; i++) {
+ walk(node.childNodes[i]);
+ }
+ }
+ };
+ walk(node);
+ return nodes;
+ };
+
+ var removeNode = function(node) {
+ node.parentNode.removeChild(node);
+ };
+
+ var trim = function(text) {
+ return text.replace(/^\s+|\s+$/g, '');
+ };
+
var data = askbot['data'];
if (data['userIsAuthenticated']){
var votes = {};
@@ -57,20 +93,32 @@
}
function hide_convert_answer_links(post_id){
- var answer_convert_id = 'post-' + post_id + '-convert';
- var convert_answer = document.getElementById(answer_convert_id);
+ var id1 = 'post-' + post_id + '-convert';//for repost as Q comment
+ var repostAsQuestionComment = document.getElementById(id1);
+ var id2 = 'post-' + post_id + '-repost-as-comment-under-previous-answer';
+ var repostAsPrevAnsComment = document.getElementById(id2);
+ var extraOptsList = repostAsQuestionComment.parentNode;
+ var extraOpts = extraOptsList.parentNode;
if (data['userIsAdminOrMod']){
var answer_id = 'post-id-' + post_id;
var answer_container = document.getElementById(answer_id);
- var answer_element= answer_container.getElementsByClassName('answer-body')[0].children[1];
- if (
- answer_element.textContent.length >
- askbot['data']['maxCommentLength']
- ){
- convert_answer.parentNode.removeChild(convert_answer);
+ var answerBody = findChildrenByClassName(answer_container, 'answer-body')[0];
+ //todo: this is not reliable
+ var answerBodyNodes = answerBody.childNodes;
+ var answerElement = answerBodyNodes[answerBodyNodes.length - 1];
+ if (trim(answerElement.textContent).length > askbot['data']['maxCommentLength']) {
+ repostAsQuestionComment.parentNode.removeChild(repostAsQuestionComment);
+ repostAsPrevAnsComment.parentNode.removeChild(repostAsPrevAnsComment);
+ } else if (parseInt(post_id) === data['oldestAnswerId']) {
+ repostAsPrevAnsComment.parentNode.removeChild(repostAsPrevAnsComment);
}
} else{
- convert_answer.parentNode.removeChild(convert_answer);
+ repostAsQuestionComment.parentNode.removeChild(repostAsQuestionComment);
+ repostAsPrevAnsComment.parentNode.removeChild(repostAsPrevAnsComment);
+ }
+
+ if (extraOptsList.getElementsByTagName('li').length === 0) {
+ extraOpts.parentNode.removeChild(extraOpts);
}
}
@@ -87,21 +135,21 @@
if (data['userIsAdminOrMod']){
return;//all remaining functions stay on
}
- if (data['user_posts'] === undefined) {
- return;
- }
- if (post_id in data['user_posts']){
+ if (data['user_posts'] && post_id in data['user_posts']){
//todo: remove edit button from older comments
- return;//same here
+ return;
}
+ var deleteBtn = document.getElementById('post-' + post_id + '-delete');
+ var controls = deleteBtn.parentNode;
if (//maybe remove "delete" button
data['userReputation'] <
{{settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS}}
) {
- var delete_btn = document.getElementById(
- 'post-' + post_id + '-delete'
- );
- delete_btn.parentNode.removeChild(delete_btn);
+ removeNode(deleteBtn);
+ }
+ var flags = findChildrenByClassName(controls, 'question-flag');
+ if (flags.length > 0) {
+ removeNode(flags[0]);
}
if (//maybe remove "edit" button
data['userReputation'] <
@@ -116,8 +164,10 @@
data['userReputation'] <
{{settings.MIN_REP_TO_RETAG_OTHERS_QUESTIONS}}
){
- var retag_btn = document.getElementById('retag');
- retag_btn.parentNode.removeChild(retag_btn);
+ var retagBtn = document.getElementById('retag');
+ if (retagBtn) {
+ retagBtn.parentNode.removeChild(retagBtn);
+ }
}
}
function render_add_comment_button(post_id, extra_comment_count){
@@ -188,7 +238,7 @@
function hide_convert_links(){
if (!askbot['data']['userIsAdminOrMod']){
- var links = document.getElementsByClassName('convert-comment');
+ var links = findChildrenByClassName(document, 'convert-comment');
for (i=0; i<links.length; i++){
links[i].setAttribute('style', 'display:none;');
}
diff --git a/askbot/templates/question/answer_controls.html b/askbot/templates/question/answer_controls.html
index 4efc7247..c7d3c4d9 100644
--- a/askbot/templates/question/answer_controls.html
+++ b/askbot/templates/question/answer_controls.html
@@ -1,32 +1,8 @@
{#<span class="action-link swap-qa">
<a id="swap-question-with-answer-{{answer.id}}">{% trans %}swap with question{% endtrans %}</a>
</span>uncomment if needed#}
-<span class="action-link">
- <a class="permant-link"
- href="{{ answer.get_absolute_url(question_post=question) }}"
- title="{% trans %}permanent link{% endtrans %}">
- {% trans %}link{% endtrans %}
- </a>
-</span>
-<span
- id="post-{{answer.id}}-publish"
- class="action-link"
->
- {% if answer.id in published_answer_ids %}
- <a
- class="answer-unpublish"
- data-answer-id="{{ answer.id }}"
- >{% trans %}unpublish{% endtrans %}</a>
- {% else %}
- <a
- class="answer-publish"
- data-answer-id="{{ answer.id}}"
- >{% trans %}publish{% endtrans %}</a>
- {% endif %}
-</span>
-<span id='post-{{answer.id}}-delete' class="action-link delete-post">
- <a class="question-delete"
- >{% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a>
+<span id='post-{{answer.id}}-edit' class="action-link">
+ <a class="question-edit" href="{% url edit_answer answer.id %}">{% trans %}edit{% endtrans %}</a>
</span>
{% if answer.offensive_flag_count > 0 %}
<span
@@ -52,15 +28,63 @@
<a class="question-flag">{% trans %}flag offensive{% endtrans %}</a>
</span>
{% endif %}
-<span id='post-{{answer.id}}-edit' class="action-link">
- <a class="question-edit" href="{% url edit_answer answer.id %}">{% trans %}edit{% endtrans %}</a>
+<span id='post-{{answer.id}}-delete' class="action-link delete-post">
+ <a class="question-delete"
+ >{% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a>
+</span>
+<span
+ id="post-{{answer.id}}-publish"
+ class="action-link"
+>
+ {% if answer.id in published_answer_ids %}
+ <a
+ class="answer-unpublish"
+ data-answer-id="{{ answer.id }}"
+ >{% trans %}unpublish{% endtrans %}</a>
+ {% else %}
+ <a
+ class="answer-publish"
+ data-answer-id="{{ answer.id}}"
+ >{% trans %}publish{% endtrans %}</a>
+ {% endif %}
+</span>
+<span class="action-link">
+ <a class="permant-link"
+ href="{{ answer.get_absolute_url(question_post=question) }}"
+ title="{% trans %}permanent link{% endtrans %}">
+ {% trans %}link{% endtrans %}
+ </a>
</span>
-<span id='post-{{answer.id}}-convert' class="action-link">
- <form class="answer-convert" action="{% url answer_to_comment %}" method="POST">
- {% csrf_token %}
- <input type="hidden" name="answer_id" id="id_answer_id" value="{{answer.id}}"/>
- <input type="submit" name="" value="{% trans %}convert to comment{% endtrans %}"/>
- </form>
+<span class="action-link dropdown-toggle">{% trans %}more{% endtrans %}
+<ul class="dropdown-menu">
+ <li id='post-{{answer.id}}-convert'>
+ <form
+ class="answer-convert"
+ action="{% url repost_answer_as_comment_under_question %}"
+ method="post"
+ >
+ {% csrf_token %}
+ <input type="hidden" name="answer_id" id="id_answer_id" value="{{answer.id}}"/>
+ <input
+ type="submit"
+ value="{% trans %}repost as a question comment{% endtrans %}"
+ />
+ </form>
+ </li>
+ <li id='post-{{ answer.id }}-repost-as-comment-under-previous-answer'>
+ <form class="answer-convert repost-as-comment-under-previous-answer"
+ action="{% url repost_answer_as_comment_under_previous_answer %}"
+ method="post"
+ >
+ {% csrf_token %}
+ <input type="hidden" name="answer_id" value="{{ answer.id }}"/>
+ <input
+ type="submit"
+ value="{% trans %}repost as a comment under the older answer{% endtrans %}"
+ />
+ </form>
+ </li>
+</ul>
</span>
<script type="text/javascript">
askbot['functions']['hideConvertAnswerLinks']('{{ answer.id }}');
diff --git a/askbot/templates/question/javascript.html b/askbot/templates/question/javascript.html
index 8b24655a..edd7ead7 100644
--- a/askbot/templates/question/javascript.html
+++ b/askbot/templates/question/javascript.html
@@ -10,7 +10,6 @@
askbot['urls']['editComment'] = '{% url edit_comment %}';
askbot['urls']['deleteComment'] = '{% url delete_comment %}';
askbot['urls']['convertComment'] = '{% url comment_to_answer %}';
- askbot['urls']['convertAnswer'] = '{% url answer_to_comment %}';
askbot['urls']['getComment'] = '{% url get_comment %}';
askbot['urls']['saveDraftAnswer'] = '{% url save_draft_answer %}';
askbot['urls']['vote_url'] = '{% url vote question.id %}';
diff --git a/askbot/templates/question/question_controls.html b/askbot/templates/question/question_controls.html
index c782d9ad..88ef42d8 100644
--- a/askbot/templates/question/question_controls.html
+++ b/askbot/templates/question/question_controls.html
@@ -1,12 +1,8 @@
-<a
- id="post-{{question.id}}-delete"
- class="question-delete"
->{% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a>
-{% if thread.closed %}
- <a class="question-close" href="{% url reopen question.id %}">{% trans %}reopen{% endtrans %}</a>
-{% else %}
- <a class="question-close" href="{% url close question.id %}">{% trans %}close{% endtrans %}</a>
-{% endif %}
+<a id="post-{{question.id}}-edit" class="question-edit" href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a>
+<script type="text/javascript">
+ var retagUrl = "{% url retag_question question.id %}";
+</script>
+<a id="retag" class="question-retag"href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a>
{% if question.offensive_flag_count > 0 %}
<span
id="question-offensive-remove-flag-{{ question.id }}"
@@ -29,11 +25,15 @@
<a class="question-flag">{% trans %}flag offensive{% endtrans %}</a>
</span>
{% endif %}
-<script type="text/javascript">
- var retagUrl = "{% url retag_question question.id %}";
-</script>
-<a id="retag" class="question-retag"href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a>
-<a id="post-{{question.id}}-edit" class="question-edit" href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a>
+{% if thread.closed %}
+ <a class="question-close" href="{% url reopen question.id %}">{% trans %}reopen{% endtrans %}</a>
+{% else %}
+ <a class="question-close" href="{% url close question.id %}">{% trans %}close{% endtrans %}</a>
+{% endif %}
+<a
+ id="post-{{question.id}}-delete"
+ class="question-delete"
+>{% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a>
<script type="text/javascript">
askbot['functions']['renderPostControls']('{{question.id}}');
</script>
diff --git a/askbot/urls.py b/askbot/urls.py
index a82b4694..551b9ecd 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -194,15 +194,22 @@ urlpatterns = patterns('',
views.readers.get_comment,
name='get_comment'
),
- url(#post only
+ url(
r'^comment/convert/$',
views.writers.comment_to_answer,
name='comment_to_answer'
),
+ url(
+ r'^answer/repost-as-comment-under-question/$',
+ views.writers.repost_answer_as_comment,
+ kwargs={'destination': 'comment_under_question'},
+ name='repost_answer_as_comment_under_question'
+ ),
url(#post only
- r'^answer/convert/$',
- views.writers.answer_to_comment,
- name='answer_to_comment'
+ '^answer/repost-as-comment-under-previous-answer/$',
+ views.writers.repost_answer_as_comment,
+ kwargs={'destination': 'comment_under_previous_answer'},
+ name='repost_answer_as_comment_under_previous_answer'
),
url(#post only
r'^answer/publish/$',
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 7fbcf6d0..51a4da8e 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -598,6 +598,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
'user_post_id_list': user_post_id_list,
'user_can_post_comment': user_can_post_comment,#in general
'user_already_gave_answer': user_already_gave_answer,
+ 'oldest_answer_id': thread.get_oldest_answer_id(request.user),
'previous_answer': previous_answer,
'tab_id' : answer_sort_method,
'favorited' : favorited,
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index b581036c..8c421fb7 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -771,22 +771,43 @@ def comment_to_answer(request):
@decorators.admins_only
@decorators.post_only
-def answer_to_comment(request):
+#todo: change the urls config for this
+def repost_answer_as_comment(request, destination=None):
+ assert(
+ destination in (
+ 'comment_under_question',
+ 'comment_under_previous_answer'
+ )
+ )
answer_id = request.POST.get('answer_id')
if answer_id:
answer_id = int(answer_id)
answer = get_object_or_404(models.Post,
post_type = 'answer', id=answer_id)
+
+ if destination == 'comment_under_question':
+ destination_post = answer.thread._question_post()
+ else:
+ #comment_under_previous_answer
+ destination_post = answer.get_previous_answer(user=request.user)
+ #todo: implement for comment under other answer
+
+ if destination_post is None:
+ message = _('Error - could not find the destination post')
+ request.user.message_set.create(message=message)
+ return HttpResponseRedirect(answer.get_absolute_url())
+
if len(answer.text) <= askbot_settings.MAX_COMMENT_LENGTH:
answer.post_type = 'comment'
- answer.parent = answer.thread._question_post()
+ answer.parent = destination_post
#can we trust this?
old_comment_count = answer.comment_count
answer.comment_count = 0
answer_comments = models.Post.objects.get_comments().filter(parent=answer)
- answer_comments.update(parent=answer.parent)
+ answer_comments.update(parent=destination_post)
+ #why this and not just "save"?
answer.parse_and_save(author=answer.author)
answer.thread.update_answer_count()