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
13 from django.utils.translation import ugettext as _
14 from django.utils.translation import ungettext
15 from django.conf import settings
16 from forum import skins
18 register = template.Library()
20 GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
21 'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
22 '?s=%(size)s&d=identicon&r=PG" '
23 'alt="%(username)s\'s gravatar image" />')
26 def gravatar(user, size):
28 Creates an ``<img>`` for a user's Gravatar with a given size.
30 This tag can accept a User object, or a dict containing the
34 gravatar = user['gravatar']
35 username = user['username']
36 except (TypeError, AttributeError, KeyError):
37 gravatar = user.gravatar
38 username = user.username
39 return mark_safe(GRAVATAR_TEMPLATE % {
41 'gravatar_hash': gravatar,
42 'username': template.defaultfilters.urlencode(username),
48 def tag_font_size(max_size, min_size, current_size):
50 do a logarithmic mapping calcuation for a proper size for tagging cloud
51 Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
53 #avoid invalid calculation
57 weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
60 return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
63 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
64 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
65 NUM_PAGES_OUTSIDE_RANGE = 1
67 @register.inclusion_tag("paginator.html")
68 def cnprog_paginator(context):
71 Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
73 if (context["is_paginated"]):
74 " Initialize variables "
75 in_leading_range = in_trailing_range = False
76 pages_outside_leading_range = pages_outside_trailing_range = range(0)
78 if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
79 in_leading_range = in_trailing_range = True
80 page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
81 elif (context["page"] <= LEADING_PAGE_RANGE):
82 in_leading_range = True
83 page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
84 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
85 elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
86 in_trailing_range = True
87 page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
88 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
90 page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
91 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
92 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
94 extend_url = context.get('extend_url', '')
96 "base_url": context["base_url"],
97 "is_paginated": context["is_paginated"],
98 "previous": context["previous"],
99 "has_previous": context["has_previous"],
100 "next": context["next"],
101 "has_next": context["has_next"],
102 "page": context["page"],
103 "pages": context["pages"],
104 "page_numbers": page_numbers,
105 "in_leading_range" : in_leading_range,
106 "in_trailing_range" : in_trailing_range,
107 "pages_outside_leading_range": pages_outside_leading_range,
108 "pages_outside_trailing_range": pages_outside_trailing_range,
109 "extend_url" : extend_url
112 @register.inclusion_tag("pagesize.html")
113 def cnprog_pagesize(context):
115 display the pagesize selection boxes for paginator
117 if (context["is_paginated"]):
119 "base_url": context["base_url"],
120 "pagesize" : context["pagesize"],
121 "is_paginated": context["is_paginated"]
124 @register.inclusion_tag("post_contributor_info.html")
125 def post_contributor_info(post,contributor_type='original_author'):
126 """contributor_type: original_author|last_updater
128 if isinstance(post,Question):
129 post_type = 'question'
130 elif isinstance(post,Answer):
132 elif isinstance(post,AnswerRevision) or isinstance(post,QuestionRevision):
133 post_type = 'revision'
136 'post_type':post_type,
137 'wiki_on':settings.WIKI_ON,
138 'contributor_type':contributor_type
142 def get_score_badge(user):
143 BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
145 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
146 '<span class="badge1">●</span>'
147 '<span class="badgecount">%(gold)s</span>'
150 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
151 '<span class="silver">●</span>'
152 '<span class="badgecount">%(silver)s</span>'
155 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
156 '<span class="bronze">●</span>'
157 '<span class="badgecount">%(bronze)s</span>'
159 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
160 return mark_safe(BADGE_TEMPLATE % {
161 'reputation' : user.reputation,
163 'silver' : user.silver,
164 'bronze' : user.bronze,
165 'badgesword' : _('badges'),
166 'reputationword' : _('reputation points'),
170 def get_score_badge_by_details(rep, gold, silver, bronze):
171 BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
173 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
174 '<span class="badge1">●</span>'
175 '<span class="badgecount">%(gold)s</span>'
178 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
179 '<span class="badge2">●</span>'
180 '<span class="badgecount">%(silver)s</span>'
183 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
184 '<span class="badge3">●</span>'
185 '<span class="badgecount">%(bronze)s</span>'
187 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
188 return mark_safe(BADGE_TEMPLATE % {
193 'repword' : _('reputation points'),
194 'badgeword' : _('badges'),
198 def get_user_vote_image(dic, key, arrow):
200 if int(dic[key]) == int(arrow):
205 def get_age(birthday):
206 current_time = datetime.datetime(*time.localtime()[0:6])
208 month = birthday.month
210 diff = current_time - datetime.datetime(year,month,day,0,0,0)
211 return diff.days / 365
214 def get_total_count(up_count, down_count):
215 return up_count + down_count
218 def format_number(value):
219 strValue = str(value)
220 if len(strValue) <= 3:
224 pattern = re.compile('(-?\d+)(\d{3})')
225 m = re.match(pattern, strValue)
229 result = ',' + second + result
230 strValue = first + ',' + second
231 m = re.match(pattern, strValue)
232 return first + result
235 def convert2tagname_list(question):
236 question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
240 def diff_date(date, limen=2):
241 now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
244 hours = int(diff.seconds/3600)
245 minutes = int(diff.seconds/60)
248 if date.year == now.year:
249 return date.strftime("%b %d at %H:%M")
251 return date.strftime("%b %d '%y at %H:%M")
253 return _('2 days ago')
255 return _('yesterday')
257 return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
259 return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
262 def get_latest_changed_timestamp():
264 from time import localtime, strftime
266 root = settings.SITE_SRC_ROOT
270 '%s/templates' % root,
272 stamp = (path.getmtime(d) for d in dir)
274 timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
281 url = skins.find_media_source(url)
283 url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
284 return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
286 class ItemSeparatorNode(template.Node):
287 def __init__(self,separator):
288 sep = separator.strip()
289 if sep[0] == sep[-1] and sep[0] in ('\'','"'):
292 raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
294 def render(self,context):
297 class JoinItemListNode(template.Node):
298 def __init__(self,separator=ItemSeparatorNode("''"), items=()):
299 self.separator = separator
301 def render(self,context):
303 empty_re = re.compile(r'^\s*$')
304 for item in self.items:
305 bit = item.render(context)
306 if not empty_re.search(bit):
308 return self.separator.render(context).join(out)
310 @register.tag(name="joinitems")
311 def joinitems(parser,token):
313 tagname,junk,sep_token = token.split_contents()
315 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
317 sep_node = ItemSeparatorNode(sep_token)
319 raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
322 nodelist.append(parser.parse(('separator','endjoinitems')))
323 next = parser.next_token()
324 if next.contents == 'endjoinitems':
327 return JoinItemListNode(separator=sep_node,items=nodelist)
329 class BlockMediaUrlNode(template.Node):
330 def __init__(self,nodelist):
331 self.items = nodelist
332 def render(self,context):
333 prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
337 for item in self.items:
338 url += item.render(context)
340 url = skins.find_media_source(url)
342 out = posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
343 return out.replace(' ','')
345 @register.tag(name='blockmedia')
346 def blockmedia(parser,token):
348 tagname = token.split_contents()
350 raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
353 nodelist.append(parser.parse(('endblockmedia')))
354 next = parser.next_token()
355 if next.contents == 'endblockmedia':
357 return BlockMediaUrlNode(nodelist)