Changeset 3107
- Timestamp:
- 01/20/08 01:44:43 (10 months ago)
- Files:
-
- announcerplugin/0.11/announcerplugin/api.py (modified) (5 diffs)
- announcerplugin/0.11/announcerplugin/distributors/email_distributor.py (modified) (7 diffs)
- announcerplugin/0.11/announcerplugin/formatters/ticket_email.py (modified) (2 diffs)
- announcerplugin/0.11/announcerplugin/formatters/wiki_email.py (modified) (4 diffs)
- announcerplugin/0.11/announcerplugin/pref.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/producers/wiki.py (modified) (3 diffs)
- announcerplugin/0.11/announcerplugin/resolvers/defaultdomain.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/resolvers/sessionemail.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/resolvers/specified.py (modified) (2 diffs)
- announcerplugin/0.11/announcerplugin/subscribers/rulefilters.py (moved) (moved from announcerplugin/0.11/announcerplugin/subscribers/ticket.py) (1 diff)
- announcerplugin/0.11/announcerplugin/subscribers/ticket_compat.py (modified) (4 diffs)
- announcerplugin/0.11/announcerplugin/subscribers/ticket_groups.py (modified) (4 diffs)
- announcerplugin/0.11/announcerplugin/subscribers/watchers.py (modified) (6 diffs)
- announcerplugin/0.11/announcerplugin/subscribers/wiki.py (modified) (2 diffs)
- announcerplugin/0.11/announcerplugin/templates/ticket_email_mimic.html (modified) (2 diffs)
- announcerplugin/0.11/announcerplugin/templates/wiki_email_plaintext.txt (modified) (1 diff)
- announcerplugin/0.11/setup.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
announcerplugin/0.11/announcerplugin/api.py
r3064 r3107 39 39 40 40 Each subscription that is returned is in the form of: 41 ('transport', 'name', 'address')41 ('transport', 'name', authenticated, 'address') 42 42 43 43 The transport should be one that a distributor (and formatter) can … … 195 195 """Handles mapping Trac usernames to addresses for distributors to use.""" 196 196 197 def get_address_for_name(name ):197 def get_address_for_name(name, authenticated): 198 198 """Accepts a session name, and returns an address. 199 199 … … 343 343 if ('*' in categories) or (evt.category in categories): 344 344 supported_subscribers.append(sp) 345 345 346 346 self.log.debug( 347 347 "AnnouncementSystem found the following subscribers capable of " … … 349 349 ', '.join([ss.__class__.__name__ for ss in supported_subscribers])) 350 350 ) 351 351 352 352 subscriptions = set() 353 353 for sp in supported_subscribers: … … 355 355 x for x in sp.get_subscriptions_for_event(evt) if x 356 356 ) 357 357 358 358 self.log.debug( 359 359 "AnnouncementSystem has found the following subscriptions: %s" % ( 360 360 ', '.join( 361 [' (%s via %s)' % ((s[1] or s[2]),s[0]) for s in subscriptions]361 ['[%s(%s) via %s]' % ((s[1] or s[3]), s[2] and 'authenticated' or 'not authenticated',s[0]) for s in subscriptions] 362 362 ) 363 363 ) 364 364 ) 365 365 366 366 packages = {} 367 for transport, target, address in subscriptions:367 for transport, sid, authenticated, address in subscriptions: 368 368 if transport not in packages: 369 369 packages[transport] = set() 370 370 371 packages[transport].add(( target,address))371 packages[transport].add((sid,authenticated,address)) 372 372 373 373 for distributor in self.distributors: announcerplugin/0.11/announcerplugin/distributors/email_distributor.py
r3051 r3107 153 153 messages = {} 154 154 155 for name, a ddress in recipients:155 for name, authenticated, address in recipients: 156 156 if name: 157 format = self._get_preferred_format(event.realm, name )157 format = self._get_preferred_format(event.realm, name, authenticated) 158 158 else: 159 159 format = self._get_default_format() … … 164 164 if name and not address: 165 165 for resolver in self.resolvers: 166 address = resolver.get_address_for_name(name )166 address = resolver.get_address_for_name(name, authenticated) 167 167 if address: 168 self.log.debug("EmailDistributor found the address '%s' for '%s' via: %s" % ( 169 address, name, resolver.__class__.__name__ 168 self.log.debug("EmailDistributor found the address '%s' for '%s (%s)' via: %s" % ( 169 address, name, authenticated and 'authenticated' or 'not authenticated', 170 resolver.__class__.__name__ 170 171 ) 171 172 ) … … 173 174 174 175 if address: 175 messages[format].add((name, a ddress))176 messages[format].add((name, authenticated, address)) 176 177 else: 177 self.log.debug("EmailDistributor was unable to find an address for: %s" % name) 178 self.log.debug("EmailDistributor was unable to find an address for: %s (%s)" % ( 179 name, authenticated and 'authenticated' or 'not authenticated' 180 ) 181 ) 178 182 179 183 for format in messages.keys(): … … 181 185 self.log.debug( 182 186 "EmailDistributor is sending event as '%s' to: %s" % ( 183 format, ', '.join( 184 ('%s <%s>' % (name, address) for name, address in messages[format]) 185 ) 187 format, ', '.join(x[2] for x in messages[format]) 186 188 ) 187 189 ) … … 191 193 return self.default_email_format 192 194 193 def _get_preferred_format(self, realm, sid ):195 def _get_preferred_format(self, realm, sid, authenticated): 194 196 db = self.env.get_db_cnx() 195 197 cursor = db.cursor() … … 199 201 FROM session_attribute 200 202 WHERE sid=%s 201 AND authenticated= 1203 AND authenticated=%s 202 204 AND name=%s 203 """, (sid, 'announcer_email_format_%s' % realm))205 """, (sid, authenticated, 'announcer_email_format_%s' % realm)) 204 206 205 207 result = cursor.fetchone() 206 208 if result: 207 209 chosen = result[0] 208 self.log.debug("EmailDistributor determined the preferred format for '%s' is: %s" % (sid, chosen)) 210 self.log.debug("EmailDistributor determined the preferred format for '%s (%s)' is: %s" % ( 211 sid, authenticated and 'authenticated' or 'not authenticated', chosen 212 ) 213 ) 209 214 return chosen 210 215 else: … … 257 262 start = time.time() 258 263 259 package = (self.smtp_from, [x[ 1] for x in recipients if x], rootMessage.as_string() )264 package = (self.smtp_from, [x[2] for x in recipients if x], rootMessage.as_string() ) 260 265 if self.use_threaded_delivery: 261 266 self._deliveryQueue.put(package) announcerplugin/0.11/announcerplugin/formatters/ticket_email.py
r3046 r3107 31 31 implements(IAnnouncementFormatter) 32 32 33 ticket_email_subject = Option('announcer', 'ticket_email_subject', "Ticket #${ticket.id}: ${ticket['summary']}") 33 ticket_email_subject = Option('announcer', 'ticket_email_subject', 34 "Ticket #${ticket.id}: ${ticket['summary']} {% if action %}[${action}]{% end %}") 34 35 35 36 def get_format_transport(self): … … 69 70 if transport == "email": 70 71 if realm == "ticket": 72 if event.changes: 73 if 'status' in event.changes: 74 action = 'Status -> %s' % (event.target['status']) 75 else: 76 action = None 71 77 template = NewTextTemplate(self.ticket_email_subject) 72 return template.generate(ticket=event.target, event=event ).render()78 return template.generate(ticket=event.target, event=event, action=action).render() 73 79 74 80 def format(self, transport, realm, style, event): announcerplugin/0.11/announcerplugin/formatters/wiki_email.py
r3047 r3107 31 31 implements(IAnnouncementFormatter) 32 32 33 wiki_email_subject = Option('announcer', 'wiki_email_subject', " Wiki ${page.name} changed")33 wiki_email_subject = Option('announcer', 'wiki_email_subject', "Page: ${page.name} ${action}") 34 34 35 35 def get_format_transport(self): … … 70 70 if realm == "wiki": 71 71 template = NewTextTemplate(self.wiki_email_subject) 72 return template.generate(page=event.target, event=event ).render()72 return template.generate(page=event.target, event=event, action=event.category).render() 73 73 74 74 def format(self, transport, realm, style, event): … … 81 81 82 82 def _format_plaintext(self, event): 83 page = event.target 83 84 data = dict( 84 page = event.target, 85 action = event.category, 86 page = page, 85 87 author = event.author, 86 88 comment = event.comment, 87 89 category = event.category, 88 page_link = self.env.abs_href(' page', event.target.name),90 page_link = self.env.abs_href('wiki', page.name), 89 91 project_name = self.env.project_name, 90 92 project_desc = self.env.project_description, … … 92 94 ) 93 95 96 if page.version: 97 data["changed"] = True 98 data["diff_link"] = self.env.abs_href('wiki', page.name, action="diff", version=page.version) 99 100 94 101 chrome = Chrome(self.env) 95 102 dirs = [] 96 103 for provider in chrome.template_providers: 97 104 dirs += provider.get_templates_dirs() 98 105 99 106 templates = TemplateLoader(dirs, variable_lookup='lenient') 100 107 101 108 template = templates.load('wiki_email_plaintext.txt', cls=NewTextTemplate) 102 109 103 110 if template: 104 111 stream = template.generate(**data) 105 112 output = stream.render('text') 106 113 107 114 return output 108 115 announcerplugin/0.11/announcerplugin/pref.py
r3070 r3107 25 25 26 26 def get_preference_panels(self, req): 27 if req.authname and req.authname != 'anonymous': 28 yield ('announcer', 'Announcements') 27 yield ('announcer', 'Announcements') 29 28 30 29 def _get_boxes(self, req): announcerplugin/0.11/announcerplugin/producers/wiki.py
r3086 r3107 24 24 25 25 def wiki_page_added(self, page): 26 history = list(page.get_history())[0] 26 27 announcer = AnnouncementSystem(page.env) 27 28 announcer.send( 28 29 WikiChangeEvent("wiki", "created", page, 29 author= page.author30 author=history[2], version=history[0] 30 31 ) 31 32 ) … … 40 41 ) 41 42 42 def wiki_page_deleted( page):43 def wiki_page_deleted(self, page): 43 44 announcer = AnnouncementSystem(page.env) 44 45 announcer.send( … … 46 47 ) 47 48 48 def wiki_page_version_deleted( page):49 def wiki_page_version_deleted(self, page): 49 50 announcer = AnnouncementSystem(page.env) 50 51 announcer.send( announcerplugin/0.11/announcerplugin/resolvers/defaultdomain.py
r3046 r3107 11 11 """Default host/domain to append to address that do not specify one""") 12 12 13 def get_address_for_name(self, name ):13 def get_address_for_name(self, name, authenticated): 14 14 if self.smtp_default_domain: 15 15 return '%s@%s' % (name, self.smtp_default_domain) announcerplugin/0.11/announcerplugin/resolvers/sessionemail.py
r3041 r3107 7 7 implements(IAnnouncementAddressResolver) 8 8 9 def get_address_for_name(self, name ):9 def get_address_for_name(self, name, authenticated): 10 10 db = self.env.get_db_cnx() 11 11 cursor = db.cursor() 12 13 12 cursor.execute(""" 14 SELECT value , authenticated13 SELECT value 15 14 FROM session_attribute 16 15 WHERE sid=%s 16 AND authenticated=%s 17 17 AND name=%s 18 """, (name, 'email')) 19 20 for record in sorted(cursor.fetchall(), key=lambda x: x[1]): 21 return record[0] 18 """, (name, authenticated and 1 or 0, 'email')) 19 20 result = cursor.fetchone() 21 if result: 22 return result[0] 22 23 23 24 return None announcerplugin/0.11/announcerplugin/resolvers/specified.py
r3046 r3107 9 9 implements(IAnnouncementAddressResolver, IAnnouncementPreferenceProvider) 10 10 11 def get_address_for_name(self, name ):11 def get_address_for_name(self, name, authenticated): 12 12 db = self.env.get_db_cnx() 13 13 cursor = db.cursor() … … 29 29 # IAnnouncementDistributor 30 30 def get_announcement_preference_boxes(self, req): 31 yield "emailaddress", "Announcement Email Address" 31 if req.authname != "anonymous": 32 yield "emailaddress", "Announcement Email Address" 32 33 33 34 def render_announcement_preference_box(self, req, panel): announcerplugin/0.11/announcerplugin/subscribers/rulefilters.py
r3064 r3107 137 137 ) 138 138 return "prefs_announcer_rules.html", data 139 announcerplugin/0.11/announcerplugin/subscribers/ticket_compat.py
r3086 r3107 29 29 def get_subscriptions_for_event(self, event): 30 30 self.log.debug("StaticTicketSubscriber added '%s' because of rule: smtp_always_bcc" % self.bcc) 31 yield ('email', None, self.bcc)31 yield ('email', None, False, self.bcc) 32 32 33 33 class LegacyTicketSubscriber(Component): … … 103 103 ## TODO: Is this an option? 104 104 self.log.debug("LegacyTicketSubscriber added '%s' because of rule: component owner" % (component.owner,)) 105 yield ('email', component.owner, None)106 105 yield ('email', component.owner, True, None) 106 107 107 if self.always_notify_owner and ticket['owner'] and not self._check_opt_out('notify_owner', ticket['owner']): 108 self.log.debug("LegacyTicketSubscriber added '%s' because of rule: always_notify_owner" % ticket['owner']) 109 yield ('email', ticket['owner'], None) 108 owner = ticket['owner'] 109 if '@' in owner: 110 name, authenticated, address = None, False, owner 111 else: 112 name, authenticated, address = owner, True, None 113 114 self.log.debug( 115 "LegacyTicketSubscriber added '%s (%s)' because of rule: always_notify_owner" % ( 116 owner, authenticated and 'authenticated' or 'not authenticated' 117 ) 118 ) 119 yield ('email', name, authenticated, address) 110 120 111 121 if self.always_notify_reporter and ticket['reporter'] and not self._check_opt_out('notify_reporter', ticket['reporter']): 112 self.log.debug("LegacyTicketSubscriber added '%s' because of rule: always_notify_reporter" % ticket['reporter']) 113 yield ('email', ticket['reporter'], None) 122 reporter = ticket['reporter'] 123 if '@' in reporter: 124 name, authenticated, address = None, False, reporter 125 else: 126 name, authenticated, address = reporter, True, None 127 128 self.log.debug( 129 "LegacyTicketSubscriber added '%s (%s)' because of rule: always_notify_reporter" % ( 130 reporter, authenticated and 'authenticated' or 'not authenticated' 131 ) 132 ) 133 yield ('email', name, authenticated, address) 114 134 115 135 if self.always_notify_updater and event.author and not self._check_opt_out('notify_updater', event.author): 116 self.log.debug("LegacyTicketSubscriber added '%s' because of rule: always_notify_updater" % event.author) 117 yield ('email', event.author, None) 136 self.log.debug("LegacyTicketSubscriber added '%s (authenticated)' because of rule: always_notify_updater" % event.author) 137 yield ('email', event.author, True, None) 138 118 139 return 119 140 … … 147 168 def get_subscription_categories(self, realm): 148 169 if realm == 'ticket': 149 return ('changed', 'attachment added') 170 yield 'changed' 171 yield 'attachment added' 172 return 150 173 151 174 def get_subscriptions_for_event(self, event): … … 167 190 if name or address: 168 191 self.log.debug("CarbonCopySubscriber added '%s <%s>' because of rule: carbon copied" % (name,address)) 169 yield ('email', name, address) 192 yield ('email', name, name and True or False, address) 193 announcerplugin/0.11/announcerplugin/subscribers/ticket_groups.py
r3047 r3107 19 19 return ('ticket',) 20 20 21 def get_subscription_categories(self, *args): 22 return ('changed', 'created', 'attachment added') 21 def get_subscription_categories(self, realm): 22 if realm == "ticket": 23 yield 'changed' 24 yield 'created' 25 yield 'attachment added' 23 26 24 27 def get_subscriptions_for_event(self, event): … … 31 34 member = None 32 35 for member in self._get_membership(chunk[1:]): 33 self.log.debug("JoinableGroupSubscriber added '%s' because of opt-in to group: %s" % (member[1], chunk[1:])) 36 self.log.debug( 37 "JoinableGroupSubscriber added '%s (%s)' because of opt-in to group: %s" % ( 38 member[1], member[2] and 'authenticated' or 'not authenticated', chunk[1:] 39 ) 40 ) 34 41 yield member 35 42 … … 42 49 43 50 cursor.execute(""" 44 SELECT sid 51 SELECT sid, authenticated 45 52 FROM session_attribute 46 WHERE authenticated=147 53 AND name=%s 48 54 AND value=%s … … 50 56 51 57 for result in cursor.fetchall(): 52 yield ("email", result[0], None) 58 if result[1] in (1, '1', True): 59 authenticated = True 60 else: 61 authenticated = False 62 yield ("email", result[0], authenticated, None) 53 63 54 64 def get_announcement_preference_boxes(self, req): 65 if req.authname == "anonymous" and 'email' not in req.session: 66 return 67 55 68 if self.joinable_groups: 56 69 yield "joinable_groups", "Joinable Groups (Opt-In)" 70 57 71 return 58 72 59 73 def render_announcement_preference_box(self, req, panel): 60 74 cfg = self.config announcerplugin/0.11/announcerplugin/subscribers/watchers.py
r3090 r3107 136 136 self._add_notice(req) 137 137 138 if req.authname != "anonymous" :138 if req.authname != "anonymous" or (req.authname == 'anonymous' and 'email' in req.session): 139 139 for pattern in self.watchable_paths: 140 140 path = self.normalise_resource(req.path_info) 141 141 if re.match(pattern, path): 142 142 realm, _ = path.split('/', 1) 143 143 144 144 if '%s_VIEW' % realm.upper() not in req.perm: 145 145 return handler … … 147 147 self.render_watcher(req) 148 148 break 149 149 150 150 return handler 151 151 152 152 def post_process_request(self, req, template, data, content_type): 153 153 return (template, data, content_type) … … 181 181 182 182 # IWikiChangeListener 183 def wiki_page_added( self, page):184 pass 185 186 def wiki_page_changed( self, page, version, t, comment, author, ipnr):187 pass 188 189 def wiki_page_deleted( page):183 def wiki_page_added(*args): 184 pass 185 186 def wiki_page_changed(*args): 187 pass 188 189 def wiki_page_deleted(self, page): 190 190 db = self.env.get_db_cnx() 191 191 … … 202 202 db.commit() 203 203 204 def wiki_page_version_deleted( page):204 def wiki_page_version_deleted(*args): 205 205 pass 206 206 207 207 # ITicketChangeListener 208 208 209 def ticket_created( self, ticket):210 pass 211 212 def ticket_changed( self, ticket, comment, author, old_values):209 def ticket_created(*args): 210 pass 211 212 def ticket_changed(*args): 213 213 pass 214 214 … … 243 243 244 244 cursor.execute(""" 245 SELECT transport, sid 245 SELECT transport, sid, authenticated 246 246 FROM subscriptions 247 247 WHERE enabled=1 AND managed=%s … … 251 251 """, ('watcher', event.realm, '*', self._get_target_identifier(event.realm, event.target))) 252 252 253 for transport, sid in cursor.fetchall(): 254 self.log.debug("WatchSubscriber added '%s' because of rule: watched" % (sid,)) 255 yield (transport, sid, None) 253 for transport, sid, authenticated in cursor.fetchall(): 254 self.log.debug("WatchSubscriber added '%s (%s)' because of rule: watched" % ( 255 sid,authenticated and 'authenticated' or 'not authenticated' 256 ) 257 ) 258 yield (transport, sid, authenticated, None) 256 259 257 260 def _get_target_identifier(self, realm, target): announcerplugin/0.11/announcerplugin/subscribers/wiki.py
r3091 r3107 11 11 def get_subscription_realms(self): 12 12 return ('wiki',) 13 14 def get_subscription_categories(self, *args): 15 return ('changed', 'created', 'attachment added', 'deleted', 'version deleted') 16 13 14 def get_subscription_categories(self, realm): 15 if realm == "wiki": 16 return ('changed', 'created', 'attachment added', 'deleted', 'version deleted') 17 else: 18 return tuple() 19 17 20 def get_subscriptions_for_event(self, event): 18 21 if event.realm == 'wiki': 19 22 if event.category in self.get_subscription_categories(event.realm): 20 23 page = event.target 21 for name in self._get_membership(page.name):22 yield ('email', name, None)23 24 for name, authenticated in self._get_membership(page.name): 25 yield ('email', name, authenticated, None) 26 24 27 def _get_membership(self, name): 25 28 db = self.env.get_db_cnx() … … 27 30 28 31 cursor.execute(""" 29 SELECT sid, value32 SELECT sid, authenticated, value 30 33 FROM session_attribute 31 WHERE authenticated=1 32 AND name=%s 34 WHERE name=%s 33 35 """, ('announcer_wiki_interests', )) 34 36 35 for resultin cursor.fetchall():36 for raw in result[1].split(' '):37 for sid, authenticated, value in cursor.fetchall(): 38 for raw in value.split(' '): 37 39 pat = urllib.unquote(raw) 38 40 if re.match(pat, name): 39 41 self.log.debug( 40 "GeneralWikiSubscriber added '%s ' because name '%s' matches pattern: %s" % (41 result[0], name, pat42 "GeneralWikiSubscriber added '%s (%s)' because name '%s' matches pattern: %s" % ( 43 sid, authenticated and 'authenticated' or 'not authenticated', name, pat 42 44 ) 43 45 ) 44 yield result[0]45 46 yield sid, authenticated 47 46 48 def get_announcement_preference_boxes(self, req): 47 49 yield "general_wiki", "General Wiki Announcements" 48 50 49 51 def render_announcement_preference_box(self, req, panel): 50 52 sess = req.session announcerplugin/0.11/announcerplugin/templates/ticket_email_mimic.html
r3046 r3107 145 145 </div> 146 146 <py:if test="has_changes"> 147 <div class="changetitle" style="font-size: small; margin: 1em;">Changes (by ${author}):</div>147 <div class="changetitle" style="font-size: small; margin: 1em;">Changes: (by <strong>${author}</strong>)</div> 148 148 <ul> 149 149 <li py:for="change in short_changes" class="changeitem" style="font-size: small;"> … … 167 167 </py:if> 168 168 <py:if test="comment"> 169 <div class="commentstitle" style="font-size: small; margin: 1em;">Comments: </div>169 <div class="commentstitle" style="font-size: small; margin: 1em;">Comments: <span py:if="not has_changes">(by <strong>${author}</strong>)</span></div> 170 170 <div class="comments" style="font-size: small; margin: 0 0 0 2em; font-style: italic;">${comment}</div> 171 171 </py:if> announcerplugin/0.11/announcerplugin/templates/wiki_email_plaintext.txt
r3047 r3107 1 2 * The user '${author}' has changed the ${page.name} page. 1 {% choose %}\ 2 {% when action == "created" %} * The user '${author}' has created the page: ${page.name}. {% end %}\ 3 {% when action == "changed" %} * The user '${author}' has changed the page: ${page.name}. 4 * Diff link: <URL:${diff_link}> 5 {% end %}\ 6 {% when action == "attachment added" %} * The user '${author}' has added the attachment '${event.attachment.name}' to the page: ${page.name}. {% end %}\ 7 {% when action == "version deleted" %} * The page '${page.name}' has been reverted to its previous version. {% end %}\ 8 {% when action == "deleted" %} * The '${page.name}' has been deleted. {% end %}\ 9 {% end %} 3 10 4 11 -- announcerplugin/0.11/setup.py
r3064 r3107 32 32 setup( 33 33 name = 'AnnouncerPlugin', 34 version = '0. 1',34 version = '0.2', 35 35 author = 'Stephen Hansen', 36 36 author_email = 'shansen@advpubtech.com', … … 47 47 'trac.plugins': [ 48 48 'announcerplugin = announcerplugin', 49 # 'announcerplugin.api = announcerplugin.api',50 # 'announcerplugin.producers = announcerplugin.producers',51 # 'announcerplugin.producers.ticket = announcerplugin.producers.ticket',52 # 'announcerplugin.producers.attachment = announcerplugin.producers.attachment',53 # 'announcerplugin.subscribers = announcerplugin.subscribers',54 # 'announcerplugin.subscribers.ticket = announcerplugin.subscribers.ticket',55 # 'announcerplugin.subscribers.ticket_compat = announcerplugin.subscribers.ticket_compat',56 # 'announcerplugin.pref = announcerplugin.pref'57 49 ] 58 50 }
