| 1 |
# Created by Noah Kantrowitz on 2008-05-16. |
|---|
| 2 |
# Copyright (c) 2008 Noah Kantrowitz. All rights reserved. |
|---|
| 3 |
import itertools |
|---|
| 4 |
import re |
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
from trac.core import * |
|---|
| 8 |
from trac.web.api import ITemplateStreamFilter |
|---|
| 9 |
from trac.web.chrome import ITemplateProvider, add_stylesheet |
|---|
| 10 |
from trac.config import Option, OrderedExtensionsOption |
|---|
| 11 |
from genshi.builder import tag |
|---|
| 12 |
from genshi.filters.transform import Transformer |
|---|
| 13 |
from pkg_resources import resource_filename |
|---|
| 14 |
|
|---|
| 15 |
from hackergotchi.api import IHackergotchiProvider |
|---|
| 16 |
|
|---|
| 17 |
class HackergotchiModule(Component): |
|---|
| 18 |
"""A stream filter to add hackergotchi emblems to the timeline.""" |
|---|
| 19 |
|
|---|
| 20 |
providers = OrderedExtensionsOption('hackergotchi', 'providers', |
|---|
| 21 |
IHackergotchiProvider, |
|---|
| 22 |
default='GravatarHackergotchiProvider, IdenticonHackergotchiProvider') |
|---|
| 23 |
|
|---|
| 24 |
implements(ITemplateStreamFilter, ITemplateProvider) |
|---|
| 25 |
|
|---|
| 26 |
anon_re = re.compile('([^<]+?)\s+<([^>]+)>', re.U) |
|---|
| 27 |
|
|---|
| 28 |
# ITemplateStreamFilter methods |
|---|
| 29 |
def filter_stream(self, req, method, filename, stream, data): |
|---|
| 30 |
if req.path_info.startswith('/timeline'): |
|---|
| 31 |
closure_state = [0] |
|---|
| 32 |
cache = {} |
|---|
| 33 |
def f(stream): |
|---|
| 34 |
# Update the closed value |
|---|
| 35 |
n = closure_state[0] |
|---|
| 36 |
closure_state[0] += 1 |
|---|
| 37 |
|
|---|
| 38 |
# Extract the user information |
|---|
| 39 |
author = data['events'][n]['author'].strip() |
|---|
| 40 |
user_info = cache.get(author) |
|---|
| 41 |
if user_info is not None: |
|---|
| 42 |
author, name, email = user_info |
|---|
| 43 |
else: |
|---|
| 44 |
db = self.env.get_db_cnx() |
|---|
| 45 |
user_info = self._get_info(author, db) |
|---|
| 46 |
cache[author] = user_info |
|---|
| 47 |
author, name, email = user_info |
|---|
| 48 |
|
|---|
| 49 |
# Try to find a provider |
|---|
| 50 |
for provider in self.providers: |
|---|
| 51 |
href = provider.get_hackergotchi(req.href, author, name, email) |
|---|
| 52 |
if href is not None: |
|---|
| 53 |
break |
|---|
| 54 |
else: |
|---|
| 55 |
href = req.href.chrome('hackergotchi', 'default.png') |
|---|
| 56 |
|
|---|
| 57 |
# Build our element |
|---|
| 58 |
elm = tag.img(src=href, alt='Hackergotchi for %s'%author, |
|---|
| 59 |
class_='hackergotchi') |
|---|
| 60 |
|
|---|
| 61 |
# Output the combined stream |
|---|
| 62 |
return itertools.chain(elm.generate(), stream) |
|---|
| 63 |
|
|---|
| 64 |
stream |= Transformer('//div[@id="content"]/dl/dt/a/span[@class="time"]').filter(f) |
|---|
| 65 |
add_stylesheet(req, 'hackergotchi/hackergotchi.css') |
|---|
| 66 |
return stream |
|---|
| 67 |
|
|---|
| 68 |
# ITemplateProvider methods |
|---|
| 69 |
def get_htdocs_dirs(self): |
|---|
| 70 |
yield 'hackergotchi', resource_filename(__name__, 'htdocs') |
|---|
| 71 |
|
|---|
| 72 |
def get_templates_dirs(self): |
|---|
| 73 |
#return [resource_filename(__name__, 'templates')] |
|---|
| 74 |
return [] |
|---|
| 75 |
|
|---|
| 76 |
# Internal methods |
|---|
| 77 |
def _get_info(self, author, db): |
|---|
| 78 |
if author == 'anonymous': |
|---|
| 79 |
# Don't even bother trying for "anonymous" |
|---|
| 80 |
return author, None, None |
|---|
| 81 |
|
|---|
| 82 |
md = self.anon_re.match(author) |
|---|
| 83 |
if md: |
|---|
| 84 |
# name <email> |
|---|
| 85 |
return 'anonymous', md.group(1), md.group(2) |
|---|
| 86 |
|
|---|
| 87 |
cursor = db.cursor() |
|---|
| 88 |
cursor.execute('SELECT name, value FROM session_attribute WHERE sid=%s AND authenticated=%s', |
|---|
| 89 |
(author, 1)) |
|---|
| 90 |
rows = cursor.fetchall() |
|---|
| 91 |
if rows: |
|---|
| 92 |
# Authenticated user, with session |
|---|
| 93 |
name = email = None |
|---|
| 94 |
for key, value in rows: |
|---|
| 95 |
if key == 'name': |
|---|
| 96 |
name = value |
|---|
| 97 |
elif key == 'email': |
|---|
| 98 |
email = value |
|---|
| 99 |
if name or email: |
|---|
| 100 |
return author, name, email |
|---|
| 101 |
else: |
|---|
| 102 |
return author, None, None |
|---|
| 103 |
|
|---|
| 104 |
# Assume anonymous user from this point on |
|---|
| 105 |
if '@' in author: |
|---|
| 106 |
# Likely an email address |
|---|
| 107 |
return 'anonymous', None, author |
|---|
| 108 |
|
|---|
| 109 |
# See if there is a default domain |
|---|
| 110 |
domain = self.config.get('notification', 'smtp_default_domain') |
|---|
| 111 |
if domain and ' ' not in author: |
|---|
| 112 |
return author, None, author+'@'+domain |
|---|
| 113 |
|
|---|
| 114 |
return 'anonymous', author, None |
|---|