source: quietplugin/1.2/quiet/web_ui.py

Last change on this file was 16352, checked in by Ryan J Ollos, 7 years ago

1.2.1dev: Check QUIET_MODE when distributing email

Also, use authenticated flag when checking quiet mode because
event.author is never a session id, so quiet mode will only
work for authenticated users.

Patch by Jun Omae.

Refs #13117.

File size: 6.3 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2011-2012 Rob Guttman <guttman@alum.mit.edu>
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8#
9
10import json
11
12from trac.config import Option
13from trac.core import Component, implements
14from trac.notification.mail import EmailDistributor
15from trac.perm import IPermissionRequestor, PermissionCache
16from trac.util.html import html
17from trac.util.translation import _
18from trac.web.chrome import ITemplateProvider, add_ctxtnav, add_script, \
19                            add_script_data, add_stylesheet
20from trac.web.main import IRequestFilter, IRequestHandler
21
22MODE = 'quietmode'
23LISTEN = 'quietlisten'
24
25
26class QuietEmailDistributor(EmailDistributor):
27    """Specializes Announcer's email distributor to honor quiet mode."""
28    def distribute(self, transport, recipients, event):
29        if hasattr(event, 'author') and \
30                self._is_quiet_mode(event.author) and \
31                'QUIET_MODE' in PermissionCache(self.env, event.author):
32            self.log.debug("%s skipping distribution of %s because quiet "
33                           "mode is enabled for %s", self.__class__.__name__,
34                           event.__class__.__name__, event.author)
35            return
36        self.log.debug("%s dispatching to EmailDistributor",
37                       self.__class__.__name__)
38        super(QuietEmailDistributor, self).distribute(transport, recipients,
39                                                      event)
40
41    def _is_quiet_mode(self, user):
42        for val, in self.env.db_query("""
43                SELECT value FROM session_attribute
44                WHERE sid=%s AND authenticated=1 AND name=%s
45                """, (user, MODE)):
46            return val == '1'
47        else:
48            return False
49
50
51class QuietBase(object):
52    """Shared class for common methods."""
53
54    enter_label = Option('quiet', 'enter_label', _('Enter Quiet Mode'))
55    leave_label = Option('quiet', 'leave_label', _('Leave Quiet Mode'))
56
57    def _get_label(self, req, is_quiet=None):
58        if is_quiet is None:
59            is_quiet = self._is_quiet(req)
60        return is_quiet and _(self.leave_label) or _(self.enter_label)
61
62    def _set_quiet_action(self, req, action):
63        if action == 'toggle':
64            return self._set_quiet(req, not self._is_quiet(req))
65        elif action in ('enter', 'leave'):
66            return self._set_quiet(req, action == 'enter')
67        else:
68            return self._is_quiet(req)
69
70    def _is_quiet(self, req):
71        """Returns true if the user requested quiet mode."""
72        val = req.session.get(MODE, '0')
73        return val == '1'
74
75    def _set_quiet(self, req, yes):
76        """Set or unset quiet mode for the user."""
77        val = yes and '1' or '0'
78        req.session[MODE] = val
79        req.session.save()
80        return val == '1'
81
82
83class QuietModule(Component, QuietBase):
84    implements(IRequestFilter, ITemplateProvider, IPermissionRequestor)
85
86    # IPermissionRequestor methods
87    def get_permission_actions(self):
88        return ['QUIET_MODE']
89
90    # ITemplateProvider methods
91    def get_htdocs_dirs(self):
92        from pkg_resources import resource_filename
93        return [('quiet', resource_filename(__name__, 'htdocs'))]
94
95    def get_templates_dirs(self):
96        return []
97
98    # IRequestFilter methods
99    def pre_process_request(self, req, handler):
100        return handler
101
102    def post_process_request(self, req, template, data, content_type):
103        if 'QUIET_MODE' in req.perm and \
104                req.path_info.startswith(('/ticket', '/newticket',
105                                          '/changeset', '/query', '/report')):
106            href = req.href(MODE, 'toggle')
107            a = html.a(self._get_label(req), href=href, id=MODE)
108            add_ctxtnav(req, a)
109            add_script(req, 'quiet/quiet.js')
110            add_stylesheet(req, 'quiet/quiet.css')
111            add_script_data(req, {'quiet': {'toggle': MODE,
112                                            'listen': LISTEN}})
113        return template, data, content_type
114
115
116class QuietAjaxModule(Component, QuietBase):
117    implements(IRequestHandler)
118
119    # IRequestHandler methods
120    def match_request(self, req):
121        return req.path_info.startswith('/' + MODE)
122
123    def process_request(self, req):
124        try:
125            action = req.path_info[req.path_info.rfind('/') + 1:]
126            is_quiet = self._set_quiet_action(req, action)
127            data = {'label': self._get_label(req, is_quiet),
128                    'is_quiet': is_quiet}
129            process_json(req, data)
130        except Exception:
131            process_error(req)
132
133
134class QuietListenerAjaxModule(Component):
135    implements(IRequestHandler)
136
137    # IRequestHandler methods
138    def match_request(self, req):
139        return req.path_info.startswith('/' + LISTEN)
140
141    def process_request(self, req):
142        try:
143            data = self._get_listeners(req)
144            process_json(req, data)
145        except Exception:
146            process_error(req)
147
148    def _get_listeners(self, req):
149        listeners = []
150        for key, action in self.env.config.options('quiet'):
151            if not key.endswith('.action'):
152                continue
153            num = key.split('.', 1)[0]
154            only, eq = self.env.config.get('quiet', num + '.only_if', ''), ''
155            if only and '=' in only:
156                only, eq = only.split('=', 1)
157            submit = self.env.config.get('quiet', num+'.submit',
158                                         'false').lower()
159            listeners.append({
160                'action': action,
161                'selector': self.env.config.get('quiet',
162                                                num + '.selector', ''),
163                'only': only, 'eq': eq,
164                'submit': submit == 'true',
165            })
166        return listeners
167
168
169def process_json(req, data):
170    try:
171        process_msg(req, 200, 'application/json', json.dumps(data))
172    except Exception:
173        process_error(req)
174
175
176def process_error(req):
177    import traceback
178    msg = "Oops...\n" + traceback.format_exc() + "\n"
179    process_msg(req, 500, 'text/plain', msg)
180
181
182def process_msg(req, code, type, msg):
183    req.send_response(code)
184    req.send_header('Content-Type', type)
185    req.send_header('Content-Length', len(msg))
186    req.end_headers()
187    req.write(msg)
Note: See TracBrowser for help on using the repository browser.