]> git.openstreetmap.org Git - osqa.git/blob - forum/templatetags/extra_tags.py
892f38b981d103d3431c28b114332e3a54816118
[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     try:
31         gravatar = user['gravatar']
32         username = user['username']
33     except (TypeError, AttributeError, KeyError):
34         gravatar = user.gravatar
35         username = user.username
36     return mark_safe(GRAVATAR_TEMPLATE % {
37     'size': size,
38     'gravatar_hash': gravatar,
39     'username': template.defaultfilters.urlencode(username),
40     })
41
42
43 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
44 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
45 NUM_PAGES_OUTSIDE_RANGE = 1
46 ADJACENT_PAGES = 2
47 @register.inclusion_tag("paginator.html")
48 def cnprog_paginator(context):
49     """
50     custom paginator tag
51     Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
52     """
53     if (context["is_paginated"]):
54         " Initialize variables "
55         in_leading_range = in_trailing_range = False
56         pages_outside_leading_range = pages_outside_trailing_range = range(0)
57
58         if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
59             in_leading_range = in_trailing_range = True
60             page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
61         elif (context["page"] <= LEADING_PAGE_RANGE):
62             in_leading_range = True
63             page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
64             pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
65         elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
66             in_trailing_range = True
67             page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1)
68                             if n > 0 and n <= context["pages"]]
69             pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
70         else:
71             page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if
72                             n > 0 and n <= context["pages"]]
73             pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
74             pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
75
76         extend_url = context.get('extend_url', '')
77         return {
78         "base_url": context["base_url"],
79         "is_paginated": context["is_paginated"],
80         "previous": context["previous"],
81         "has_previous": context["has_previous"],
82         "next": context["next"],
83         "has_next": context["has_next"],
84         "page": context["page"],
85         "pages": context["pages"],
86         "page_numbers": page_numbers,
87         "in_leading_range" : in_leading_range,
88         "in_trailing_range" : in_trailing_range,
89         "pages_outside_leading_range": pages_outside_leading_range,
90         "pages_outside_trailing_range": pages_outside_trailing_range,
91         "extend_url" : extend_url
92         }
93
94 @register.inclusion_tag("pagesize.html")
95 def cnprog_pagesize(context):
96     """
97     display the pagesize selection boxes for paginator
98     """
99     if (context["is_paginated"]):
100         return {
101         "base_url": context["base_url"],
102         "pagesize" : context["pagesize"],
103         "is_paginated": context["is_paginated"]
104         }
105
106
107 @register.simple_tag
108 def get_score_badge(user):
109     if user.is_suspended():
110         return _("(suspended)")
111
112     BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
113     if user.gold > 0 :
114         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
115         '<span class="badge1">&#9679;</span>'
116         '<span class="badgecount">%(gold)s</span>'
117         '</span>')
118     if user.silver > 0:
119         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
120         '<span class="silver">&#9679;</span>'
121         '<span class="badgecount">%(silver)s</span>'
122         '</span>')
123     if user.bronze > 0:
124         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
125         '<span class="bronze">&#9679;</span>'
126         '<span class="badgecount">%(bronze)s</span>'
127         '</span>')
128     BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
129     return mark_safe(BADGE_TEMPLATE % {
130     'reputation' : user.reputation,
131     'gold' : user.gold,
132     'silver' : user.silver,
133     'bronze' : user.bronze,
134     'badgesword' : _('badges'),
135     'reputationword' : _('reputation points'),
136     })
137
138
139 @register.simple_tag
140 def get_age(birthday):
141     current_time = datetime.datetime(*time.localtime()[0:6])
142     year = birthday.year
143     month = birthday.month
144     day = birthday.day
145     diff = current_time - datetime.datetime(year, month, day, 0, 0, 0)
146     return diff.days / 365
147
148 @register.simple_tag
149 def diff_date(date, limen=2):
150     if not date:
151         return _('unknown')
152
153     now = datetime.datetime.now()
154     diff = now - date
155     days = diff.days
156     hours = int(diff.seconds/3600)
157     minutes = int(diff.seconds/60)
158
159     if days > 2:
160         if date.year == now.year:
161             return date.strftime("%b %d at %H:%M")
162         else:
163             return date.strftime("%b %d '%y at %H:%M")
164     elif days == 2:
165         return _('2 days ago')
166     elif days == 1:
167         return _('yesterday')
168     elif minutes >= 60:
169         return ungettext('%(hr)d hour ago', '%(hr)d hours ago', hours) % {'hr':hours}
170     elif diff.seconds >= 60:
171         return ungettext('%(min)d min ago', '%(min)d mins ago', minutes) % {'min':minutes}
172     else:
173         return ungettext('%(sec)d sec ago', '%(sec)d secs ago', diff.seconds) % {'sec':diff.seconds}
174
175 @register.simple_tag
176 def media(url):
177     url = skins.find_media_source(url)
178     if url:
179         url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
180         return posixpath.normpath(url)
181
182 class ItemSeparatorNode(template.Node):
183     def __init__(self, separator):
184         sep = separator.strip()
185         if sep[0] == sep[-1] and sep[0] in ('\'', '"'):
186             sep = sep[1:-1]
187         else:
188             raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
189         self.content = sep
190
191     def render(self, context):
192         return self.content
193
194 class BlockMediaUrlNode(template.Node):
195     def __init__(self, nodelist):
196         self.items = nodelist
197
198     def render(self, context):
199         prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
200         url = ''
201         if self.items:
202             url += '/'
203         for item in self.items:
204             url += item.render(context)
205
206         url = skins.find_media_source(url)
207         url = prefix + url
208         out = posixpath.normpath(url)
209         return out.replace(' ', '')
210
211 @register.tag(name='blockmedia')
212 def blockmedia(parser, token):
213     try:
214         tagname = token.split_contents()
215     except ValueError:
216         raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
217     nodelist = []
218     while True:
219         nodelist.append(parser.parse(('endblockmedia')))
220         next = parser.next_token()
221         if next.contents == 'endblockmedia':
222             break
223     return BlockMediaUrlNode(nodelist)
224
225
226 @register.simple_tag
227 def fullmedia(url):
228     domain = settings.APP_URL
229     #protocol = getattr(settings, "PROTOCOL", "http")
230     path = media(url)
231     return "%s%s" % (domain, path)
232
233
234 class SimpleVarNode(template.Node):
235     def __init__(self, name, value):
236         self.name = name
237         self.value = template.Variable(value)
238
239     def render(self, context):
240         context[self.name] = self.value.resolve(context)
241         return ''
242
243 class BlockVarNode(template.Node):
244     def __init__(self, name, block):
245         self.name = name
246         self.block = block
247
248     def render(self, context):
249         source = self.block.render(context)
250         context[self.name] = source.strip()
251         return ''
252
253
254 @register.tag(name='var')
255 def do_var(parser, token):
256     tokens = token.split_contents()[1:]
257
258     if not len(tokens) or not re.match('^\w+$', tokens[0]):
259         raise template.TemplateSyntaxError("Expected variable name")
260
261     if len(tokens) == 1:
262         nodelist = parser.parse(('endvar',))
263         parser.delete_first_token()
264         return BlockVarNode(tokens[0], nodelist)
265     elif len(tokens) == 3:
266         return SimpleVarNode(tokens[0], tokens[2])
267
268     raise template.TemplateSyntaxError("Invalid number of arguments")
269
270 class DeclareNode(template.Node):
271     dec_re = re.compile('^\s*(\w+)\s*(:?=)\s*(.*)$')
272
273     def __init__(self, block):
274         self.block = block
275
276     def render(self, context):
277         source = self.block.render(context)
278
279         for line in source.splitlines():
280             m = self.dec_re.search(line)
281             if m:
282                 clist = list(context)
283                 clist.reverse()
284                 d = {}
285                 d['_'] = _
286                 d['os'] = os
287                 d['html'] = html
288                 d['reverse'] = reverse
289                 for c in clist:
290                     d.update(c)
291                 try:
292                     context[m.group(1).strip()] = eval(m.group(3).strip(), d)
293                 except Exception, e:
294                     logging.error("Error in declare tag, when evaluating: %s" % m.group(3).strip())
295                     raise
296         return ''
297
298 @register.tag(name='declare')
299 def do_declare(parser, token):
300     nodelist = parser.parse(('enddeclare',))
301     parser.delete_first_token()
302     return DeclareNode(nodelist)