1 from datetime import datetime, timedelta
\r
4 from forum.models import Question, Action
\r
5 from django.template import Template, Context
\r
6 from django.utils.translation import ungettext, ugettext as _
\r
7 from django.utils.html import strip_tags
\r
8 from django.utils.encoding import smart_unicode
\r
9 from django.utils.safestring import mark_safe
\r
10 from django.conf import settings as django_settings
\r
11 from django.core.urlresolvers import reverse
\r
12 from django import template
\r
13 from forum.actions import *
\r
14 from forum import settings
\r
16 register = template.Library()
\r
18 @register.inclusion_tag('node/vote_buttons.html')
\r
19 def vote_buttons(post, user):
\r
20 context = dict(post=post, user_vote='none')
\r
22 if user.is_authenticated():
\r
23 context['user_vote'] = {1: 'up', -1: 'down', None: 'none'}[VoteAction.get_for(user, post)]
\r
27 @register.inclusion_tag('node/accept_button.html')
\r
28 def accept_button(answer, user):
\r
29 if not settings.DISABLE_ACCEPTING_FEATURE:
\r
31 'can_accept': user.is_authenticated() and user.can_accept_answer(answer),
\r
38 @register.inclusion_tag('node/wiki_symbol.html')
\r
39 def wiki_symbol(user, post):
\r
41 'is_wiki': post.nis.wiki,
\r
42 'post_type': post.friendly_name
\r
46 if user.can_edit_post(post):
\r
47 context['can_edit'] = True
\r
48 context['edit_url'] = reverse('edit_' + post.node_type, kwargs={'id': post.id})
\r
49 context['by'] = post.nstate.wiki.by.username
\r
50 context['at'] = post.nstate.wiki.at
\r
54 @register.inclusion_tag('node/favorite_mark.html')
\r
55 def favorite_mark(question, user):
\r
57 FavoriteAction.objects.get(canceled=False, node=question, user=user)
\r
62 return {'favorited': favorited, 'favorite_count': question.favorite_count, 'question': question}
\r
64 @register.simple_tag
\r
65 def post_classes(post):
\r
68 if post.nis.deleted:
\r
69 classes.append('deleted')
\r
71 if post.node_type == "answer":
\r
72 if (not settings.DISABLE_ACCEPTING_FEATURE) and post.nis.accepted:
\r
73 classes.append('accepted-answer')
\r
75 if post.author == post.question.author:
\r
76 classes.append('answered-by-owner')
\r
78 return " ".join(classes)
\r
80 def post_control(text, url, command=False, withprompt=False, confirm=False, title="", copy=False, extra_classes=[]):
\r
81 classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ") + \
\r
82 (copy and " copy" or " ")
\r
84 for extra_class in extra_classes:
\r
85 classes += " %s" % extra_class
\r
87 return {'text': text, 'url': url, 'classes': classes, 'title': title}
\r
90 moderation_enabled = False
\r
91 for m in django_settings.MODULE_LIST:
\r
92 if m.__name__.endswith('moderation'):
\r
93 moderation_enabled = True
\r
95 @register.inclusion_tag('node/post_controls.html' if not moderation_enabled else "modules/moderation/node/post_controls.html")
\r
96 def post_controls(post, user):
\r
99 post_type = post.node_type
\r
101 # We show the link tool if the post is an Answer. It is visible to Guests too.
\r
102 if post_type == "answer":
\r
103 # Answer permanent link tool
\r
104 controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id,}),
\r
105 title=_("answer permanent link"), command=True, withprompt=True, copy=True))
\r
107 # Users should be able to award points for an answer. Users cannot award their own answers
\r
108 if user != post.author and user.is_authenticated() and user.reputation > 1:
\r
109 controls.append(post_control(_("award points"), reverse('award_points', kwargs={'user_id' : post.author.id,
\r
110 'answer_id' : post.id}), title=_("award points to %s") % smart_unicode(post.author.username),
\r
111 command=True, withprompt=True))
\r
113 # The other controls are visible only to authenticated users.
\r
114 if user.is_authenticated():
\r
116 edit_url = reverse('edit_' + post_type, kwargs={'id': post.id})
\r
117 if user.can_edit_post(post):
\r
118 controls.append(post_control(_('edit'), edit_url))
\r
119 elif post_type == 'question' and user.can_retag_questions():
\r
120 controls.append(post_control(_('retag'), edit_url))
\r
124 if post_type == 'question':
\r
125 if post.nis.closed and user.can_reopen_question(post):
\r
126 controls.append(post_control(_('reopen'), reverse('reopen', kwargs={'id': post.id}), command=True))
\r
127 elif not post.nis.closed and user.can_close_question(post):
\r
128 controls.append(post_control(_('close'), reverse('close', kwargs={'id': post.id}), command=True, withprompt=True))
\r
130 if user.can_flag_offensive(post):
\r
131 label = _('report')
\r
133 if user.can_view_offensive_flags(post):
\r
134 label = "%s (%d)" % (label, post.flag_count)
\r
137 report_control = post_control(label, reverse('flag_post', kwargs={'id': post.id}),
\r
138 command=True, withprompt=True,
\r
139 title=_("report as offensive (i.e containing spam, advertising, malicious text, etc.)"))
\r
141 # Depending on the setting choose where to attach the control
\r
142 if settings.REPORT_OFFENSIVE_CONTROL_POSITION.value == "more":
\r
143 menu.append(report_control)
\r
145 controls.append(report_control)
\r
147 if user.can_delete_post(post):
\r
148 if post.nis.deleted:
\r
149 controls.append(post_control(_('undelete'), reverse('delete_post', kwargs={'id': post.id}),
\r
150 command=True, confirm=True))
\r
152 controls.append(post_control(_('delete'), reverse('delete_post', kwargs={'id': post.id}),
\r
153 command=True, confirm=True))
\r
155 if user.can_delete_post(post):
\r
156 menu.append(post_control(_('see revisions'),
\r
157 reverse('revisions',
\r
158 kwargs={'id': post.id}),
\r
159 command=False, confirm=False))
\r
161 if settings.WIKI_ON:
\r
162 if (not post.nis.wiki) and user.can_wikify(post):
\r
163 menu.append(post_control(_('mark as community wiki'), reverse('wikify', kwargs={'id': post.id}),
\r
164 command=True, confirm=True))
\r
166 elif post.nis.wiki and user.can_cancel_wiki(post):
\r
167 menu.append(post_control(_('cancel community wiki'), reverse('wikify', kwargs={'id': post.id}),
\r
168 command=True, confirm=True))
\r
170 if post.node_type == "answer" and user.can_convert_to_comment(post):
\r
171 menu.append(post_control(_('convert to comment'), reverse('convert_to_comment', kwargs={'id': post.id}),
\r
172 command=True, withprompt=True))
\r
174 if post.node_type == "answer" and user.can_convert_to_question(post):
\r
175 menu.append(post_control(_('convert to question'), reverse('convert_to_question', kwargs={'id': post.id}),
\r
176 command=False, confirm=True))
\r
178 if user.is_superuser or user.is_staff:
\r
179 plain_text = strip_tags(post.html)
\r
181 char_count = len(plain_text)
\r
182 fullStr = plain_text + " "
\r
183 left_trimmedStr = re.sub(re.compile(r"^[^\w]+", re.IGNORECASE), "", fullStr)
\r
184 cleanedStr = re.sub(re.compile(r"[^\w]+", re.IGNORECASE), " ", left_trimmedStr)
\r
185 splitString = cleanedStr.split(" ")
\r
186 word_count = len(splitString) - 1
\r
188 metrics = mark_safe("<b>%s %s / %s %s</b>" % (char_count, ungettext('character', 'characters', char_count),
\r
189 word_count, ungettext('word', 'words', word_count)))
\r
191 menu.append(post_control(metrics, "#", command=False, withprompt=False))
\r
193 return {'controls': controls, 'menu': menu, 'post': post, 'user': user}
\r
195 def _comments(post, user):
\r
196 all_comments = post.comments.filter_state(deleted=False)\
\r
197 .order_by('-added_at' if settings.SHOW_LATEST_COMMENTS_FIRST else 'added_at')
\r
199 if len(all_comments) <= 5:
\r
200 top_scorers = all_comments
\r
202 top_scorers = sorted(all_comments, lambda c1, c2: cmp(c2.score, c1.score))[0:5]
\r
206 for c in all_comments:
\r
208 'can_delete': user.can_delete_comment(c),
\r
209 'can_like': user.can_like_comment(c),
\r
210 'can_edit': user.can_edit_comment(c),
\r
211 'can_convert': user.can_convert_comment_to_answer(c)
\r
214 if c in top_scorers or c.is_reply_to(user):
\r
215 context['top_scorer'] = True
\r
218 if context['can_like']:
\r
219 context['likes'] = VoteAction.get_for(user, c) == 1
\r
221 context['user'] = c.user
\r
222 context['comment'] = c.comment
\r
223 context.update(dict(c.__dict__))
\r
224 comments.append(context)
\r
226 # Generate canned comments
\r
227 canned_comments = []
\r
228 for comment in settings.CANNED_COMMENTS:
\r
229 t = Template(smart_unicode(comment))
\r
232 'settings' : settings,
\r
234 canned_comments.append(t.render(c))
\r
236 total = len(all_comments)
\r
238 'comments': comments,
\r
239 'canned_comments': canned_comments,
\r
241 'can_comment': user.can_comment(post),
\r
242 'max_length': settings.FORM_MAX_COMMENT_BODY,
\r
243 'min_length': settings.FORM_MIN_COMMENT_BODY,
\r
244 'show_gravatar': settings.FORM_GRAVATAR_IN_COMMENTS,
\r
245 'showing': showing,
\r
247 'more_comments_count' : int(total - showing),
\r
248 'show_latest_comments_first' : settings.SHOW_LATEST_COMMENTS_FIRST,
\r
252 @register.inclusion_tag('node/comments.html')
\r
253 def comments(post, user):
\r
254 return _comments(post, user)
\r
256 @register.inclusion_tag("node/contributors_info.html", takes_context=True)
\r
257 def contributors_info(context, node, verb=None):
\r
259 'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or (
\r
260 (node.node_type == "answer") and _("answered") or _("posted"))),
\r
262 'context' : context
\r
265 @register.inclusion_tag("node/reviser_info.html")
\r
266 def reviser_info(revision):
\r
267 return {'revision': revision}
\r