from datetime import datetime, timedelta\r
-\r
-from forum.models import Question, FavoriteQuestion\r
-from django.utils.translation import ugettext as _\r
+import re\r
+\r
+from forum.models import Question, Action\r
+from django.template import Template, Context\r
+from django.utils.translation import ungettext, ugettext as _\r
+from django.utils.html import strip_tags\r
+from django.utils.encoding import smart_unicode\r
+from django.utils.safestring import mark_safe\r
+from django.conf import settings as django_settings\r
from django.core.urlresolvers import reverse\r
from django import template\r
-from django.conf import settings\r
+from forum.actions import *\r
+from forum import settings\r
\r
register = template.Library()\r
\r
@register.inclusion_tag('node/vote_buttons.html')\r
def vote_buttons(post, user):\r
- context = {\r
- 'post': post,\r
- 'user_vote': 'none'\r
- }\r
+ context = dict(post=post, user_vote='none')\r
\r
if user.is_authenticated():\r
- try:\r
- vote = post.votes.get(user=user)\r
- context['user_vote'] = vote.is_upvote() and 'up' or 'down'\r
- except:\r
- pass\r
+ context['user_vote'] = {1: 'up', -1: 'down', None: 'none'}[VoteAction.get_for(user, post)]\r
\r
return context\r
\r
@register.inclusion_tag('node/accept_button.html')\r
def accept_button(answer, user):\r
- return {\r
- 'can_accept': user.is_authenticated() and user.can_accept_answer(answer),\r
- 'answer': answer,\r
- 'user': user\r
+ if not settings.DISABLE_ACCEPTING_FEATURE:\r
+ return {\r
+ 'can_accept': user.is_authenticated() and user.can_accept_answer(answer),\r
+ 'answer': answer,\r
+ 'user': user\r
+ }\r
+ else:\r
+ return ''\r
+\r
+@register.inclusion_tag('node/wiki_symbol.html')\r
+def wiki_symbol(user, post):\r
+ context = {\r
+ 'is_wiki': post.nis.wiki,\r
+ 'post_type': post.friendly_name\r
}\r
\r
+ if post.nis.wiki:\r
+ if user.can_edit_post(post):\r
+ context['can_edit'] = True\r
+ context['edit_url'] = reverse('edit_' + post.node_type, kwargs={'id': post.id})\r
+ context['by'] = post.nstate.wiki.by.username\r
+ context['at'] = post.nstate.wiki.at\r
+\r
+ return context\r
+\r
@register.inclusion_tag('node/favorite_mark.html')\r
def favorite_mark(question, user):\r
try:\r
- FavoriteQuestion.objects.get(question=question, user=user)\r
+ FavoriteAction.objects.get(canceled=False, node=question, user=user)\r
favorited = True\r
except:\r
favorited = False\r
\r
- favorite_count = question.favorited_by.count()\r
+ return {'favorited': favorited, 'favorite_count': question.favorite_count, 'question': question}\r
\r
- return {'favorited': favorited, 'favorite_count': favorite_count, 'question': question}\r
+@register.simple_tag\r
+def post_classes(post):\r
+ classes = []\r
\r
-def post_control(text, url, command=False, title=""):\r
- return {'text': text, 'url': url, 'command': command, 'title': title}\r
+ if post.nis.deleted:\r
+ classes.append('deleted')\r
\r
-@register.inclusion_tag('node/post_controls.html')\r
-def post_controls(post, user):\r
- controls = []\r
+ if post.node_type == "answer":\r
+ if (not settings.DISABLE_ACCEPTING_FEATURE) and post.nis.accepted:\r
+ classes.append('accepted-answer')\r
\r
- if user.is_authenticated():\r
- post_type = (post.__class__ is Question) and 'question' or 'answer'\r
+ if post.author == post.question.author:\r
+ classes.append('answered-by-owner')\r
\r
- if post_type == "answer":\r
- controls.append(post_control(_('permanent link'), '#%d' % post.id, title=_("answer permanent link")))\r
+ return " ".join(classes)\r
+\r
+def post_control(text, url, command=False, withprompt=False, confirm=False, title="", copy=False):\r
+ classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ") + \\r
+ (copy and " copy" or " ")\r
+ return {'text': text, 'url': url, 'classes': classes, 'title': title}\r
\r
- edit_url = reverse('edit_' + post_type, kwargs={'id': post.id})\r
- if user.can_edit_post(post):\r
- controls.append(post_control(_('edit'), edit_url))\r
- elif post_type == 'question' and user.can_retag_questions():\r
- controls.append(post_control(_('retag'), edit_url))\r
+\r
+moderation_enabled = False\r
+for m in django_settings.MODULE_LIST:\r
+ if m.__name__.endswith('moderation'):\r
+ moderation_enabled = True\r
+\r
+@register.inclusion_tag('node/post_controls.html' if not moderation_enabled else "modules/moderation/node/post_controls.html")\r
+def post_controls(post, user):\r
+ controls = []\r
+ menu = []\r
+ post_type = post.node_type\r
+\r
+ # We show the link tool if the post is an Answer. It is visible to Guests too.\r
+ if post_type == "answer":\r
+ # Answer permanent link tool\r
+ controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id,}),\r
+ title=_("answer permanent link"), command=True, withprompt=True, copy=True))\r
+\r
+ # Users should be able to award points for an answer. Users cannot award their own answers\r
+ if user != post.author and user.is_authenticated():\r
+ controls.append(post_control(_("award points"), reverse('award_points', kwargs={'user_id' : post.author.id,\r
+ 'answer_id' : post.id}), title=_("award points to %s") % smart_unicode(post.author.username),\r
+ command=True, withprompt=True))\r
+\r
+ # The other controls are visible only to authenticated users.\r
+ if user.is_authenticated():\r
+ try:\r
+ edit_url = reverse('edit_' + post_type, kwargs={'id': post.id})\r
+ if user.can_edit_post(post):\r
+ controls.append(post_control(_('edit'), edit_url))\r
+ elif post_type == 'question' and user.can_retag_questions():\r
+ controls.append(post_control(_('retag'), edit_url))\r
+ except:\r
+ pass\r
\r
if post_type == 'question':\r
- if post.closed and user.can_reopen_question(post):\r
- controls.append(post_control(_('reopen'), reverse('reopen', kwargs={'id': post.id})))\r
- elif not post.closed and user.can_close_question(post):\r
- controls.append(post_control(_('close'), reverse('close', kwargs={'id': post.id})))\r
+ if post.nis.closed and user.can_reopen_question(post):\r
+ controls.append(post_control(_('reopen'), reverse('reopen', kwargs={'id': post.id}), command=True))\r
+ elif not post.nis.closed and user.can_close_question(post):\r
+ controls.append(post_control(_('close'), reverse('close', kwargs={'id': post.id}), command=True, withprompt=True))\r
\r
if user.can_flag_offensive(post):\r
- label = _('flag')\r
+ label = _('report')\r
\r
if user.can_view_offensive_flags(post):\r
- label = "%s (%d)" % (label, post.flaggeditems.count())\r
+ label = "%s (%d)" % (label, post.flag_count)\r
\r
controls.append(post_control(label, reverse('flag_post', kwargs={'id': post.id}),\r
- command=True, title=_("report as offensive (i.e containing spam, advertising, malicious text, etc.)")))\r
+ command=True, withprompt=True, title=_("report as offensive (i.e containing spam, advertising, malicious text, etc.)")))\r
\r
if user.can_delete_post(post):\r
- controls.append(post_control(_('delete'), reverse('delete_post', kwargs={'id': post.id}),\r
- command=True))\r
+ if post.nis.deleted:\r
+ controls.append(post_control(_('undelete'), reverse('delete_post', kwargs={'id': post.id}),\r
+ command=True, confirm=True))\r
+ else:\r
+ controls.append(post_control(_('delete'), reverse('delete_post', kwargs={'id': post.id}),\r
+ command=True, confirm=True))\r
+\r
+ if user.can_delete_post(post):\r
+ menu.append(post_control(_('see revisions'),\r
+ reverse('revisions',\r
+ kwargs={'id': post.id}),\r
+ command=False, confirm=False))\r
+\r
+ if settings.WIKI_ON:\r
+ if (not post.nis.wiki) and user.can_wikify(post):\r
+ menu.append(post_control(_('mark as community wiki'), reverse('wikify', kwargs={'id': post.id}),\r
+ command=True, confirm=True))\r
+\r
+ elif post.nis.wiki and user.can_cancel_wiki(post):\r
+ menu.append(post_control(_('cancel community wiki'), reverse('wikify', kwargs={'id': post.id}),\r
+ command=True, confirm=True))\r
+\r
+ if post.node_type == "answer" and user.can_convert_to_comment(post):\r
+ menu.append(post_control(_('convert to comment'), reverse('convert_to_comment', kwargs={'id': post.id}),\r
+ command=True, withprompt=True))\r
+ \r
+ if post.node_type == "answer" and user.can_convert_to_question(post):\r
+ menu.append(post_control(_('convert to question'), reverse('convert_to_question', kwargs={'id': post.id}),\r
+ command=False, confirm=True))\r
\r
- return {'controls': controls}\r
+ if user.is_superuser or user.is_staff:\r
+ plain_text = strip_tags(post.html)\r
+\r
+ char_count = len(plain_text)\r
+ fullStr = plain_text + " "\r
+ left_trimmedStr = re.sub(re.compile(r"^[^\w]+", re.IGNORECASE), "", fullStr)\r
+ cleanedStr = re.sub(re.compile(r"[^\w]+", re.IGNORECASE), " ", left_trimmedStr)\r
+ splitString = cleanedStr.split(" ")\r
+ word_count = len(splitString) - 1\r
+\r
+ metrics = mark_safe("<b>%s %s / %s %s</b>" % (char_count, ungettext('character', 'characters', char_count),\r
+ word_count, ungettext('word', 'words', word_count)))\r
+\r
+ menu.append(post_control(metrics, "#", command=False, withprompt=False))\r
+\r
+ return {'controls': controls, 'menu': menu, 'post': post, 'user': user}\r
\r
@register.inclusion_tag('node/comments.html')\r
def comments(post, user):\r
- all_comments = post.comments.filter(deleted=False).order_by('added_at')\r
+ all_comments = post.comments.filter_state(deleted=False).order_by('-added_at')\r
\r
if len(all_comments) <= 5:\r
top_scorers = all_comments\r
else:\r
- top_scorers = sorted(all_comments, lambda c1, c2: c2.score - c1.score)[0:5]\r
+ top_scorers = sorted(all_comments, lambda c1, c2: cmp(c2.score, c1.score))[0:5]\r
\r
comments = []\r
showing = 0\r
context = {\r
'can_delete': user.can_delete_comment(c),\r
'can_like': user.can_like_comment(c),\r
- 'can_edit': user.can_edit_comment(c)\r
+ 'can_edit': user.can_edit_comment(c),\r
+ 'can_convert': user.can_convert_comment_to_answer(c)\r
}\r
\r
if c in top_scorers or c.is_reply_to(user):\r
showing += 1\r
\r
if context['can_like']:\r
- try:\r
- c.votes.get(user=user)\r
- context['likes'] = True\r
- except:\r
- context['likes'] = False\r
+ context['likes'] = VoteAction.get_for(user, c) == 1\r
\r
context['user'] = c.user\r
+ context['comment'] = c.comment\r
context.update(dict(c.__dict__))\r
comments.append(context)\r
\r
+ # Generate canned comments\r
+ canned_comments = []\r
+ for comment in settings.CANNED_COMMENTS:\r
+ t = Template(smart_unicode(comment))\r
+ c = Context({\r
+ 'post' : post,\r
+ 'settings' : settings,\r
+ })\r
+ canned_comments.append(t.render(c))\r
+\r
return {\r
'comments': comments,\r
+ 'canned_comments': canned_comments,\r
'post': post,\r
'can_comment': user.can_comment(post),\r
- 'max_length': settings.COMMENT_MAX_LENGTH,\r
+ 'max_length': settings.FORM_MAX_COMMENT_BODY,\r
+ 'min_length': settings.FORM_MIN_COMMENT_BODY,\r
+ 'show_gravatar': settings.FORM_GRAVATAR_IN_COMMENTS,\r
'showing': showing,\r
'total': len(all_comments),\r
+ 'user': user,\r
}\r
+\r
+\r
+@register.inclusion_tag("node/contributors_info.html")\r
+def contributors_info(node, verb=None):\r
+ return {\r
+ 'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or (\r
+ (node.node_type == "answer") and _("answered") or _("posted"))),\r
+ 'node': node,\r
+ }\r
+\r
+@register.inclusion_tag("node/reviser_info.html")\r
+def reviser_info(revision):\r
+ return {'revision': revision}\r