]> git.openstreetmap.org Git - osqa.git/blob - forum/templatetags/extra_tags.py
26c52b8d0408b2e6c55f384bc6b002c93d26c776
[osqa.git] / forum / templatetags / extra_tags.py
1 import time
2 import os
3 import posixpath
4 import datetime
5 import math
6 import re
7 import logging
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
17
18 register = template.Library()
19
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&amp;d=identicon&amp;r=PG" '
23                      'alt="%(username)s\'s gravatar image" />')
24
25 @register.simple_tag
26 def gravatar(user, size):
27     """
28     Creates an ``<img>`` for a user's Gravatar with a given size.
29
30     This tag can accept a User object, or a dict containing the
31     appropriate values.
32     """
33     try:
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 % {
40         'size': size,
41         'gravatar_hash': gravatar,
42         'username': template.defaultfilters.urlencode(username),
43     })
44
45 MAX_FONTSIZE = 18
46 MIN_FONTSIZE = 12
47 @register.simple_tag
48 def tag_font_size(max_size, min_size, current_size):
49     """
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/
52     """
53     #avoid invalid calculation
54     if current_size == 0:
55         current_size = 1
56     try:
57         weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
58     except:
59         weight = 0
60     return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
61
62
63 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
64 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
65 NUM_PAGES_OUTSIDE_RANGE = 1
66 ADJACENT_PAGES = 2
67 @register.inclusion_tag("paginator.html")
68 def cnprog_paginator(context):
69     """
70     custom paginator tag
71     Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
72     """
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)
77
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)]
89         else:
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)]
93
94         extend_url = context.get('extend_url', '')
95         return {
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
110         }
111
112 @register.inclusion_tag("pagesize.html")
113 def cnprog_pagesize(context):
114     """
115     display the pagesize selection boxes for paginator
116     """
117     if (context["is_paginated"]):
118         return {
119             "base_url": context["base_url"],
120             "pagesize" : context["pagesize"],
121             "is_paginated": context["is_paginated"]
122         }
123
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
127     """
128     if isinstance(post,Question):
129         post_type = 'question'
130     elif isinstance(post,Answer):
131         post_type = 'answer'
132     elif isinstance(post,AnswerRevision) or isinstance(post,QuestionRevision):
133         post_type = 'revision'
134     return {
135         'post':post,
136         'post_type':post_type,
137         'wiki_on':settings.WIKI_ON,
138         'contributor_type':contributor_type
139     }
140         
141 @register.simple_tag
142 def get_score_badge(user):
143     BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
144     if user.gold > 0 :
145         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
146         '<span class="badge1">&#9679;</span>'
147         '<span class="badgecount">%(gold)s</span>'
148         '</span>')
149     if user.silver > 0:
150         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
151         '<span class="silver">&#9679;</span>'
152         '<span class="badgecount">%(silver)s</span>'
153         '</span>')
154     if user.bronze > 0:
155         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
156         '<span class="bronze">&#9679;</span>'
157         '<span class="badgecount">%(bronze)s</span>'
158         '</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,
162         'gold' : user.gold,
163         'silver' : user.silver,
164         'bronze' : user.bronze,
165                 'badgesword' : _('badges'),
166                 'reputationword' : _('reputation points'),
167     })
168     
169 @register.simple_tag
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>'
172     if gold > 0 :
173         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
174         '<span class="badge1">&#9679;</span>'
175         '<span class="badgecount">%(gold)s</span>'
176         '</span>')
177     if silver > 0:
178         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
179         '<span class="badge2">&#9679;</span>'
180         '<span class="badgecount">%(silver)s</span>'
181         '</span>')
182     if bronze > 0:
183         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
184         '<span class="badge3">&#9679;</span>'
185         '<span class="badgecount">%(bronze)s</span>'
186         '</span>')
187     BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
188     return mark_safe(BADGE_TEMPLATE % {
189         'reputation' : rep,
190         'gold' : gold,
191         'silver' : silver,
192         'bronze' : bronze,
193                 'repword' : _('reputation points'),
194                 'badgeword' : _('badges'),
195     })      
196     
197 @register.simple_tag
198 def get_user_vote_image(dic, key, arrow):
199     if dic.has_key(key):
200         if int(dic[key]) == int(arrow):
201             return '-on'
202     return ''
203         
204 @register.simple_tag
205 def get_age(birthday):
206     current_time = datetime.datetime(*time.localtime()[0:6])
207     year = birthday.year
208     month = birthday.month
209     day = birthday.day
210     diff = current_time - datetime.datetime(year,month,day,0,0,0)
211     return diff.days / 365
212
213 @register.simple_tag
214 def get_total_count(up_count, down_count):
215     return up_count + down_count
216
217 @register.simple_tag
218 def format_number(value):
219     strValue = str(value)
220     if len(strValue) <= 3:
221         return strValue
222     result = ''
223     first = ''
224     pattern = re.compile('(-?\d+)(\d{3})')
225     m = re.match(pattern, strValue)
226     while m != None:
227         first = m.group(1)
228         second = m.group(2)
229         result = ',' + second + result
230         strValue = first + ',' + second
231         m = re.match(pattern, strValue)
232     return first + result
233
234 @register.simple_tag
235 def convert2tagname_list(question):
236     question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
237     return ''
238
239 @register.simple_tag
240 def diff_date(date, limen=2):
241     now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
242     diff = now - date
243     days = diff.days
244     hours = int(diff.seconds/3600)
245     minutes = int(diff.seconds/60)
246
247     if days > 2:
248         if date.year == now.year:
249             return date.strftime("%b %d at %H:%M")
250         else:
251             return date.strftime("%b %d '%y at %H:%M")
252     elif days == 2:
253         return _('2 days ago')
254     elif days == 1:
255         return _('yesterday')
256     elif minutes >= 60:
257         return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
258     else:
259         return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
260
261 @register.simple_tag
262 def get_latest_changed_timestamp():
263     try:
264         from time import localtime, strftime
265         from os import path
266         root = settings.SITE_SRC_ROOT
267         dir = (
268             root,
269             '%s/forum' % root,
270             '%s/templates' % root,
271         )
272         stamp = (path.getmtime(d) for d in dir)
273         latest = max(stamp)
274         timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
275     except:
276         timestr = ''
277     return timestr
278
279 @register.simple_tag
280 def media(url):
281     url = skins.find_media_source(url)
282     if url:
283         url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
284         return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
285
286 class ItemSeparatorNode(template.Node):
287     def __init__(self,separator):
288         sep = separator.strip()
289         if sep[0] == sep[-1] and sep[0] in ('\'','"'):
290             sep = sep[1:-1]
291         else:
292             raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
293         self.content = sep
294     def render(self,context):
295         return self.content
296
297 class JoinItemListNode(template.Node):
298     def __init__(self,separator=ItemSeparatorNode("''"), items=()):
299         self.separator = separator
300         self.items = items
301     def render(self,context):
302         out = []
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):
307                 out.append(bit)
308         return self.separator.render(context).join(out)
309
310 @register.tag(name="joinitems")
311 def joinitems(parser,token):
312     try:
313         tagname,junk,sep_token = token.split_contents()
314     except ValueError:
315         raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
316     if junk == 'using':
317         sep_node = ItemSeparatorNode(sep_token)
318     else:
319         raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
320     nodelist = []
321     while True:
322         nodelist.append(parser.parse(('separator','endjoinitems')))
323         next = parser.next_token()
324         if next.contents == 'endjoinitems':
325             break
326
327     return JoinItemListNode(separator=sep_node,items=nodelist)
328
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/'
334         url = ''
335         if self.items:
336             url += '/'     
337         for item in self.items:
338             url += item.render(context)
339
340         url = skins.find_media_source(url)
341         url = prefix + url
342         out = posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
343         return out.replace(' ','')
344
345 @register.tag(name='blockmedia')
346 def blockmedia(parser,token):
347     try:
348         tagname = token.split_contents()
349     except ValueError:
350         raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
351     nodelist = []
352     while True:
353         nodelist.append(parser.parse(('endblockmedia')))
354         next = parser.next_token()
355         if next.contents == 'endblockmedia':
356             break
357     return BlockMediaUrlNode(nodelist)