8 from django import template
9 from django.utils.encoding import smart_unicode
10 from django.utils.safestring import mark_safe
11 from forum.models import Question, Answer, QuestionRevision, AnswerRevision, NodeRevision
12 from django.utils.translation import ugettext as _
13 from django.utils.translation import ungettext
14 from django.utils import simplejson
15 from forum import settings
16 from django.template.defaulttags import url as default_url
17 from forum import skins
18 from forum.utils import html
19 from django.core.urlresolvers import reverse
21 register = template.Library()
23 GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
24 'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
25 '?s=%(size)s&d=identicon&r=PG" '
26 'alt="%(username)s\'s gravatar image" />')
29 def gravatar(user, size):
31 gravatar = user['gravatar']
32 username = user['username']
33 except (TypeError, AttributeError, KeyError):
34 gravatar = user.gravatar
35 username = user.username
36 return mark_safe(GRAVATAR_TEMPLATE % {
38 'gravatar_hash': gravatar,
39 'username': template.defaultfilters.urlencode(username),
43 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
44 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
45 NUM_PAGES_OUTSIDE_RANGE = 1
47 @register.inclusion_tag("paginator.html")
48 def cnprog_paginator(context):
51 Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
53 if (context["is_paginated"]):
54 " Initialize variables "
55 in_leading_range = in_trailing_range = False
56 pages_outside_leading_range = pages_outside_trailing_range = range(0)
58 if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
59 in_leading_range = in_trailing_range = True
60 page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
61 elif (context["page"] <= LEADING_PAGE_RANGE):
62 in_leading_range = True
63 page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
64 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
65 elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
66 in_trailing_range = True
67 page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1)
68 if n > 0 and n <= context["pages"]]
69 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
71 page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if
72 n > 0 and n <= context["pages"]]
73 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
74 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
76 extend_url = context.get('extend_url', '')
78 "base_url": context["base_url"],
79 "is_paginated": context["is_paginated"],
80 "previous": context["previous"],
81 "has_previous": context["has_previous"],
82 "next": context["next"],
83 "has_next": context["has_next"],
84 "page": context["page"],
85 "pages": context["pages"],
86 "page_numbers": page_numbers,
87 "in_leading_range" : in_leading_range,
88 "in_trailing_range" : in_trailing_range,
89 "pages_outside_leading_range": pages_outside_leading_range,
90 "pages_outside_trailing_range": pages_outside_trailing_range,
91 "extend_url" : extend_url
94 @register.inclusion_tag("pagesize.html")
95 def cnprog_pagesize(context):
97 display the pagesize selection boxes for paginator
99 if (context["is_paginated"]):
101 "base_url": context["base_url"],
102 "pagesize" : context["pagesize"],
103 "is_paginated": context["is_paginated"]
108 def get_score_badge(user):
109 if user.is_suspended():
110 return _("(suspended)")
112 BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
114 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
115 '<span class="badge1">●</span>'
116 '<span class="badgecount">%(gold)s</span>'
119 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
120 '<span class="silver">●</span>'
121 '<span class="badgecount">%(silver)s</span>'
124 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
125 '<span class="bronze">●</span>'
126 '<span class="badgecount">%(bronze)s</span>'
128 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
129 return mark_safe(BADGE_TEMPLATE % {
130 'reputation' : user.reputation,
132 'silver' : user.silver,
133 'bronze' : user.bronze,
134 'badgesword' : _('badges'),
135 'reputationword' : _('reputation points'),
140 def get_age(birthday):
141 current_time = datetime.datetime(*time.localtime()[0:6])
143 month = birthday.month
145 diff = current_time - datetime.datetime(year, month, day, 0, 0, 0)
146 return diff.days / 365
149 def diff_date(date, limen=2):
153 now = datetime.datetime.now()
156 hours = int(diff.seconds/3600)
157 minutes = int(diff.seconds/60)
160 if date.year == now.year:
161 return date.strftime("%b %d at %H:%M")
163 return date.strftime("%b %d '%y at %H:%M")
165 return _('2 days ago')
167 return _('yesterday')
169 return ungettext('%(hr)d hour ago', '%(hr)d hours ago', hours) % {'hr':hours}
171 return ungettext('%(min)d min ago', '%(min)d mins ago', minutes) % {'min':minutes}
175 url = skins.find_media_source(url)
177 url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
178 return posixpath.normpath(url)
180 class ItemSeparatorNode(template.Node):
181 def __init__(self, separator):
182 sep = separator.strip()
183 if sep[0] == sep[-1] and sep[0] in ('\'', '"'):
186 raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
189 def render(self, context):
192 class BlockMediaUrlNode(template.Node):
193 def __init__(self, nodelist):
194 self.items = nodelist
196 def render(self, context):
197 prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
201 for item in self.items:
202 url += item.render(context)
204 url = skins.find_media_source(url)
206 out = posixpath.normpath(url)
207 return out.replace(' ', '')
209 @register.tag(name='blockmedia')
210 def blockmedia(parser, token):
212 tagname = token.split_contents()
214 raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
217 nodelist.append(parser.parse(('endblockmedia')))
218 next = parser.next_token()
219 if next.contents == 'endblockmedia':
221 return BlockMediaUrlNode(nodelist)
226 domain = settings.APP_URL
227 #protocol = getattr(settings, "PROTOCOL", "http")
229 return "%s%s" % (domain, path)
232 class SimpleVarNode(template.Node):
233 def __init__(self, name, value):
235 self.value = template.Variable(value)
237 def render(self, context):
238 context[self.name] = self.value.resolve(context)
241 class BlockVarNode(template.Node):
242 def __init__(self, name, block):
246 def render(self, context):
247 source = self.block.render(context)
248 context[self.name] = source.strip()
252 @register.tag(name='var')
253 def do_var(parser, token):
254 tokens = token.split_contents()[1:]
256 if not len(tokens) or not re.match('^\w+$', tokens[0]):
257 raise template.TemplateSyntaxError("Expected variable name")
260 nodelist = parser.parse(('endvar',))
261 parser.delete_first_token()
262 return BlockVarNode(tokens[0], nodelist)
263 elif len(tokens) == 3:
264 return SimpleVarNode(tokens[0], tokens[2])
266 raise template.TemplateSyntaxError("Invalid number of arguments")
268 class DeclareNode(template.Node):
269 dec_re = re.compile('^\s*(\w+)\s*(:?=)\s*(.*)$')
271 def __init__(self, block):
274 def render(self, context):
275 source = self.block.render(context)
277 for line in source.splitlines():
278 m = self.dec_re.search(line)
280 clist = list(context)
286 d['reverse'] = reverse
290 context[m.group(1).strip()] = eval(m.group(3).strip(), d)
292 logging.error("Error in declare tag, when evaluating: %s" % m.group(3).strip())
296 @register.tag(name='declare')
297 def do_declare(parser, token):
298 nodelist = parser.parse(('enddeclare',))
299 parser.delete_first_token()
300 return DeclareNode(nodelist)