]> git.openstreetmap.org Git - osqa.git/blob - forum/templatetags/extra_tags.py
db3027898fbf11aea516e36f59a54dc8f521ab84
[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.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
20
21 register = template.Library()
22
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&amp;d=identicon&amp;r=PG" '
26                      'alt="%(username)s\'s gravatar image" />')
27
28 @register.simple_tag
29 def gravatar(user, size):
30     """
31     Creates an ``<img>`` for a user's Gravatar with a given size.
32
33     This tag can accept a User object, or a dict containing the
34     appropriate values.
35     """
36     try:
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 % {
43         'size': size,
44         'gravatar_hash': gravatar,
45         'username': template.defaultfilters.urlencode(username),
46     })
47
48 #MAX_FONTSIZE = 18
49 #MIN_FONTSIZE = 12
50 #@register.simple_tag
51 #def tag_font_size(max_size, min_size, current_size):
52 #    """
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/
55 #    """
56 #    #avoid invalid calculation
57 #    if current_size == 0:
58 #        current_size = 1
59 #    try:
60 #        weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
61 #    except:
62 #        weight = 0
63 #    return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
64
65
66 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
67 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
68 NUM_PAGES_OUTSIDE_RANGE = 1
69 ADJACENT_PAGES = 2
70 @register.inclusion_tag("paginator.html")
71 def cnprog_paginator(context):
72     """
73     custom paginator tag
74     Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
75     """
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)
80
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)]
92         else:
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)]
96
97         extend_url = context.get('extend_url', '')
98         return {
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
113         }
114
115 @register.inclusion_tag("pagesize.html")
116 def cnprog_pagesize(context):
117     """
118     display the pagesize selection boxes for paginator
119     """
120     if (context["is_paginated"]):
121         return {
122             "base_url": context["base_url"],
123             "pagesize" : context["pagesize"],
124             "is_paginated": context["is_paginated"]
125         }
126
127    
128 @register.simple_tag
129 def get_score_badge(user):
130     BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
131     if user.gold > 0 :
132         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
133         '<span class="badge1">&#9679;</span>'
134         '<span class="badgecount">%(gold)s</span>'
135         '</span>')
136     if user.silver > 0:
137         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
138         '<span class="silver">&#9679;</span>'
139         '<span class="badgecount">%(silver)s</span>'
140         '</span>')
141     if user.bronze > 0:
142         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
143         '<span class="bronze">&#9679;</span>'
144         '<span class="badgecount">%(bronze)s</span>'
145         '</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,
149         'gold' : user.gold,
150         'silver' : user.silver,
151         'bronze' : user.bronze,
152                 'badgesword' : _('badges'),
153                 'reputationword' : _('reputation points'),
154     })
155     
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>'
159 #    if gold > 0 :
160 #        BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
161 #        '<span class="badge1">&#9679;</span>'
162 #        '<span class="badgecount">%(gold)s</span>'
163 #        '</span>')
164 #    if silver > 0:
165 #        BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
166 #        '<span class="badge2">&#9679;</span>'
167 #        '<span class="badgecount">%(silver)s</span>'
168 #        '</span>')
169 #    if bronze > 0:
170 #        BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
171 #        '<span class="badge3">&#9679;</span>'
172 #        '<span class="badgecount">%(bronze)s</span>'
173 #        '</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,
177 #        'gold' : gold,
178 #        'silver' : silver,
179 #        'bronze' : bronze,
180 #               'repword' : _('reputation points'),
181 #               'badgeword' : _('badges'),
182 #    })
183     
184 @register.simple_tag
185 def get_age(birthday):
186     current_time = datetime.datetime(*time.localtime()[0:6])
187     year = birthday.year
188     month = birthday.month
189     day = birthday.day
190     diff = current_time - datetime.datetime(year,month,day,0,0,0)
191     return diff.days / 365
192
193 #@register.simple_tag
194 #def get_total_count(up_count, down_count):
195 #    return up_count + down_count
196
197 #@register.simple_tag
198 #def format_number(value):
199 #    strValue = str(value)
200 #    if len(strValue) <= 3:
201 #        return strValue
202 #    result = ''
203 #    first = ''
204 #    pattern = re.compile('(-?\d+)(\d{3})')
205 #    m = re.match(pattern, strValue)
206 #    while m != None:
207 #        first = m.group(1)
208 #        second = m.group(2)
209 #        result = ',' + second + result
210 #        strValue = first + ',' + second
211 #        m = re.match(pattern, strValue)
212 #    return first + result
213
214 #@register.simple_tag
215 #def convert2tagname_list(question):
216 #    question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
217 #    return ''
218
219 @register.simple_tag
220 def diff_date(date, limen=2):
221     if not date:
222         return _('unknown')
223         
224     now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
225     diff = now - date
226     days = diff.days
227     hours = int(diff.seconds/3600)
228     minutes = int(diff.seconds/60)
229
230     if days > 2:
231         if date.year == now.year:
232             return date.strftime("%b %d at %H:%M")
233         else:
234             return date.strftime("%b %d '%y at %H:%M")
235     elif days == 2:
236         return _('2 days ago')
237     elif days == 1:
238         return _('yesterday')
239     elif minutes >= 60:
240         return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
241     else:
242         return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
243
244 #@register.simple_tag
245 #def get_latest_changed_timestamp():
246 #    try:
247 #        from time import localtime, strftime
248 #        from os import path
249 #        root = settings.SITE_SRC_ROOT
250 #        dir = (
251 #            root,
252 #            '%s/forum' % root,
253 #            '%s/templates' % root,
254 #        )
255 #        stamp = (path.getmtime(d) for d in dir)
256 #        latest = max(stamp)
257 #        timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
258 #    except:
259 #        timestr = ''
260 #    return timestr
261
262 @register.simple_tag
263 def media(url):
264     url = skins.find_media_source(url)
265     if url:
266         url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
267         return posixpath.normpath(url)
268
269 class ItemSeparatorNode(template.Node):
270     def __init__(self,separator):
271         sep = separator.strip()
272         if sep[0] == sep[-1] and sep[0] in ('\'','"'):
273             sep = sep[1:-1]
274         else:
275             raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
276         self.content = sep
277     def render(self,context):
278         return self.content
279
280 #class JoinItemListNode(template.Node):
281 #    def __init__(self,separator=ItemSeparatorNode("''"), items=()):
282 #        self.separator = separator
283 #        self.items = items
284 #    def render(self,context):
285 #        out = []
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):
290 #                out.append(bit)
291 #        return self.separator.render(context).join(out)
292 #
293 #@register.tag(name="joinitems")
294 #def joinitems(parser,token):
295 #    try:
296 #        tagname,junk,sep_token = token.split_contents()
297 #    except ValueError:
298 #        raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
299 #    if junk == 'using':
300 #        sep_node = ItemSeparatorNode(sep_token)
301 #    else:
302 #        raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
303 #    nodelist = []
304 #    while True:
305 #        nodelist.append(parser.parse(('separator','endjoinitems')))
306 #        next = parser.next_token()
307 #        if next.contents == 'endjoinitems':
308 #            break
309
310 #    return JoinItemListNode(separator=sep_node,items=nodelist)
311
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/'
317         url = ''
318         if self.items:
319             url += '/'     
320         for item in self.items:
321             url += item.render(context)
322
323         url = skins.find_media_source(url)
324         url = prefix + url
325         out = posixpath.normpath(url)
326         return out.replace(' ','')
327
328 @register.tag(name='blockmedia')
329 def blockmedia(parser,token):
330     try:
331         tagname = token.split_contents()
332     except ValueError:
333         raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
334     nodelist = []
335     while True:
336         nodelist.append(parser.parse(('endblockmedia')))
337         next = parser.next_token()
338         if next.contents == 'endblockmedia':
339             break
340     return BlockMediaUrlNode(nodelist)
341
342
343 @register.simple_tag
344 def fullmedia(url):
345     domain = settings.APP_URL
346     #protocol = getattr(settings, "PROTOCOL", "http")
347     path = media(url)
348     return "%s%s" % (domain, path)
349
350
351 class SimpleVarNode(template.Node):
352     def __init__(self, name, value):
353         self.name = name
354         self.value = template.Variable(value)
355
356     def render(self, context):
357         context[self.name] = self.value.resolve(context)
358         return ''
359
360 class BlockVarNode(template.Node):
361     def __init__(self, name, block):
362         self.name = name
363         self.block = block
364
365     def render(self, context):
366         source = self.block.render(context)
367         context[self.name] = source.strip()
368         return ''
369
370
371 @register.tag(name='var')
372 def do_var(parser, token):
373     tokens = token.split_contents()[1:]
374
375     if not len(tokens) or not re.match('^\w+$', tokens[0]):
376         raise template.TemplateSyntaxError("Expected variable name")
377
378     if len(tokens) == 1:
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])
384
385     raise template.TemplateSyntaxError("Invalid number of arguments")
386
387 class DeclareNode(template.Node):
388     dec_re = re.compile('^\s*(\w+)\s*(:?=)\s*(.*)$')
389
390     def __init__(self, block):
391         self.block = block
392
393     def render(self, context):
394         source = self.block.render(context)
395
396         for line in source.splitlines():
397             m = self.dec_re.search(line)
398             if m:
399                 clist = list(context)
400                 clist.reverse()
401                 d = {}
402                 d['_'] = _
403                 d['os'] = os
404                 d['html'] = html
405                 d['reverse'] = reverse
406                 for c in clist:
407                     d.update(c)
408                 try:
409                     context[m.group(1).strip()] = eval(m.group(3).strip(), d)
410                 except Exception, e:
411                     logging.error("Error in declare tag, when evaluating: %s" % m.group(3).strip())
412                     raise
413         return ''
414
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)