source: watchlistplugin/0.12/tracwatchlist/plugin.py

Last change on this file was 15264, checked in by Ryan J Ollos, 8 years ago

Remove unnecessary svn:mime-type on py files

svn:mime-type was set to "plain" for many files.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id Date Author Rev URL
File size: 30.4 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3= Watchlist Plugin for Trac =
4Plugin Website:  http://trac-hacks.org/wiki/WatchlistPlugin
5Trac website:    http://trac.edgewall.org/
6
7Copyright (c) 2008-2010 by Martin Scharrer <martin@scharrer-online.de>
8All rights reserved.
9
10The i18n support was added by Steffen Hoffmann <hoff.st@web.de>.
11
12This program is free software: you can redistribute it and/or modify
13it under the terms of the GNU General Public License as published by
14the Free Software Foundation, either version 3 of the License, or
15(at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20GNU General Public License for more details.
21
22For a copy of the GNU General Public License see
23<http://www.gnu.org/licenses/>.
24
25$Id: plugin.py 15264 2016-02-11 04:22:34Z rjollos $
26"""
27
28__url__      = ur"$URL: //trac-hacks.org/svn/watchlistplugin/0.12/tracwatchlist/plugin.py $"[6:-2]
29__author__   = ur"$Author: rjollos $"[9:-2]
30__revision__ = int("0" + ur"$Rev: 15264 $"[6:-2].strip('M'))
31__date__     = ur"$Date: 2016-02-11 04:22:34 +0000 (Thu, 11 Feb 2016) $"[7:-2]
32
33from  pkg_resources          import  resource_filename
34from  datetime               import  datetime
35
36from  trac.core              import  *
37from  genshi.builder         import  tag
38from  trac.config            import  BoolOption, ListOption
39from  trac.util.text         import  to_unicode
40from  trac.web.api           import  IRequestFilter, IRequestHandler, \
41                                     RequestDone, HTTPNotFound, HTTPBadRequest
42from  trac.web.chrome        import  ITemplateProvider, add_ctxtnav, \
43                                     add_notice
44from  trac.wiki.model        import  WikiPage
45
46from  tracwatchlist.api      import  IWatchlistProvider
47from  tracwatchlist.translation import  add_domain, _, N_, t_, tag_, gettext,\
48                                        i18n_enabled
49from  tracwatchlist.util     import  ensure_string, ensure_iter, LC_TIME,\
50                                     datetime_format, current_timestamp
51from  tracwatchlist.manual   import  WatchlistManual
52
53
54class WatchlistPlugin(Component):
55    """Main class of the Trac WatchlistPlugin.
56
57    Displays watchlist for wiki pages, ticket and possible other Trac realms.
58
59    For documentation see http://trac-hacks.org/wiki/WatchlistPlugin.
60    """
61    providers = ExtensionPoint(IWatchlistProvider)
62
63    implements( IRequestHandler, IRequestFilter, ITemplateProvider )
64
65    bool_options = [
66        BoolOption('watchlist', 'attachment_changes'                   , True , doc=N_("Take attachment changes into account")),
67        BoolOption('watchlist', 'notifications'                        , False, doc=N_("Notifications")),
68        BoolOption('watchlist', 'display_notify_navitems'              , False, doc=N_("Display notification navigation items")),
69        BoolOption('watchlist', 'display_notify_column'                , True , doc=N_("Display notification column in watchlist tables")),
70        BoolOption('watchlist', 'notify_by_default'                    , False, doc=N_("Enable notifications by default for all watchlist entries")),
71        BoolOption('watchlist', 'stay_at_resource'                     , False, doc=N_("The user stays at the resource after a watch/unwatch operation and the watchlist page is not displayed")),
72        BoolOption('watchlist', 'stay_at_resource_notify'              , True , doc=N_("The user stays at the resource after a notify/do-not-notify operation and the watchlist page is not displayed")),
73        BoolOption('watchlist', 'show_messages_on_resource_page'       , True , doc=N_("Action messages are shown on resource pages")),
74        BoolOption('watchlist', 'show_messages_on_watchlist_page'      , True , doc=N_("Action messages are shown when going to the watchlist page")),
75        BoolOption('watchlist', 'show_messages_while_on_watchlist_page', True , doc=N_("Show action messages while on watchlist page")),
76        BoolOption('watchlist', 'autocomplete_inputs'                  , True , doc=N_("Autocomplete input fields (add/remove resources)")),
77        BoolOption('watchlist', 'dynamic_tables'                       , True , doc=N_("Dynamic watchlist tables")),
78        BoolOption('watchlist', 'datetime_picker'                      , True , doc=N_("Provide date/time picker application")),
79        BoolOption('watchlist', 'individual_column_filtering'          , True , doc=N_("Individual column filtering")),
80    ]
81
82    list_options = [
83        ListOption('watchlist', 'realm_order', 'wiki,ticket', doc=N_("Display only the given watchlist sections in the given order"))
84    ]
85
86    wsub = None
87
88
89    def __init__(self):
90        # bind the 'watchlist' catalog to the specified locale directory
91        try:
92            locale_dir = resource_filename(__name__, 'locale')
93        except KeyError:
94            pass
95        else:
96            add_domain(self.env.path, locale_dir)
97
98        #
99        self.realms = []
100        self.realm_handler = {}
101        for provider in self.providers:
102            for realm in provider.get_realms():
103                assert realm not in self.realms
104                self.realms.append(realm)
105                self.realm_handler[realm] = provider
106
107        try:
108            # Import methods from WatchSubscriber of the AnnouncerPlugin
109            from  announcerplugin.subscribers.watchers  import  WatchSubscriber
110            self.wsub = self.env[WatchSubscriber]
111            if self.wsub:
112                self.log.debug("WS: WatchSubscriber found in announcerplugin")
113        except Exception, e:
114            try:
115                # Import fallback methods for AnnouncerPlugin 1.0
116                from announcer.opt.subscribers import WatchSubscriber
117                self.wsub = self.env[WatchSubscriber]
118                if self.wsub:
119                    self.log.debug("WS: WatchSubscriber found in announcer")
120            except Exception, ee:
121                self.log.debug("WS! " + str(e))
122                self.log.debug("WS! " + str(ee))
123                self.wsub = None
124
125
126    ## User settings ########################################################
127    def get_settings(self, user):
128        settings = {}
129        settings['booloptions'] = dict([
130            ( option.name, self.config.getbool('watchlist',option.name,option.default) )
131                for option in self.bool_options ])
132        settings['booloptions_doc'] = dict([ (option.name,gettext(option.__doc__)) for option in self.bool_options ])
133        settings['booloptions_order'] = [ option.name for option in self.bool_options ]
134        settings['listoptions'] = dict([
135            ( option.name, self.config.getlist('watchlist',option.name,option.default) )
136                for option in self.list_options ])
137        settings['listoptions_doc'] = dict([ (option.name,gettext(option.__doc__)) for option in self.list_options ])
138        settings['listoptions_order'] = [ option.name for option in self.list_options ]
139        usersettings = self._get_user_settings(user)
140        if 'booloptions' in usersettings:
141            settings['booloptions'].update( usersettings['booloptions'] )
142            del usersettings['booloptions']
143        for l in settings['listoptions'].keys():
144            if l in usersettings:
145                settings['listoptions'][l] = usersettings[l]
146                del usersettings[l]
147        settings.update( usersettings )
148        return settings
149
150
151    def _delete_user_settings(self, user):
152        """Deletes all user settings in 'watchlist_settings' table.
153           This can be done to reset all settings to the default values
154           and to resolve possible errors with wrongly stored settings.
155           This can happen while using the develop version of this plugin."""
156        db = self.env.get_db_cnx()
157        cursor = db.cursor()
158
159        cursor.execute("""
160          DELETE
161            FROM watchlist_settings
162           WHERE wluser=%s
163        """, (user,))
164        db.commit()
165        return
166
167
168    def _save_user_settings(self, user, settings):
169        """Saves user settings in 'watchlist_settings' table.
170           Only saving of all user settings is supported at the moment."""
171        db = self.env.get_db_cnx()
172        cursor = db.cursor()
173        options = settings['booloptions']
174
175        settingsstr = "&".join([ "=".join([k,unicode(v)])
176                            for k,v in options.iteritems()])
177
178        cursor.execute("""
179          DELETE
180            FROM watchlist_settings
181           WHERE wluser=%s
182        """, (user,))
183
184        cursor.execute("""
185          INSERT
186            INTO watchlist_settings (wluser,name,type,settings)
187          VALUES (%s,'booloptions','ListOfBool',%s)
188          """, (user, settingsstr) )
189
190        cursor.executemany("""
191          INSERT
192            INTO watchlist_settings (wluser,name,type,settings)
193          VALUES (%s,%s,'ListOfStrings',%s)
194          """, [(user, realm + '_fields', ','.join(settings[realm + '_fields']))
195                for realm in self.realms if realm + '_fields' in settings ] )
196
197        cursor.executemany("""
198          INSERT
199            INTO watchlist_settings (wluser,name,type,settings)
200          VALUES (%s,%s,'ListOfStrings',%s)
201          """, [(user, name, ','.join(value))
202                for name,value in settings.get('listoptions',{}).iteritems() ])
203
204        db.commit()
205        return True
206
207
208    def _get_user_settings(self, user):
209        db = self.env.get_db_cnx()
210        cursor = db.cursor()
211        cursor.execute("""
212          SELECT name,type,settings
213            FROM watchlist_settings
214           WHERE wluser=%s
215        """, (user,))
216
217        settings = dict()
218        for name,type,settingsstr in cursor.fetchall():
219            if type == 'ListOfBool':
220                settings[name] = dict([
221                    (k,v=='True') for k,v in
222                        [ kv.split('=') for kv in settingsstr.split("&") ] ])
223            elif type == 'ListOfStrings':
224                settings[name] = filter(None,settingsstr.split(','))
225            else:
226                settings[name] = settingsstr
227        return settings
228
229
230    ## Change/access watch status ###########################################
231    def has_watchlist(self, user):
232        """Checks if user has a non-empty watchlist."""
233        db = self.env.get_db_cnx()
234        cursor = db.cursor()
235        cursor.execute("""
236          SELECT count(*)
237            FROM watchlist
238           WHERE wluser=%s;
239        """, (user,)
240        )
241        count = cursor.fetchone()
242        if not count or not count[0]:
243            return False
244        else:
245            return True
246
247
248    def get_watched_resources(self, realm, user, db=None):
249        """Returns list of resources watched by the given user in the given realm.
250           The list contains a list with the resource id and the last time it
251           got visited."""
252        db = db or self.env.get_db_cnx()
253        cursor = db.cursor()
254        cursor.execute("""
255            SELECT resid,lastvisit
256                FROM watchlist
257            WHERE realm=%s AND wluser=%s
258        """, (realm, user))
259        return cursor.fetchall()
260
261
262    def is_watching(self, realm, resid, user, db=None):
263        """Checks if user watches the given resource(s).
264           Returns True/False for a single resource or
265           a list of watched resources."""
266        db = db or self.env.get_db_cnx()
267        cursor = db.cursor()
268        if getattr(resid, '__iter__', False):
269            reses = list(resid)
270            if not reses:
271                return []
272            cursor.execute("""
273                SELECT resid
274                FROM watchlist
275                WHERE wluser=%s AND realm=%s AND
276                        resid IN (
277            """ + ",".join(("%s",) * len(reses)) + ")",
278            [user,realm] + reses)
279            return [ res[0] for res in cursor.fetchall() ]
280        else:
281            cursor.execute("""
282                SELECT count(*)
283                  FROM watchlist
284                 WHERE realm=%s AND resid=%s AND wluser=%s;
285            """, (realm, to_unicode(resid), user)
286            )
287            count = cursor.fetchone()
288            if not count or not count[0]:
289                return False
290            else:
291                return True
292
293
294    def watch(self, realm, resid, user, lastvisit=0, db=None):
295        """Adds given resources to watchlist.
296           They must not be watched already."""
297        db = db or self.env.get_db_cnx()
298        cursor = db.cursor()
299        cursor.executemany("""
300            INSERT
301            INTO watchlist (wluser, realm, resid, lastvisit)
302            VALUES (%s,%s,%s,%s)
303        """, [(user, realm, res, lastvisit) for res in ensure_iter(resid)])
304
305
306    def unwatch(self, realm, resid, user, db=None):
307        db = db or self.env.get_db_cnx()
308        cursor = db.cursor()
309        cursor.log = self.log
310        self.log.debug("resid = " + unicode(resid))
311        reses = list(ensure_iter(resid))
312        cursor.execute("""
313            DELETE
314            FROM watchlist
315            WHERE wluser=%s AND realm=%s AND
316                    resid IN (
317        """ + ",".join(("%s",) * len(reses)) + ")",
318        [user,realm] + reses)
319
320
321    def visiting(self, realm, resid, user, db=None):
322        """Marks the given resource as visited just now."""
323        db = db or self.env.get_db_cnx()
324        cursor = db.cursor()
325        now = current_timestamp()
326        cursor.execute("""
327          UPDATE watchlist
328             SET lastvisit=%s
329           WHERE realm=%s AND resid=%s AND wluser=%s;
330        """, (now, realm, to_unicode(resid), user)
331        )
332        db.commit()
333        return
334
335
336    ## Change/access notification status ####################################
337    def is_notify(self, req, realm, resid):
338        try:
339            return self.wsub.is_watching(req.session.sid, True, realm, resid)
340        except AttributeError:
341            return False
342        except Exception, e:
343            self.log.error("is_notify error: " + str(e))
344            return False
345
346
347    def set_notify(self, req, realm, resid):
348        try:
349            self.wsub.set_watch(req.session.sid, True, realm, resid)
350        except AttributeError:
351            return False
352        except Exception, e:
353            self.log.error("set_notify error: " + str(e))
354
355
356    def unset_notify(self, req, realm, resid):
357        try:
358            self.wsub.set_unwatch(req.session.sid, True, realm, resid)
359        except AttributeError:
360            return False
361        except Exception, e:
362            self.log.error("unset_notify error: " + str(e))
363
364
365    ## Methods for IRequestHandler ##########################################
366    def match_request(self, req):
367        return req.path_info == "/watchlist"
368
369
370    def process_request(self, req):
371        """Processes requests to the '/watchlist' path."""
372        user  = to_unicode( req.authname )
373
374        # Reject anonymous users
375        if not user or user == 'anonymous':
376            # TRANSLATOR: Link part of
377            # "Please %(log_in)s to view or change your watchlist"
378            log_in=tag.a(_("log in"), href=req.href('login'))
379            if tag_ == None:
380                # For Trac 0.11
381                raise HTTPNotFound(
382                        tag("Please ", log_in, " to view or change your watchlist"))
383            else:
384                # For Trac 0.12
385                raise HTTPNotFound(
386                        tag_("Please %(log_in)s to view or change your watchlist",
387                            log_in=log_in))
388
389        # Get and format request arguments
390        realm = to_unicode( req.args.get('realm', u'') )
391        resid = ensure_string( req.args.get('resid', u'') ).strip()
392        action = req.args.get('action','view')
393        async = req.args.get('async', 'false') == 'true'
394
395        # Handle AJAX search early to speed up things
396        if action == "search":
397            """AJAX search request. At the moment only used to get list
398               of all not watched resources."""
399            handler = self.realm_handler[realm]
400            query = to_unicode( req.args.get('q', u'') ).strip()
401            group = to_unicode( req.args.get('group', u'notwatched') )
402            if not query:
403                req.send('', 'text/plain', 200 )
404            if group == 'notwatched':
405                result = list(handler.unwatched_resources(realm, query, user, self, fuzzy=1))
406            else:
407                result = list(handler.watched_resources(realm, query, user, self, fuzzy=1))
408            result.sort(cmp=handler.get_sort_cmp(realm),
409                        key=handler.get_sort_key(realm))
410            req.send( unicode(u'\n'.join(result) + u'\n').encode("utf-8"),
411                'text/plain', 200 )
412
413        # DB cursor
414        db = self.env.get_db_cnx()
415        cursor = db.cursor()
416
417        wldict = dict()
418        for k,v in req.args.iteritems():
419            try:
420                wldict[str(k)] = v
421            except:
422                pass
423
424        wldict['action'] = action
425
426        onwatchlistpage = req.environ.get('HTTP_REFERER','').find(
427                          req.href.watchlist()) != -1
428
429        settings = self.get_settings( user )
430        options = settings['booloptions']
431        self.options = options
432        # Needed here to get updated settings
433        if action == "save":
434            newoptions = req.args.get('booloptions',[])
435            for k in settings['booloptions'].keys():
436                settings['booloptions'][k] = k in newoptions
437            for realm in self.realms:
438                settings[realm + '_fields'] = req.args.get(realm + '_fields', '').split(',')
439            for l in settings['listoptions']:
440                if l in req.args:
441                    settings['listoptions'][l] = [ e.strip() for e in req.args.get(l).split(',')]
442            self._save_user_settings(req.authname, settings)
443
444            # Clear session cache for nav items
445            try:
446                # Clear session cache for nav items, so that the post processor
447                # rereads the settings
448                del req.session['watchlist_display_notify_navitems']
449            except:
450                pass
451            req.redirect(req.href('watchlist'))
452        elif action == "defaultsettings":
453            # Only execute if sent using the watchlist preferences form
454            if onwatchlistpage and req.method == 'POST':
455                self._delete_user_settings(req.authname)
456            req.redirect(req.href('watchlist'))
457
458        redirectback_notify = options['stay_at_resource_notify'] and not \
459                              onwatchlistpage and not async
460        if action == "notifyon":
461            if not self.res_exists(realm, resid):
462                raise HTTPNotFound(t_("Page %(name)s not found", name=resid))
463            elif self.wsub and options['notifications']:
464                self.set_notify(req, realm, resid)
465                db.commit()
466            if redirectback_notify:
467                if options['show_messages_on_resource_page']:
468                    req.session['watchlist_notify_message'] = _(
469                      """
470                      You are now receiving change notifications
471                      about this resource.
472                      """
473                    )
474                req.redirect(req.href(realm,resid))
475            if async:
476                req.send("",'text/plain', 200)
477            else:
478                req.redirect(req.href('watchlist'))
479        elif action == "notifyoff":
480            if self.wsub and options['notifications']:
481                self.unset_notify(req, realm, resid)
482                db.commit()
483            if redirectback_notify:
484                if options['show_messages_on_resource_page']:
485                    req.session['watchlist_notify_message'] = _(
486                      """
487                      You are no longer receiving
488                      change notifications about this resource.
489                      """
490                    )
491                req.redirect(req.href(realm,resid))
492                raise RequestDone
493            if async:
494                req.send("",'text/plain', 200)
495            else:
496                req.redirect(req.href('watchlist'))
497
498        redirectback = options['stay_at_resource'] and not onwatchlistpage
499        if action == "watch":
500            handler = self.realm_handler[realm]
501            not_found_res = list()
502            found_res = set()
503            for rid in resid.split(u','):
504                rid = rid.strip()
505                if not rid:
506                    continue
507                reses = set(handler.resources_exists(realm, rid))
508                if len(reses) == 0:
509                    not_found_res.append(rid)
510                else:
511                    found_res.update(reses)
512            watched_res = set(self.is_watching(realm, found_res, user))
513            new_res = found_res.difference(watched_res)
514            already_watched_res = found_res.intersection(watched_res)
515            comp=handler.get_sort_cmp(realm)
516            key=handler.get_sort_key(realm)
517            wldict['already_watched_res'] = sorted(already_watched_res, cmp=comp, key=key)
518            wldict['new_res'] = sorted(new_res, cmp=comp, key=key)
519            wldict['not_found_res'] = not_found_res
520
521            if new_res:
522                self.watch(realm, new_res, user, db=db)
523                db.commit()
524
525            if options['show_messages_on_resource_page'] and not onwatchlistpage and redirectback:
526                req.session['watchlist_message'] = _(
527                  "You are now watching this resource."
528                )
529            if self.wsub and options['notifications'] and options['notify_by_default']:
530                for res in new_res:
531                    self.set_notify(req, realm, res)
532                db.commit()
533            if redirectback and len(new_res) == 1:
534                req.redirect(req.href(realm, new_res.pop()))
535            action = 'view'
536
537        elif action == "unwatch":
538            handler = self.realm_handler[realm]
539            not_found_res = list()
540            not_watched_res = list()
541            del_res = list()
542            resids = resid.strip().split(u',')
543            for rid in resids:
544                rid = rid.strip()
545                if not rid:
546                    continue
547                reses = list(handler.watched_resources(realm, rid, user, self))
548                if len(reses) == 0:
549                    reses = list(handler.resources_exists(realm, rid))
550                    if len(reses) == 0:
551                        not_found_res.append(rid)
552                    else:
553                        not_watched_res.extend(reses)
554                else:
555                    del_res.extend(reses)
556            comp=handler.get_sort_cmp(realm)
557            key=handler.get_sort_key(realm)
558            wldict['del_res'] = sorted(del_res, cmp=comp, key=key)
559            wldict['not_watched_res'] = sorted(not_watched_res, cmp=comp, key=key)
560            wldict['not_found_res'] = sorted(not_found_res, cmp=comp, key=key)
561
562            if del_res:
563                self.unwatch(realm, del_res, user, db=db)
564            elif len(resids) == 1:
565                # If there where no maches and only own resid try to delete it
566                # anyway. Might be a delete wiki page.
567                self.log.debug("resid = " + unicode(resid))
568                self.unwatch(realm, [resid], user, db=db)
569
570            # Unset notification
571            if self.wsub and options['notifications'] and options['notify_by_default']:
572                for res in del_res:
573                    self.unset_notify(req, realm, res)
574            db.commit()
575            # Send an empty response for asynchronous requests
576            if async:
577                req.send("",'text/plain', 200)
578            # Redirect back to resource if so configured:
579            if redirectback and len(del_res) == 1:
580                if options['show_messages_on_resource_page']:
581                    req.session['watchlist_message'] = _(
582                    "You are no longer watching this resource."
583                    )
584                req.redirect(req.href(realm,del_res[0]))
585            action = 'view'
586
587        # Up to here all watchlist actions except 'view' got handled and
588        # either redirected the request or set the action to 'view'.
589        if action != "view":
590            raise HTTPBadRequest(_("Invalid watchlist action '%(action)s'!", action=action))
591        # Display watchlist page:
592
593        if onwatchlistpage:
594            wldict['show_messages'] = options['show_messages_while_on_watchlist_page']
595        else:
596            wldict['show_messages'] = options['show_messages_on_watchlist_page']
597
598        wldict['perm']   = req.perm
599        offset = datetime.now(req.tz).utcoffset()
600        if offset is None:
601            offset = 0
602        else:
603            offset = offset.days * 24 * 60 + offset.seconds / 60
604        wldict['tzoffset'] = offset
605        wldict['i18n'] = i18n_enabled
606        wldict['realms'] = [ r for r in settings['listoptions']['realm_order'] if r in self.realms ]
607        wldict['notifications'] = bool(self.wsub and options['notifications'] and options['display_notify_column'])
608        wldict['booloptions'] = options
609        wldict['booloptions_doc'] = settings['booloptions_doc']
610        wldict['booloptions_order'] = settings['booloptions_order']
611        wldict['listoptions'] = settings['listoptions']
612        wldict['listoptions_doc'] = settings['listoptions_doc']
613        wldict['listoptions_order'] = settings['listoptions_order']
614        wldict['wlgettext'] = gettext
615        locale = getattr(req, 'locale', None) or LC_TIME
616        wldict['datetime_format'] = datetime_format(locale=locale)
617        wldict['_'] = _
618        wldict['t_'] = t_
619        def get_label(realm, n_plural=1, astitle=False):
620            return self.realm_handler[realm].get_realm_label(realm, n_plural, astitle)
621        wldict['get_label'] = get_label
622
623        wldict['available_fields'] = {}
624        wldict['default_fields'] = {}
625        for r in self.realms:
626            wldict['available_fields'][r],wldict['default_fields'][r] = self.realm_handler[r].get_fields(r)
627        wldict['active_fields'] = {}
628        for r in self.realms:
629            cols = settings.get(r + '_fields',[])
630            if not cols:
631                cols = wldict['default_fields'].get(r,[])
632            wldict['active_fields'][r] = cols
633
634        # TRANSLATOR: Link to help/manual page of the plug-in
635        add_ctxtnav(req, _("Help"), href=(
636            self.env[WatchlistManual] and req.href.watchlist('manual')
637            or 'http://www.trac-hacks.org/wiki/WatchlistPlugin'))
638        for xrealm in wldict['realms']:
639            xhandler = self.realm_handler[xrealm]
640            if xhandler.has_perm(xrealm, req.perm):
641                wldict[str(xrealm) + 'list'], wldict[str(xrealm) + 'data'] = xhandler.get_list(xrealm, self, req, wldict['active_fields'][xrealm])
642                name = xhandler.get_realm_label(xrealm, n_plural=1000, astitle=True)
643                # TRANSLATOR: Navigation link to point to watchlist section of this realm
644                # (e.g. 'Wikis', 'Tickets').
645                add_ctxtnav(req, _("Watched %(realm_plural)s", realm_plural=name),
646                            href='#' + xrealm + 's')
647        add_ctxtnav(req, t_("Preferences"), href='#preferences')
648        return ("watchlist.html", wldict, "text/html")
649
650
651    ## Methods for IRequestFilter ###########################################
652    def pre_process_request(self, req, handler):
653        return handler
654
655
656    def post_process_request(self, req, template, data, content_type):
657        """Executed after EVERY request is processed.
658           Used to add navigation bars, display messages
659           and to note visits to watched resources."""
660        user = to_unicode( req.authname )
661        if not user or user == "anonymous":
662            return (template, data, content_type)
663
664        # Extract realm and resid from path:
665        parts = req.path_info[1:].split('/',1)
666
667        try:
668            realm, resid = parts[:2]
669        except:
670            # Handle special case for '/' and '/wiki'
671            if parts[0] == 'wiki' or (parts[0] == '' and
672               'WikiModule' == self.env.config.get('trac','default_handler') ):
673                realm, resid = 'wiki', 'WikiStart'
674            else:
675                realm, resid = parts[0], ''
676
677        if realm not in self.realms or not \
678                self.realm_handler[realm].has_perm(realm, req.perm):
679            return (template, data, content_type)
680
681        notify = 'False'
682        # The notification setting is stored in the session to avoid rereading
683        # the whole user settings for every page displayed
684        try:
685            notify = req.session['watchlist_display_notify_navitems']
686        except KeyError:
687            settings = self.get_settings(user)
688            options = settings['booloptions']
689            notify = (self.wsub and options['notifications']
690                  and options['display_notify_navitems']) and 'True' or 'False'
691            req.session['watchlist_display_notify_navitems'] = notify
692
693        try:
694            add_notice(req, req.session['watchlist_message'])
695            del req.session['watchlist_message']
696        except KeyError:
697            pass
698        try:
699            add_notice(req, req.session['watchlist_notify_message'])
700            del req.session['watchlist_notify_message']
701        except KeyError:
702            pass
703
704        if self.is_watching(realm, resid, user):
705            add_ctxtnav(req, _("Unwatch"),
706                href=req.href('watchlist', action='unwatch',
707                    resid=resid, realm=realm),
708                title=_("Remove %(document)s from watchlist",
709                    document=realm))
710            self.visiting(realm, resid, user)
711        else:
712            add_ctxtnav(req, _("Watch"),
713                href=req.href('watchlist', action='watch',
714                resid=resid, realm=realm),
715                title=_("Add %(document)s to watchlist", document=realm))
716        if notify == 'True':
717            if self.is_notify(req, realm, resid):
718                add_ctxtnav(req, _("Do not Notify me"),
719                    href=req.href('watchlist', action='notifyoff',
720                        resid=resid, realm=realm),
721                    title=_("Do not notify me if %(document)s changes",
722                        document=realm))
723            else:
724                add_ctxtnav(req, _("Notify me"),
725                    href=req.href('watchlist', action='notifyon',
726                        resid=resid, realm=realm),
727                    title=_("Notify me if %(document)s changes",
728                        document=realm))
729
730        return (template, data, content_type)
731
732
733    def res_exists(self, realm, resid):
734        return self.realm_handler[realm].resources_exists(realm, [resid])
735
736
737    ## Methods for ITemplateProvider ########################################
738    def get_htdocs_dirs(self):
739        return [('watchlist', resource_filename(__name__, 'htdocs'))]
740
741
742    def get_templates_dirs(self):
743        return [ resource_filename(__name__, 'templates') ]
744
745# EOF
Note: See TracBrowser for help on using the repository browser.