]> git.openstreetmap.org Git - osqa.git/blob - forum/templatetags/extra_tags.py
822fdc1b94636c6f28c4620d08dd036b78f5b779
[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, NodeRevision
13 from django.utils.translation import ugettext as _
14 from django.utils.translation import ungettext
15 from django.utils import simplejson
16 from django.conf import settings
17 from django.template.defaulttags import url as default_url
18 from forum import skins
19
20 register = template.Library()
21
22 GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
23                      'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
24                      '?s=%(size)s&amp;d=identicon&amp;r=PG" '
25                      'alt="%(username)s\'s gravatar image" />')
26
27 @register.simple_tag
28 def gravatar(user, size):
29     """
30     Creates an ``<img>`` for a user's Gravatar with a given size.
31
32     This tag can accept a User object, or a dict containing the
33     appropriate values.
34     """
35     try:
36         gravatar = user['gravatar']
37         username = user['username']
38     except (TypeError, AttributeError, KeyError):
39         gravatar = user.gravatar
40         username = user.username
41     return mark_safe(GRAVATAR_TEMPLATE % {
42         'size': size,
43         'gravatar_hash': gravatar,
44         'username': template.defaultfilters.urlencode(username),
45     })
46
47 MAX_FONTSIZE = 18
48 MIN_FONTSIZE = 12
49 @register.simple_tag
50 def tag_font_size(max_size, min_size, current_size):
51     """
52     do a logarithmic mapping calcuation for a proper size for tagging cloud
53     Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
54     """
55     #avoid invalid calculation
56     if current_size == 0:
57         current_size = 1
58     try:
59         weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
60     except:
61         weight = 0
62     return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
63
64
65 LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
66 LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
67 NUM_PAGES_OUTSIDE_RANGE = 1
68 ADJACENT_PAGES = 2
69 @register.inclusion_tag("paginator.html")
70 def cnprog_paginator(context):
71     """
72     custom paginator tag
73     Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
74     """
75     if (context["is_paginated"]):
76         " Initialize variables "
77         in_leading_range = in_trailing_range = False
78         pages_outside_leading_range = pages_outside_trailing_range = range(0)
79
80         if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
81             in_leading_range = in_trailing_range = True
82             page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
83         elif (context["page"] <= LEADING_PAGE_RANGE):
84             in_leading_range = True
85             page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
86             pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
87         elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
88             in_trailing_range = True
89             page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
90             pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
91         else:
92             page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
93             pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
94             pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
95
96         extend_url = context.get('extend_url', '')
97         return {
98             "base_url": context["base_url"],
99             "is_paginated": context["is_paginated"],
100             "previous": context["previous"],
101             "has_previous": context["has_previous"],
102             "next": context["next"],
103             "has_next": context["has_next"],
104             "page": context["page"],
105             "pages": context["pages"],
106             "page_numbers": page_numbers,
107             "in_leading_range" : in_leading_range,
108             "in_trailing_range" : in_trailing_range,
109             "pages_outside_leading_range": pages_outside_leading_range,
110             "pages_outside_trailing_range": pages_outside_trailing_range,
111             "extend_url" : extend_url
112         }
113
114 @register.inclusion_tag("pagesize.html")
115 def cnprog_pagesize(context):
116     """
117     display the pagesize selection boxes for paginator
118     """
119     if (context["is_paginated"]):
120         return {
121             "base_url": context["base_url"],
122             "pagesize" : context["pagesize"],
123             "is_paginated": context["is_paginated"]
124         }
125
126 @register.inclusion_tag("post_contributor_info.html")
127 def post_contributor_info(post,contributor_type='original_author'):
128     """contributor_type: original_author|last_updater
129     """
130     if isinstance(post,Question):
131         post_type = 'question'
132     elif isinstance(post,Answer):
133         post_type = 'answer'
134     elif isinstance(post,(AnswerRevision, QuestionRevision, NodeRevision)):
135         post_type = 'revision'
136     return {
137         'post':post,
138         'post_type':post_type,
139         'wiki_on':settings.WIKI_ON,
140         'contributor_type':contributor_type
141     }
142         
143 @register.simple_tag
144 def get_score_badge(user):
145     BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
146     if user.gold > 0 :
147         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
148         '<span class="badge1">&#9679;</span>'
149         '<span class="badgecount">%(gold)s</span>'
150         '</span>')
151     if user.silver > 0:
152         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
153         '<span class="silver">&#9679;</span>'
154         '<span class="badgecount">%(silver)s</span>'
155         '</span>')
156     if user.bronze > 0:
157         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
158         '<span class="bronze">&#9679;</span>'
159         '<span class="badgecount">%(bronze)s</span>'
160         '</span>')
161     BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
162     return mark_safe(BADGE_TEMPLATE % {
163         'reputation' : user.reputation,
164         'gold' : user.gold,
165         'silver' : user.silver,
166         'bronze' : user.bronze,
167                 'badgesword' : _('badges'),
168                 'reputationword' : _('reputation points'),
169     })
170     
171 @register.simple_tag
172 def get_score_badge_by_details(rep, gold, silver, bronze):
173     BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
174     if gold > 0 :
175         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
176         '<span class="badge1">&#9679;</span>'
177         '<span class="badgecount">%(gold)s</span>'
178         '</span>')
179     if silver > 0:
180         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
181         '<span class="badge2">&#9679;</span>'
182         '<span class="badgecount">%(silver)s</span>'
183         '</span>')
184     if bronze > 0:
185         BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
186         '<span class="badge3">&#9679;</span>'
187         '<span class="badgecount">%(bronze)s</span>'
188         '</span>')
189     BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
190     return mark_safe(BADGE_TEMPLATE % {
191         'reputation' : rep,
192         'gold' : gold,
193         'silver' : silver,
194         'bronze' : bronze,
195                 'repword' : _('reputation points'),
196                 'badgeword' : _('badges'),
197     })      
198     
199 @register.simple_tag
200 def get_age(birthday):
201     current_time = datetime.datetime(*time.localtime()[0:6])
202     year = birthday.year
203     month = birthday.month
204     day = birthday.day
205     diff = current_time - datetime.datetime(year,month,day,0,0,0)
206     return diff.days / 365
207
208 @register.simple_tag
209 def get_total_count(up_count, down_count):
210     return up_count + down_count
211
212 @register.simple_tag
213 def format_number(value):
214     strValue = str(value)
215     if len(strValue) <= 3:
216         return strValue
217     result = ''
218     first = ''
219     pattern = re.compile('(-?\d+)(\d{3})')
220     m = re.match(pattern, strValue)
221     while m != None:
222         first = m.group(1)
223         second = m.group(2)
224         result = ',' + second + result
225         strValue = first + ',' + second
226         m = re.match(pattern, strValue)
227     return first + result
228
229 @register.simple_tag
230 def convert2tagname_list(question):
231     question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
232     return ''
233
234 @register.simple_tag
235 def diff_date(date, limen=2):
236     if not date:
237         return _('unknown')
238         
239     now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
240     diff = now - date
241     days = diff.days
242     hours = int(diff.seconds/3600)
243     minutes = int(diff.seconds/60)
244
245     if days > 2:
246         if date.year == now.year:
247             return date.strftime("%b %d at %H:%M")
248         else:
249             return date.strftime("%b %d '%y at %H:%M")
250     elif days == 2:
251         return _('2 days ago')
252     elif days == 1:
253         return _('yesterday')
254     elif minutes >= 60:
255         return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
256     else:
257         return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
258
259 @register.simple_tag
260 def get_latest_changed_timestamp():
261     try:
262         from time import localtime, strftime
263         from os import path
264         root = settings.SITE_SRC_ROOT
265         dir = (
266             root,
267             '%s/forum' % root,
268             '%s/templates' % root,
269         )
270         stamp = (path.getmtime(d) for d in dir)
271         latest = max(stamp)
272         timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
273     except:
274         timestr = ''
275     return timestr
276
277 @register.simple_tag
278 def media(url):
279     url = skins.find_media_source(url)
280     if url:
281         url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
282         return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
283
284 class ItemSeparatorNode(template.Node):
285     def __init__(self,separator):
286         sep = separator.strip()
287         if sep[0] == sep[-1] and sep[0] in ('\'','"'):
288             sep = sep[1:-1]
289         else:
290             raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
291         self.content = sep
292     def render(self,context):
293         return self.content
294
295 class JoinItemListNode(template.Node):
296     def __init__(self,separator=ItemSeparatorNode("''"), items=()):
297         self.separator = separator
298         self.items = items
299     def render(self,context):
300         out = []
301         empty_re = re.compile(r'^\s*$')
302         for item in self.items:
303             bit = item.render(context)
304             if not empty_re.search(bit):
305                 out.append(bit)
306         return self.separator.render(context).join(out)
307
308 @register.tag(name="joinitems")
309 def joinitems(parser,token):
310     try:
311         tagname,junk,sep_token = token.split_contents()
312     except ValueError:
313         raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
314     if junk == 'using':
315         sep_node = ItemSeparatorNode(sep_token)
316     else:
317         raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
318     nodelist = []
319     while True:
320         nodelist.append(parser.parse(('separator','endjoinitems')))
321         next = parser.next_token()
322         if next.contents == 'endjoinitems':
323             break
324
325     return JoinItemListNode(separator=sep_node,items=nodelist)
326
327 class BlockMediaUrlNode(template.Node):
328     def __init__(self,nodelist):
329         self.items = nodelist 
330     def render(self,context):
331         prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
332         url = ''
333         if self.items:
334             url += '/'     
335         for item in self.items:
336             url += item.render(context)
337
338         url = skins.find_media_source(url)
339         url = prefix + url
340         out = posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
341         return out.replace(' ','')
342
343 @register.tag(name='blockmedia')
344 def blockmedia(parser,token):
345     try:
346         tagname = token.split_contents()
347     except ValueError:
348         raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
349     nodelist = []
350     while True:
351         nodelist.append(parser.parse(('endblockmedia')))
352         next = parser.next_token()
353         if next.contents == 'endblockmedia':
354             break
355     return BlockMediaUrlNode(nodelist)
356
357 class FullUrlNode(template.Node):
358     def __init__(self, default_node):
359         self.default_node = default_node
360
361     def render(self, context):
362         domain = settings.APP_URL
363         #protocol = getattr(settings, "PROTOCOL", "http")
364         path = self.default_node.render(context)
365         return "%s%s" % (domain, path)
366
367 @register.tag(name='fullurl')
368 def fullurl(parser, token):
369     default_node = default_url(parser, token)
370     return FullUrlNode(default_node)
371
372 @register.simple_tag
373 def fullmedia(url):
374     domain = settings.APP_URL
375     #protocol = getattr(settings, "PROTOCOL", "http")
376     path = media(url)
377     return "%s%s" % (domain, path)
378
379 class UserVarNode(template.Node):
380     def __init__(self, tokens):
381         self.tokens = tokens
382
383     def render(self, context):
384         return "{{ %s }}" % self.tokens
385
386 @register.tag(name='user_var')
387 def user_var(parser, token):
388     tokens = " ".join(token.split_contents()[1:])
389     return UserVarNode(tokens)
390
391
392 class VariablesNode(template.Node):
393     def __init__(self, nodelist, var_name):
394         self.nodelist = nodelist
395         self.var_name = var_name
396
397     def render(self, context):
398         source = self.nodelist.render(context)
399         context[self.var_name] = simplejson.loads(source)
400         return ''
401
402 @register.tag(name='var')
403 def do_variables(parser, token):
404     try:
405         tag_name, arg = token.contents.split(None, 1)
406     except ValueError:
407         msg = '"%s" tag requires arguments' % token.contents.split()[0]
408         raise template.TemplateSyntaxError(msg)
409     m = re.search(r'as (\w+)', arg)
410     if m:
411         var_name, = m.groups()
412     else:
413         msg = '"%s" tag had invalid arguments' % tag_name
414         raise template.TemplateSyntaxError(msg)
415
416     nodelist = parser.parse(('endvar',))
417     parser.delete_first_token()
418     return VariablesNode(nodelist, var_name)