Changeset 3513


Ignore:
Timestamp:
Apr 14, 2008, 6:28:38 AM (15 years ago)
Author:
Tim Coulter
Message:

First implementation finished! If there are incoming links for a ticket, they will be displayed in the "Incoming Links" section right after the ticket info.

Most of the changes made since the last commit were simply to get the data to display properly/nicely.

Because of the use of Transformer, this plugin requires Genshi trunk.

Location:
trashtalkplugin/0.11
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • trashtalkplugin/0.11/setup.py

    r3509 r3513  
    11from setuptools import find_packages, setup
    22
    3 # name can be any name.  This name will be used to create .egg file.
    4 # name that is used in packages is the one that is used in the trac.ini file.
    5 # use package name as entry_points
    63setup(
    7     name='TrashTalk', version='0.1',
    8     packages=find_packages(exclude=['*.tests*']),
    9     entry_points = """
    10         [trac.plugins]
    11         trashtalk = trashtalk
    12     """,
     4    name='TrashTalk',
     5    version='0.1',
     6    packages=['trashtalk'],
     7    package_data={ 'trashtalk' : ['htdocs/*.css'] },
     8   
     9    author='Tim Coulter',
     10    author_email='tim@timothyjcoulter.com',
     11    description='Logs URLs linking to tickets in order to show community impact.',
     12    url='http://trac-hacks.org/wiki/TrashTalkPlugin',
     13   
     14    classifiers=[
     15        'Framework :: Trac',
     16    ],
     17   
     18    install_requires = ['Trac', 'Genshi >= 0.5.dev-r698,==dev'],
     19   
     20    entry_points = {
     21        'trac.plugins': ['trashtalk = trashtalk']
     22    }
     23   
    1324)
    1425
  • trashtalkplugin/0.11/trashtalk/trashtalk.py

    r3512 r3513  
    2929
    3030Some methods below were legally pilferred from the tracpasteplugin:
    31 http://www.trac-hacks.org/wiki/TracPastePlugind
     31http://www.trac-hacks.org/wiki/TracPastePlugin
    3232"""
    3333
    3434import re
    3535
     36from genshi.core import Markup
    3637from genshi.builder import tag
     38from genshi.filters.transform import Transformer
    3739
    3840from trac.core import *
     
    4042from trac.db import Table, Column, Index
    4143from trac.resource import ResourceNotFound
    42 from trac.web.api import *
    4344from trac.ticket import Ticket
    4445from trac.util.datefmt import utc, to_timestamp
    4546from datetime import datetime
    4647
    47 def sql_escape_percent(sql):
    48     import re
    49     return re.sub("'((?:[^']|(?:''))*)'", lambda m: m.group(0).replace('%', '%%'), sql)
    50 
    51 def get_incoming_links_for_ticket(env, ticket, db=None):
    52     """Return all incoming links for the given ticket."""
    53     cursor = (db or env.get_db_cnx()).cursor()
    54     cursor.execute('select * from incoming_links where id=%s order by first desc', (ticket,))
    55     result = []
    56     for row in cursor:
    57         result.append({
    58             'id':                   row[0],
    59             'ticket':               row[1],
    60             'external_url':         row[2],
    61             'click_count':          row[3],
    62             'first':                datetime.fromtimestamp(row[4], utc),
    63             'most_recent':          datetime.fromtimestamp(row[5], utc)
    64         })
    65     return result
     48from trac.web.api import IRequestFilter, ITemplateStreamFilter
     49from trac.web.chrome import ITemplateProvider, add_stylesheet
    6650
    6751class TrashTalkPlugin(Component):
    68     implements(IRequestFilter, IEnvironmentSetupParticipant)
     52    implements(IEnvironmentSetupParticipant, ITemplateStreamFilter, ITemplateProvider, IRequestFilter)
    6953
    7054    SCHEMA = [
     
    7357            Column('ticket'),                  # The id of the ticket that has been linked to.
    7458            Column('external_url'),            # The url linking to the ticket.
    75             Column('click_count'),            # The number of times the external url has been clicked on.
     59            Column('click_count', type='int'), # The number of times the external url has been clicked on.
    7660            Column('first', type='int'),       # The first time the external_url linked to the ticket.
    7761            Column('most_recent', type='int')  # The most recent time the external_url linked to the ticket.
     
    8973
    9074    TICKET_URI_PATTERN = re.compile("^/ticket/(\d+)(?:$|.*$)", re.VERBOSE)
    91 
     75   
    9276    # IEnvironmentSetupParticipant methods
    9377    def environment_created(self):
     
    10791        self._upgrade_db(db)
    10892
    109 
     93    # ITemplateStreamFilter methods
     94    def filter_stream(self, req, method, filename, stream, data):
     95       
     96        if self._is_ticket(req):
     97           
     98            if self._is_external(req):
     99                self._talk_about(req)
     100           
     101            ticket = self._get_ticket_from_request(req)
     102            links = self._get_incoming_links_for_ticket(ticket)
     103           
     104            if len(links) != 0:
     105       
     106                ticket = self._get_ticket_from_request(req)
     107               
     108                div = tag.div(id="trashtalk")
     109   
     110                for link in links:
     111                    a = tag.a(href=link['external_url'])
     112                    a(link['external_url'])
     113                    div(a)
     114                    div(" (%s)" % link['click_count'])
     115                    div(tag.br)
     116               
     117                stream |= Transformer('//div[@id="ticket"]').after(div).after(tag.h2("Incoming Links"))
     118
     119        return stream
     120   
    110121    # IRequestFilter methods
    111122    def pre_process_request(self, req, handler):
    112         if self._is_external(req) and self._is_ticket(req):
    113             self._talk_about(req)
    114123        return handler
    115    
     124
    116125    # for ClearSilver templates
    117126    def post_process_request(self, req, template, content_type):
    118127        return (template, content_type)
    119    
     128
    120129    # for Genshi templates
    121130    def post_process_request(self, req, template, data, content_type):
     131        #if self._is_ticket(req):
     132        add_stylesheet(req, 'trashtalk/trashtalk.css')
     133
    122134        return (template, data, content_type)
     135   
     136    # ITemplateProvider methods
     137    def get_htdocs_dirs(self):
     138        """Return the absolute path of a directory containing additional
     139        static resources (such as images, style sheets, etc).
     140        """
     141        from pkg_resources import resource_filename
     142        return [('trashtalk', resource_filename(__name__, 'htdocs'))]
     143
     144    def get_templates_dirs(self):
     145        """Return the absolute path of the directory containing the provided
     146        ClearSilver templates.
     147        """
     148        #from pkg_resources import resource_filename
     149        #return [resource_filename(__name__, 'templates')]
     150        return []
    123151   
    124152    # Private methods
     
    128156        # Right now though, for a first implementation, this will suffice.
    129157       
    130         ticket = int(self.TICKET_URI_PATTERN.findall(req.path_info)[0])
     158        ticket = self._get_ticket_from_request(req)
    131159        external_url = req.get_header("referer")
    132160        click_count = 1
     
    165193    def _is_ticket(self, req):
    166194        return len(self.TICKET_URI_PATTERN.findall(req.path_info)) > 0
     195   
     196    def _get_ticket_from_request(self, req):
     197        return int(self.TICKET_URI_PATTERN.findall(req.path_info)[0])
     198   
     199    def _get_incoming_links_for_ticket(self, ticket):
     200        # Let's get a database connection.
     201        db = self.env.get_db_cnx()
     202        cursor = db.cursor()
     203        cursor.execute('select * from incoming_links where ticket=%s order by first desc', str(ticket))
     204       
     205        rows = cursor.fetchall()
     206       
     207        result = []
     208        for row in rows:
     209            result.append({
     210                'id':                   row[self.SCHEMA_LOOKUP['id']],
     211                'ticket':               row[self.SCHEMA_LOOKUP['ticket']],
     212                'external_url':         row[self.SCHEMA_LOOKUP['external_url']],
     213                'click_count':          row[self.SCHEMA_LOOKUP['click_count']],
     214                'first':                row[self.SCHEMA_LOOKUP['first']],
     215                'most_recent':          row[self.SCHEMA_LOOKUP['most_recent']]
     216            })
     217        return result
    167218   
    168219    def _upgrade_db(self, db):
     
    184235            self.env.log.error(e, exc_info=1)
    185236            raise TracError(str(e))
    186    
     237       
     238
     239   
Note: See TracChangeset for help on using the changeset viewer.