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
19 register = template.Library()
21 GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
22 'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
23 '?s=%(size)s&d=identicon&r=PG" '
24 'alt="%(username)s\'s gravatar image" />')
27 def gravatar(user, size):
29 Creates an ``<img>`` for a user's Gravatar with a given size.
31 This tag can accept a User object, or a dict containing the
35 gravatar = user['gravatar']
36 username = user['username']
37 except (TypeError, AttributeError, KeyError):
38 gravatar = user.gravatar
39 username = user.username
40 return mark_safe(GRAVATAR_TEMPLATE % {
42 'gravatar_hash': gravatar,
43 'username': template.defaultfilters.urlencode(username),
49 def tag_font_size(max_size, min_size, current_size):
51 do a logarithmic mapping calcuation for a proper size for tagging cloud
52 Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
54 #avoid invalid calculation
58 weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
61 return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
64 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
65 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
66 NUM_PAGES_OUTSIDE_RANGE = 1
68 @register.inclusion_tag("paginator.html")
69 def cnprog_paginator(context):
72 Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
74 if (context["is_paginated"]):
75 " Initialize variables "
76 in_leading_range = in_trailing_range = False
77 pages_outside_leading_range = pages_outside_trailing_range = range(0)
79 if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
80 in_leading_range = in_trailing_range = True
81 page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
82 elif (context["page"] <= LEADING_PAGE_RANGE):
83 in_leading_range = True
84 page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
85 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
86 elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
87 in_trailing_range = True
88 page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
89 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
91 page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
92 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
93 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
95 extend_url = context.get('extend_url', '')
97 "base_url": context["base_url"],
98 "is_paginated": context["is_paginated"],
99 "previous": context["previous"],
100 "has_previous": context["has_previous"],
101 "next": context["next"],
102 "has_next": context["has_next"],
103 "page": context["page"],
104 "pages": context["pages"],
105 "page_numbers": page_numbers,
106 "in_leading_range" : in_leading_range,
107 "in_trailing_range" : in_trailing_range,
108 "pages_outside_leading_range": pages_outside_leading_range,
109 "pages_outside_trailing_range": pages_outside_trailing_range,
110 "extend_url" : extend_url
113 @register.inclusion_tag("pagesize.html")
114 def cnprog_pagesize(context):
116 display the pagesize selection boxes for paginator
118 if (context["is_paginated"]):
120 "base_url": context["base_url"],
121 "pagesize" : context["pagesize"],
122 "is_paginated": context["is_paginated"]
127 def get_score_badge(user):
128 BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
130 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
131 '<span class="badge1">●</span>'
132 '<span class="badgecount">%(gold)s</span>'
135 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
136 '<span class="silver">●</span>'
137 '<span class="badgecount">%(silver)s</span>'
140 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
141 '<span class="bronze">●</span>'
142 '<span class="badgecount">%(bronze)s</span>'
144 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
145 return mark_safe(BADGE_TEMPLATE % {
146 'reputation' : user.reputation,
148 'silver' : user.silver,
149 'bronze' : user.bronze,
150 'badgesword' : _('badges'),
151 'reputationword' : _('reputation points'),
155 def get_score_badge_by_details(rep, gold, silver, bronze):
156 BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
158 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
159 '<span class="badge1">●</span>'
160 '<span class="badgecount">%(gold)s</span>'
163 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
164 '<span class="badge2">●</span>'
165 '<span class="badgecount">%(silver)s</span>'
168 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
169 '<span class="badge3">●</span>'
170 '<span class="badgecount">%(bronze)s</span>'
172 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
173 return mark_safe(BADGE_TEMPLATE % {
178 'repword' : _('reputation points'),
179 'badgeword' : _('badges'),
183 def get_age(birthday):
184 current_time = datetime.datetime(*time.localtime()[0:6])
186 month = birthday.month
188 diff = current_time - datetime.datetime(year,month,day,0,0,0)
189 return diff.days / 365
192 def get_total_count(up_count, down_count):
193 return up_count + down_count
196 def format_number(value):
197 strValue = str(value)
198 if len(strValue) <= 3:
202 pattern = re.compile('(-?\d+)(\d{3})')
203 m = re.match(pattern, strValue)
207 result = ',' + second + result
208 strValue = first + ',' + second
209 m = re.match(pattern, strValue)
210 return first + result
213 def convert2tagname_list(question):
214 question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
218 def diff_date(date, limen=2):
222 now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
225 hours = int(diff.seconds/3600)
226 minutes = int(diff.seconds/60)
229 if date.year == now.year:
230 return date.strftime("%b %d at %H:%M")
232 return date.strftime("%b %d '%y at %H:%M")
234 return _('2 days ago')
236 return _('yesterday')
238 return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
240 return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
243 def get_latest_changed_timestamp():
245 from time import localtime, strftime
247 root = settings.SITE_SRC_ROOT
251 '%s/templates' % root,
253 stamp = (path.getmtime(d) for d in dir)
255 timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
262 url = skins.find_media_source(url)
264 url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
265 return posixpath.normpath(url)
267 class ItemSeparatorNode(template.Node):
268 def __init__(self,separator):
269 sep = separator.strip()
270 if sep[0] == sep[-1] and sep[0] in ('\'','"'):
273 raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
275 def render(self,context):
278 class JoinItemListNode(template.Node):
279 def __init__(self,separator=ItemSeparatorNode("''"), items=()):
280 self.separator = separator
282 def render(self,context):
284 empty_re = re.compile(r'^\s*$')
285 for item in self.items:
286 bit = item.render(context)
287 if not empty_re.search(bit):
289 return self.separator.render(context).join(out)
291 @register.tag(name="joinitems")
292 def joinitems(parser,token):
294 tagname,junk,sep_token = token.split_contents()
296 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
298 sep_node = ItemSeparatorNode(sep_token)
300 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
303 nodelist.append(parser.parse(('separator','endjoinitems')))
304 next = parser.next_token()
305 if next.contents == 'endjoinitems':
308 return JoinItemListNode(separator=sep_node,items=nodelist)
310 class BlockMediaUrlNode(template.Node):
311 def __init__(self,nodelist):
312 self.items = nodelist
313 def render(self,context):
314 prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
318 for item in self.items:
319 url += item.render(context)
321 url = skins.find_media_source(url)
323 out = posixpath.normpath(url)
324 return out.replace(' ','')
326 @register.tag(name='blockmedia')
327 def blockmedia(parser,token):
329 tagname = token.split_contents()
331 raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
334 nodelist.append(parser.parse(('endblockmedia')))
335 next = parser.next_token()
336 if next.contents == 'endblockmedia':
338 return BlockMediaUrlNode(nodelist)
340 class FullUrlNode(template.Node):
341 def __init__(self, default_node):
342 self.default_node = default_node
344 def render(self, context):
345 domain = settings.APP_URL
346 #protocol = getattr(settings, "PROTOCOL", "http")
347 path = self.default_node.render(context)
348 return "%s%s" % (domain, path)
350 @register.tag(name='fullurl')
351 def fullurl(parser, token):
352 default_node = default_url(parser, token)
353 return FullUrlNode(default_node)
357 domain = settings.APP_URL
358 #protocol = getattr(settings, "PROTOCOL", "http")
360 return "%s%s" % (domain, path)
362 class UserVarNode(template.Node):
363 def __init__(self, tokens):
366 def render(self, context):
367 return "{{ %s }}" % self.tokens
369 @register.tag(name='user_var')
370 def user_var(parser, token):
371 tokens = " ".join(token.split_contents()[1:])
372 return UserVarNode(tokens)
375 class SimpleVarNode(template.Node):
376 def __init__(self, name, value):
378 self.value = template.Variable(value)
380 def render(self, context):
381 context[self.name] = self.value.resolve(context)
384 class BlockVarNode(template.Node):
385 def __init__(self, name, block):
389 def render(self, context):
390 source = self.block.render(context)
391 context[self.name] = source.strip()
395 @register.tag(name='var')
396 def do_var(parser, token):
397 tokens = token.split_contents()[1:]
399 if not len(tokens) or not re.match('^\w+$', tokens[0]):
400 raise template.TemplateSyntaxError("Expected variable name")
403 nodelist = parser.parse(('endvar',))
404 parser.delete_first_token()
405 return BlockVarNode(tokens[0], nodelist)
406 elif len(tokens) == 3:
407 return SimpleVarNode(tokens[0], tokens[2])
409 raise template.TemplateSyntaxError("Invalid number of arguments")
411 class DeclareNode(template.Node):
412 dec_re = re.compile('^\s*(\w+)\s*(:?=)\s*(.*)$')
414 def __init__(self, block):
417 def render(self, context):
418 source = self.block.render(context)
420 for line in source.splitlines():
421 m = self.dec_re.search(line)
422 if m and m.group(2) == '=':
423 context[m.group(1).strip()] = m.group(3).strip()
424 elif m and m.group(2) == ':=':
425 context[m.group(1).strip()] = template.Variable(m.group(3).strip()).resolve(context)
428 @register.tag(name='declare')
429 def do_declare(parser, token):
430 nodelist = parser.parse(('enddeclare',))
431 parser.delete_first_token()
432 return DeclareNode(nodelist)