# Created by Noah Kantrowitz on 2008-05-16. # Copyright (c) 2008 Noah Kantrowitz. All rights reserved. import itertools import re from trac.core import * from trac.web.api import ITemplateStreamFilter from trac.web.chrome import ITemplateProvider, add_stylesheet from trac.config import Option, OrderedExtensionsOption from genshi.builder import tag from genshi.filters.transform import Transformer from pkg_resources import resource_filename from hackergotchi.api import IHackergotchiProvider class HackergotchiModule(Component): """A stream filter to add hackergotchi emblems to the timeline.""" providers = OrderedExtensionsOption('hackergotchi', 'providers', IHackergotchiProvider, default='GravatarHackergotchiProvider, IdenticonHackergotchiProvider') implements(ITemplateStreamFilter, ITemplateProvider) anon_re = re.compile('([^<]+?)\s+<([^>]+)>', re.U) # ITemplateStreamFilter methods def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/timeline'): closure_state = [0] cache = {} def f(stream): # Update the closed value n = closure_state[0] closure_state[0] += 1 # Extract the user information author = data['events'][n]['author'].strip() user_info = cache.get(author) if user_info is not None: author, name, email = user_info else: db = self.env.get_db_cnx() user_info = self._get_info(author, db) cache[author] = user_info author, name, email = user_info # Try to find a provider for provider in self.providers: href = provider.get_hackergotchi(req.href, author, name, email) if href is not None: break else: href = req.href.chrome('hackergotchi', 'default.png') # Build our element elm = tag.img(src=href, alt='Hackergotchi for %s'%author, class_='hackergotchi') # Output the combined stream return itertools.chain(elm.generate(), stream) stream |= Transformer('//div[@id="content"]/dl/dt/a/span[@class="time"]').filter(f) add_stylesheet(req, 'hackergotchi/hackergotchi.css') return stream # ITemplateProvider methods def get_htdocs_dirs(self): yield 'hackergotchi', resource_filename(__name__, 'htdocs') def get_templates_dirs(self): #return [resource_filename(__name__, 'templates')] return [] # Internal methods def _get_info(self, author, db): if author == 'anonymous': # Don't even bother trying for "anonymous" return author, None, None md = self.anon_re.match(author) if md: # name return 'anonymous', md.group(1), md.group(2) cursor = db.cursor() cursor.execute('SELECT name, value FROM session_attribute WHERE sid=%s AND authenticated=%s', (author, 1)) rows = cursor.fetchall() if rows: # Authenticated user, with session name = email = None for key, value in rows: if key == 'name': name = value elif key == 'email': email = value if name or email: return author, name, email else: return author, None, None # Assume anonymous user from this point on if '@' in author: # Likely an email address return 'anonymous', None, author # See if there is a default domain domain = self.config.get('notification', 'smtp_default_domain') if domain and ' ' not in author: return author, None, author+'@'+domain return 'anonymous', author, None