Changeset 3513
- Timestamp:
- 04/14/08 01:28:38 (8 months ago)
- Files:
-
- trashtalkplugin/0.11/setup.py (modified) (1 diff)
- trashtalkplugin/0.11/trashtalk/htdocs (added)
- trashtalkplugin/0.11/trashtalk/htdocs/trashtalk.css (added)
- trashtalkplugin/0.11/trashtalk/trashtalk.py (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trashtalkplugin/0.11/setup.py
r3509 r3513 1 1 from setuptools import find_packages, setup 2 2 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_points6 3 setup( 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 13 24 ) 14 25 trashtalkplugin/0.11/trashtalk/trashtalk.py
r3512 r3513 29 29 30 30 Some methods below were legally pilferred from the tracpasteplugin: 31 http://www.trac-hacks.org/wiki/TracPastePlugin d31 http://www.trac-hacks.org/wiki/TracPastePlugin 32 32 """ 33 33 34 34 import re 35 35 36 from genshi.core import Markup 36 37 from genshi.builder import tag 38 from genshi.filters.transform import Transformer 37 39 38 40 from trac.core import * … … 40 42 from trac.db import Table, Column, Index 41 43 from trac.resource import ResourceNotFound 42 from trac.web.api import *43 44 from trac.ticket import Ticket 44 45 from trac.util.datefmt import utc, to_timestamp 45 46 from datetime import datetime 46 47 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 48 from trac.web.api import IRequestFilter, ITemplateStreamFilter 49 from trac.web.chrome import ITemplateProvider, add_stylesheet 66 50 67 51 class TrashTalkPlugin(Component): 68 implements(I RequestFilter, IEnvironmentSetupParticipant)52 implements(IEnvironmentSetupParticipant, ITemplateStreamFilter, ITemplateProvider, IRequestFilter) 69 53 70 54 SCHEMA = [ … … 73 57 Column('ticket'), # The id of the ticket that has been linked to. 74 58 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. 76 60 Column('first', type='int'), # The first time the external_url linked to the ticket. 77 61 Column('most_recent', type='int') # The most recent time the external_url linked to the ticket. … … 89 73 90 74 TICKET_URI_PATTERN = re.compile("^/ticket/(\d+)(?:$|.*$)", re.VERBOSE) 91 75 92 76 # IEnvironmentSetupParticipant methods 93 77 def environment_created(self): … … 107 91 self._upgrade_db(db) 108 92 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 110 121 # IRequestFilter methods 111 122 def pre_process_request(self, req, handler): 112 if self._is_external(req) and self._is_ticket(req):113 self._talk_about(req)114 123 return handler 115 124 116 125 # for ClearSilver templates 117 126 def post_process_request(self, req, template, content_type): 118 127 return (template, content_type) 119 128 120 129 # for Genshi templates 121 130 def post_process_request(self, req, template, data, content_type): 131 #if self._is_ticket(req): 132 add_stylesheet(req, 'trashtalk/trashtalk.css') 133 122 134 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 [] 123 151 124 152 # Private methods … … 128 156 # Right now though, for a first implementation, this will suffice. 129 157 130 ticket = int(self.TICKET_URI_PATTERN.findall(req.path_info)[0])158 ticket = self._get_ticket_from_request(req) 131 159 external_url = req.get_header("referer") 132 160 click_count = 1 … … 165 193 def _is_ticket(self, req): 166 194 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 167 218 168 219 def _upgrade_db(self, db): … … 184 235 self.env.log.error(e, exc_info=1) 185 236 raise TracError(str(e)) 186 237 238 239