8 from django import template
9 from django.utils.encoding import smart_unicode
10 from django.utils.safestring import mark_safe
11 from forum.const import *
12 from forum.models import Question, Answer, QuestionRevision, AnswerRevision, NodeRevision
13 from django.utils.translation import ugettext as _
14 from django.utils.translation import ungettext
15 from django.utils import simplejson
16 from django.conf import settings
17 from django.template.defaulttags import url as default_url
18 from forum import skins
20 register = template.Library()
22 GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
23 'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
24 '?s=%(size)s&d=identicon&r=PG" '
25 'alt="%(username)s\'s gravatar image" />')
28 def gravatar(user, size):
30 Creates an ``<img>`` for a user's Gravatar with a given size.
32 This tag can accept a User object, or a dict containing the
36 gravatar = user['gravatar']
37 username = user['username']
38 except (TypeError, AttributeError, KeyError):
39 gravatar = user.gravatar
40 username = user.username
41 return mark_safe(GRAVATAR_TEMPLATE % {
43 'gravatar_hash': gravatar,
44 'username': template.defaultfilters.urlencode(username),
50 def tag_font_size(max_size, min_size, current_size):
52 do a logarithmic mapping calcuation for a proper size for tagging cloud
53 Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
55 #avoid invalid calculation
59 weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
62 return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
65 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
66 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
67 NUM_PAGES_OUTSIDE_RANGE = 1
69 @register.inclusion_tag("paginator.html")
70 def cnprog_paginator(context):
73 Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
75 if (context["is_paginated"]):
76 " Initialize variables "
77 in_leading_range = in_trailing_range = False
78 pages_outside_leading_range = pages_outside_trailing_range = range(0)
80 if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
81 in_leading_range = in_trailing_range = True
82 page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
83 elif (context["page"] <= LEADING_PAGE_RANGE):
84 in_leading_range = True
85 page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
86 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
87 elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
88 in_trailing_range = True
89 page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
90 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
92 page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
93 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
94 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
96 extend_url = context.get('extend_url', '')
98 "base_url": context["base_url"],
99 "is_paginated": context["is_paginated"],
100 "previous": context["previous"],
101 "has_previous": context["has_previous"],
102 "next": context["next"],
103 "has_next": context["has_next"],
104 "page": context["page"],
105 "pages": context["pages"],
106 "page_numbers": page_numbers,
107 "in_leading_range" : in_leading_range,
108 "in_trailing_range" : in_trailing_range,
109 "pages_outside_leading_range": pages_outside_leading_range,
110 "pages_outside_trailing_range": pages_outside_trailing_range,
111 "extend_url" : extend_url
114 @register.inclusion_tag("pagesize.html")
115 def cnprog_pagesize(context):
117 display the pagesize selection boxes for paginator
119 if (context["is_paginated"]):
121 "base_url": context["base_url"],
122 "pagesize" : context["pagesize"],
123 "is_paginated": context["is_paginated"]
126 @register.inclusion_tag("post_contributor_info.html")
127 def post_contributor_info(post,contributor_type='original_author'):
128 """contributor_type: original_author|last_updater
130 if isinstance(post,Question):
131 post_type = 'question'
132 elif isinstance(post,Answer):
134 elif isinstance(post,(AnswerRevision, QuestionRevision, NodeRevision)):
135 post_type = 'revision'
138 'post_type':post_type,
139 'wiki_on':settings.WIKI_ON,
140 'contributor_type':contributor_type
144 def get_score_badge(user):
145 BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
147 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
148 '<span class="badge1">●</span>'
149 '<span class="badgecount">%(gold)s</span>'
152 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
153 '<span class="silver">●</span>'
154 '<span class="badgecount">%(silver)s</span>'
157 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
158 '<span class="bronze">●</span>'
159 '<span class="badgecount">%(bronze)s</span>'
161 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
162 return mark_safe(BADGE_TEMPLATE % {
163 'reputation' : user.reputation,
165 'silver' : user.silver,
166 'bronze' : user.bronze,
167 'badgesword' : _('badges'),
168 'reputationword' : _('reputation points'),
172 def get_score_badge_by_details(rep, gold, silver, bronze):
173 BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
175 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
176 '<span class="badge1">●</span>'
177 '<span class="badgecount">%(gold)s</span>'
180 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
181 '<span class="badge2">●</span>'
182 '<span class="badgecount">%(silver)s</span>'
185 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
186 '<span class="badge3">●</span>'
187 '<span class="badgecount">%(bronze)s</span>'
189 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
190 return mark_safe(BADGE_TEMPLATE % {
195 'repword' : _('reputation points'),
196 'badgeword' : _('badges'),
200 def get_age(birthday):
201 current_time = datetime.datetime(*time.localtime()[0:6])
203 month = birthday.month
205 diff = current_time - datetime.datetime(year,month,day,0,0,0)
206 return diff.days / 365
209 def get_total_count(up_count, down_count):
210 return up_count + down_count
213 def format_number(value):
214 strValue = str(value)
215 if len(strValue) <= 3:
219 pattern = re.compile('(-?\d+)(\d{3})')
220 m = re.match(pattern, strValue)
224 result = ',' + second + result
225 strValue = first + ',' + second
226 m = re.match(pattern, strValue)
227 return first + result
230 def convert2tagname_list(question):
231 question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
235 def diff_date(date, limen=2):
239 now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
242 hours = int(diff.seconds/3600)
243 minutes = int(diff.seconds/60)
246 if date.year == now.year:
247 return date.strftime("%b %d at %H:%M")
249 return date.strftime("%b %d '%y at %H:%M")
251 return _('2 days ago')
253 return _('yesterday')
255 return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
257 return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
260 def get_latest_changed_timestamp():
262 from time import localtime, strftime
264 root = settings.SITE_SRC_ROOT
268 '%s/templates' % root,
270 stamp = (path.getmtime(d) for d in dir)
272 timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
279 url = skins.find_media_source(url)
281 url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
282 return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
284 class ItemSeparatorNode(template.Node):
285 def __init__(self,separator):
286 sep = separator.strip()
287 if sep[0] == sep[-1] and sep[0] in ('\'','"'):
290 raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
292 def render(self,context):
295 class JoinItemListNode(template.Node):
296 def __init__(self,separator=ItemSeparatorNode("''"), items=()):
297 self.separator = separator
299 def render(self,context):
301 empty_re = re.compile(r'^\s*$')
302 for item in self.items:
303 bit = item.render(context)
304 if not empty_re.search(bit):
306 return self.separator.render(context).join(out)
308 @register.tag(name="joinitems")
309 def joinitems(parser,token):
311 tagname,junk,sep_token = token.split_contents()
313 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
315 sep_node = ItemSeparatorNode(sep_token)
317 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
320 nodelist.append(parser.parse(('separator','endjoinitems')))
321 next = parser.next_token()
322 if next.contents == 'endjoinitems':
325 return JoinItemListNode(separator=sep_node,items=nodelist)
327 class BlockMediaUrlNode(template.Node):
328 def __init__(self,nodelist):
329 self.items = nodelist
330 def render(self,context):
331 prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
335 for item in self.items:
336 url += item.render(context)
338 url = skins.find_media_source(url)
340 out = posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
341 return out.replace(' ','')
343 @register.tag(name='blockmedia')
344 def blockmedia(parser,token):
346 tagname = token.split_contents()
348 raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
351 nodelist.append(parser.parse(('endblockmedia')))
352 next = parser.next_token()
353 if next.contents == 'endblockmedia':
355 return BlockMediaUrlNode(nodelist)
357 class FullUrlNode(template.Node):
358 def __init__(self, default_node):
359 self.default_node = default_node
361 def render(self, context):
362 domain = settings.APP_URL
363 #protocol = getattr(settings, "PROTOCOL", "http")
364 path = self.default_node.render(context)
365 return "%s%s" % (domain, path)
367 @register.tag(name='fullurl')
368 def fullurl(parser, token):
369 default_node = default_url(parser, token)
370 return FullUrlNode(default_node)
374 domain = settings.APP_URL
375 #protocol = getattr(settings, "PROTOCOL", "http")
377 return "%s%s" % (domain, path)
379 class UserVarNode(template.Node):
380 def __init__(self, tokens):
383 def render(self, context):
384 return "{{ %s }}" % self.tokens
386 @register.tag(name='user_var')
387 def user_var(parser, token):
388 tokens = " ".join(token.split_contents()[1:])
389 return UserVarNode(tokens)
392 class VariablesNode(template.Node):
393 def __init__(self, nodelist, var_name):
394 self.nodelist = nodelist
395 self.var_name = var_name
397 def render(self, context):
398 source = self.nodelist.render(context)
399 context[self.var_name] = simplejson.loads(source)
402 @register.tag(name='var')
403 def do_variables(parser, token):
405 tag_name, arg = token.contents.split(None, 1)
407 msg = '"%s" tag requires arguments' % token.contents.split()[0]
408 raise template.TemplateSyntaxError(msg)
409 m = re.search(r'as (\w+)', arg)
411 var_name, = m.groups()
413 msg = '"%s" tag had invalid arguments' % tag_name
414 raise template.TemplateSyntaxError(msg)
416 nodelist = parser.parse(('endvar',))
417 parser.delete_first_token()
418 return VariablesNode(nodelist, var_name)