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 Creates an ``<img>`` for a user's Gravatar with a given size.
33 This tag can accept a User object, or a dict containing the
37 gravatar = user['gravatar']
38 username = user['username']
39 except (TypeError, AttributeError, KeyError):
40 gravatar = user.gravatar
41 username = user.username
42 return mark_safe(GRAVATAR_TEMPLATE % {
44 'gravatar_hash': gravatar,
45 'username': template.defaultfilters.urlencode(username),
51 #def tag_font_size(max_size, min_size, current_size):
53 # do a logarithmic mapping calcuation for a proper size for tagging cloud
54 # Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
56 # #avoid invalid calculation
57 # if current_size == 0:
60 # weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
63 # return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
66 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
67 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
68 NUM_PAGES_OUTSIDE_RANGE = 1
70 @register.inclusion_tag("paginator.html")
71 def cnprog_paginator(context):
74 Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
76 if (context["is_paginated"]):
77 " Initialize variables "
78 in_leading_range = in_trailing_range = False
79 pages_outside_leading_range = pages_outside_trailing_range = range(0)
81 if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
82 in_leading_range = in_trailing_range = True
83 page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
84 elif (context["page"] <= LEADING_PAGE_RANGE):
85 in_leading_range = True
86 page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
87 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
88 elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
89 in_trailing_range = True
90 page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
91 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
93 page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
94 pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
95 pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
97 extend_url = context.get('extend_url', '')
99 "base_url": context["base_url"],
100 "is_paginated": context["is_paginated"],
101 "previous": context["previous"],
102 "has_previous": context["has_previous"],
103 "next": context["next"],
104 "has_next": context["has_next"],
105 "page": context["page"],
106 "pages": context["pages"],
107 "page_numbers": page_numbers,
108 "in_leading_range" : in_leading_range,
109 "in_trailing_range" : in_trailing_range,
110 "pages_outside_leading_range": pages_outside_leading_range,
111 "pages_outside_trailing_range": pages_outside_trailing_range,
112 "extend_url" : extend_url
115 @register.inclusion_tag("pagesize.html")
116 def cnprog_pagesize(context):
118 display the pagesize selection boxes for paginator
120 if (context["is_paginated"]):
122 "base_url": context["base_url"],
123 "pagesize" : context["pagesize"],
124 "is_paginated": context["is_paginated"]
129 def get_score_badge(user):
130 BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
132 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
133 '<span class="badge1">●</span>'
134 '<span class="badgecount">%(gold)s</span>'
137 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
138 '<span class="silver">●</span>'
139 '<span class="badgecount">%(silver)s</span>'
142 BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
143 '<span class="bronze">●</span>'
144 '<span class="badgecount">%(bronze)s</span>'
146 BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
147 return mark_safe(BADGE_TEMPLATE % {
148 'reputation' : user.reputation,
150 'silver' : user.silver,
151 'bronze' : user.bronze,
152 'badgesword' : _('badges'),
153 'reputationword' : _('reputation points'),
156 #@register.simple_tag
157 #def get_score_badge_by_details(rep, gold, silver, bronze):
158 # BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
160 # BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
161 # '<span class="badge1">●</span>'
162 # '<span class="badgecount">%(gold)s</span>'
165 # BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
166 # '<span class="badge2">●</span>'
167 # '<span class="badgecount">%(silver)s</span>'
170 # BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
171 # '<span class="badge3">●</span>'
172 # '<span class="badgecount">%(bronze)s</span>'
174 # BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
175 # return mark_safe(BADGE_TEMPLATE % {
176 # 'reputation' : rep,
180 # 'repword' : _('reputation points'),
181 # 'badgeword' : _('badges'),
185 def get_age(birthday):
186 current_time = datetime.datetime(*time.localtime()[0:6])
188 month = birthday.month
190 diff = current_time - datetime.datetime(year,month,day,0,0,0)
191 return diff.days / 365
193 #@register.simple_tag
194 #def get_total_count(up_count, down_count):
195 # return up_count + down_count
197 #@register.simple_tag
198 #def format_number(value):
199 # strValue = str(value)
200 # if len(strValue) <= 3:
204 # pattern = re.compile('(-?\d+)(\d{3})')
205 # m = re.match(pattern, strValue)
208 # second = m.group(2)
209 # result = ',' + second + result
210 # strValue = first + ',' + second
211 # m = re.match(pattern, strValue)
212 # return first + result
214 #@register.simple_tag
215 #def convert2tagname_list(question):
216 # question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
220 def diff_date(date, limen=2):
224 now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
227 hours = int(diff.seconds/3600)
228 minutes = int(diff.seconds/60)
231 if date.year == now.year:
232 return date.strftime("%b %d at %H:%M")
234 return date.strftime("%b %d '%y at %H:%M")
236 return _('2 days ago')
238 return _('yesterday')
240 return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
242 return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
244 #@register.simple_tag
245 #def get_latest_changed_timestamp():
247 # from time import localtime, strftime
248 # from os import path
249 # root = settings.SITE_SRC_ROOT
253 # '%s/templates' % root,
255 # stamp = (path.getmtime(d) for d in dir)
256 # latest = max(stamp)
257 # timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
264 url = skins.find_media_source(url)
266 url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
267 return posixpath.normpath(url)
269 class ItemSeparatorNode(template.Node):
270 def __init__(self,separator):
271 sep = separator.strip()
272 if sep[0] == sep[-1] and sep[0] in ('\'','"'):
275 raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
277 def render(self,context):
280 #class JoinItemListNode(template.Node):
281 # def __init__(self,separator=ItemSeparatorNode("''"), items=()):
282 # self.separator = separator
284 # def render(self,context):
286 # empty_re = re.compile(r'^\s*$')
287 # for item in self.items:
288 # bit = item.render(context)
289 # if not empty_re.search(bit):
291 # return self.separator.render(context).join(out)
293 #@register.tag(name="joinitems")
294 #def joinitems(parser,token):
296 # tagname,junk,sep_token = token.split_contents()
298 # raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
299 # if junk == 'using':
300 # sep_node = ItemSeparatorNode(sep_token)
302 # raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
305 # nodelist.append(parser.parse(('separator','endjoinitems')))
306 # next = parser.next_token()
307 # if next.contents == 'endjoinitems':
310 # return JoinItemListNode(separator=sep_node,items=nodelist)
312 class BlockMediaUrlNode(template.Node):
313 def __init__(self,nodelist):
314 self.items = nodelist
315 def render(self,context):
316 prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
320 for item in self.items:
321 url += item.render(context)
323 url = skins.find_media_source(url)
325 out = posixpath.normpath(url)
326 return out.replace(' ','')
328 @register.tag(name='blockmedia')
329 def blockmedia(parser,token):
331 tagname = token.split_contents()
333 raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
336 nodelist.append(parser.parse(('endblockmedia')))
337 next = parser.next_token()
338 if next.contents == 'endblockmedia':
340 return BlockMediaUrlNode(nodelist)
345 domain = settings.APP_URL
346 #protocol = getattr(settings, "PROTOCOL", "http")
348 return "%s%s" % (domain, path)
351 class SimpleVarNode(template.Node):
352 def __init__(self, name, value):
354 self.value = template.Variable(value)
356 def render(self, context):
357 context[self.name] = self.value.resolve(context)
360 class BlockVarNode(template.Node):
361 def __init__(self, name, block):
365 def render(self, context):
366 source = self.block.render(context)
367 context[self.name] = source.strip()
371 @register.tag(name='var')
372 def do_var(parser, token):
373 tokens = token.split_contents()[1:]
375 if not len(tokens) or not re.match('^\w+$', tokens[0]):
376 raise template.TemplateSyntaxError("Expected variable name")
379 nodelist = parser.parse(('endvar',))
380 parser.delete_first_token()
381 return BlockVarNode(tokens[0], nodelist)
382 elif len(tokens) == 3:
383 return SimpleVarNode(tokens[0], tokens[2])
385 raise template.TemplateSyntaxError("Invalid number of arguments")
387 class DeclareNode(template.Node):
388 dec_re = re.compile('^\s*(\w+)\s*(:?=)\s*(.*)$')
390 def __init__(self, block):
393 def render(self, context):
394 source = self.block.render(context)
396 for line in source.splitlines():
397 m = self.dec_re.search(line)
399 clist = list(context)
405 d['reverse'] = reverse
409 context[m.group(1).strip()] = eval(m.group(3).strip(), d)
411 logging.error("Error in declare tag, when evaluating: %s" % m.group(3).strip())
415 @register.tag(name='declare')
416 def do_declare(parser, token):
417 nodelist = parser.parse(('enddeclare',))
418 parser.delete_first_token()
419 return DeclareNode(nodelist)