source: trunk/trac-hacks/announcerplugin/announcerplugin/subscribers/watchers.py @ 745

Revision 745, 8.9 KB checked in by rcorsaro, 4 years ago (diff)

merged latest announcer trac-hacks commits

Line 
1import re
2from trac.core import *
3from trac.config import ListOption
4from trac.web.api import IRequestFilter, IRequestHandler, Href
5from trac.web.chrome import ITemplateProvider, add_ctxtnav, add_stylesheet, \
6                            add_script
7from trac.resource import get_resource_url
8from trac.ticket.api import ITicketChangeListener
9from trac.wiki.api import IWikiChangeListener
10from genshi.builder import tag
11from announcerplugin.api import IAnnouncementSubscriber
12
13class WatchSubscriber(Component):
14
15    implements(IRequestFilter, IRequestHandler, IAnnouncementSubscriber,
16        ITicketChangeListener, IWikiChangeListener)
17
18    watchable_paths = ListOption('announcer', 'watchable_paths', 'wiki/*,ticket/*',
19        doc='List of URL paths to allow voting on. Globs are supported.')
20
21    path_match = re.compile(r'/watch/(.*)')
22
23    # IRequestHandler methods
24    def match_request(self, req):
25        if self.path_match.match(req.path_info):
26            realm = self.normalise_resource(req.path_info).split('/')[1]
27            return "%s_VIEW" % realm.upper() in req.perm
28           
29        return False
30
31    def process_request(self, req):
32        match = self.path_match.match(req.path_info)
33        resource = self.normalise_resource(match.groups()[0])
34        realm, _ = resource.split('/', 1)
35        req.perm.require('%s_VIEW' % realm.upper())
36       
37        self.toggle_watched(req.session.sid, (not req.authname == 'anonymous') and 1 or 0, resource, req)
38
39        req.redirect(req.href(resource))
40
41    def toggle_watched(self, sid, authenticated, resource, req=None):
42        realm, resource = resource.split('/', 1)
43       
44        if self.is_watching(sid, authenticated, realm, resource):
45            self.set_unwatch(sid, authenticated, realm, resource)
46            self._schedule_notice(req, 'You are no longer watching this resource for changes.')
47        else:
48            self.set_watch(sid, authenticated, realm, resource)
49            self._schedule_notice(req, 'You are now watching this resource for changes.')
50           
51    def _schedule_notice(self, req, message):
52        req.session['_announcer_watch_message_'] = message
53                   
54    def _add_notice(self, req):
55        if '_announcer_watch_message_' in req.session:
56
57            # This is temporary during 0.11b1 as add_notice was added later
58            # for the final 0.11 release.
59            try: 
60                from trac.web.chrome import add_notice
61            except:
62                from trac.web.chrome import add_warning as add_notice
63           
64            add_notice(req, req.session['_announcer_watch_message_'])
65            del req.session['_announcer_watch_message_']
66                   
67    def is_watching(self, sid, authenticated, realm, resource):
68        db = self.env.get_db_cnx()
69        cursor = db.cursor()
70       
71        cursor.execute("""
72            SELECT id
73              FROM subscriptions
74             WHERE sid=%s AND authenticated=%s
75               AND enabled=1 AND managed=%s
76               AND realm=%s
77               AND category=%s
78               AND rule=%s
79        """, (sid, int(authenticated), 'watcher', realm, '*', unicode(resource)))
80       
81        result = cursor.fetchone()
82        if result:
83            return True
84        else:
85            return False
86   
87    def set_watch(self, sid, authenticated, realm, resource):
88        db = self.env.get_db_cnx()
89        cursor = db.cursor()
90       
91        self.set_unwatch(sid, authenticated, realm, resource, use_db=db)
92
93        cursor.execute("""
94            INSERT INTO subscriptions
95                        (sid, authenticated,
96                         enabled, managed,
97                         realm, category,
98                         rule, transport)
99                 VALUES
100                        (%s, %s,
101                         1, %s,
102                         %s, %s,
103                         %s, %s)
104        """, (
105                sid, int(authenticated), 
106                'watcher', realm, '*', 
107                resource, 'email'
108            )
109        )
110       
111        db.commit()
112       
113    def set_unwatch(self, sid, authenticated, realm, resource, use_db=None):
114        if not use_db:
115            db = self.env.get_db_cnx()
116        else:
117            db = use_db
118           
119        cursor = db.cursor()
120
121        cursor.execute("""
122            DELETE
123              FROM subscriptions
124             WHERE sid=%s AND authenticated=%s
125               AND enabled=1 AND managed=%s
126               AND realm=%s
127               AND category=%s
128               AND rule=%s
129        """, (sid, int(authenticated), 'watcher', realm, '*', unicode(resource)))
130       
131        if not use_db:
132            db.commit()
133           
134    # IRequestFilter methods
135    def pre_process_request(self, req, handler):
136        self._add_notice(req)
137       
138        if req.authname != "anonymous" or (req.authname == 'anonymous' and 'email' in req.session):
139            for pattern in self.watchable_paths:
140                path = self.normalise_resource(req.path_info)
141                if re.match(pattern, path):
142                    realm, _ = path.split('/', 1)
143                   
144                    if '%s_VIEW' % realm.upper() not in req.perm:
145                        return handler
146                   
147                    self.render_watcher(req)
148                    break
149                   
150        return handler
151       
152    def post_process_request(self, req, template, data, content_type):
153        return (template, data, content_type)
154
155    # Internal methods
156    def render_watcher(self, req):
157        resource = self.normalise_resource(req.path_info)
158        realm, resource = resource.split('/', 1)
159               
160        if self.is_watching(req.session.sid, not req.authname == 'anonymous', realm, resource):
161            action_name = "Unwatch This"
162        else:
163            action_name = "Watch This"
164               
165        add_ctxtnav(req, 
166                tag.a(
167                    action_name, href=req.href.watch(realm, resource)
168                )
169        )
170
171    def normalise_resource(self, resource):
172        if isinstance(resource, basestring):
173            resource = resource.strip('/')
174            # Special-case start page
175            if not resource:
176                resource = "wiki/WikiStart"
177            elif resource == 'wiki':
178                resource += '/WikiStart'
179            return resource
180        return get_resource_url(self.env, resource, Href('')).strip('/')
181       
182    # IWikiChangeListener
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        db = self.env.get_db_cnx()
191
192        cursor = db.cursor()
193
194        cursor.execute("""
195            DELETE
196              FROM subscriptions
197             WHERE managed=%s
198               AND realm=%s
199               AND rule=%s
200        """, ('watcher', 'wiki', unicode(page.name)))
201
202        db.commit()
203
204    def wiki_page_version_deleted(*args):
205        pass
206
207    # ITicketChangeListener
208   
209    def ticket_created(*args):
210        pass
211       
212    def ticket_changed(*args):
213        pass
214       
215    def ticket_deleted(self, ticket):
216        db = self.env.get_db_cnx()
217
218        cursor = db.cursor()
219
220        cursor.execute("""
221            DELETE
222              FROM subscriptions
223             WHERE managed=%s
224               AND realm=%s
225               AND rule=%s
226        """, ('watcher', 'ticket', unicode(ticket.id)))
227
228        db.commit()
229   
230    # IAnnouncementSubscriber   
231
232    def get_subscription_realms(self):
233        return ('wiki', 'ticket')
234       
235    def get_subscription_categories(self, realm):
236        return ('created', 'changed', 'attachment added')
237       
238    def get_subscriptions_for_event(self, event):
239        if event.realm in self.get_subscription_realms():
240            if event.category in self.get_subscription_categories(event.realm):
241                db = self.env.get_db_cnx()
242                cursor = db.cursor()
243               
244                cursor.execute("""
245                    SELECT transport, sid, authenticated
246                      FROM subscriptions
247                     WHERE enabled=1 AND managed=%s
248                       AND realm=%s
249                       AND category=%s
250                       AND rule=%s
251                """, ('watcher', event.realm, '*', 
252                    unicode(self._get_target_identifier(event.realm, 
253                    event.target))))
254           
255                for transport, sid, authenticated in cursor.fetchall():
256                    self.log.debug("WatchSubscriber added '%s (%s)' because of rule: watched" % (
257                        sid,authenticated and 'authenticated' or 'not authenticated'
258                        )
259                    )
260                    yield (transport, sid, authenticated, None)
261                   
262    def _get_target_identifier(self, realm, target):
263        if realm == "wiki":
264            return target.name
265        elif realm == "ticket":
266            return target.id
Note: See TracBrowser for help on using the repository browser.