source: quietplugin/0.12/quiet/web_ui.py

Last change on this file was 14029, checked in by Ryan J Ollos, 9 years ago

1.0.1: Change license to 3-Clause BSD with permission of author

Refs #11832.

File size: 6.1 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 genshi.builder import tag
13from trac.config import Option
14from trac.core import *
15from trac.perm import IPermissionRequestor
16from trac.util.translation import _
17from trac.web.chrome import ITemplateProvider, add_ctxtnav, add_script, \
18                            add_script_data, add_stylesheet
19from trac.web.main import IRequestFilter, IRequestHandler
20
21from announcer.distributors.mail import EmailDistributor
22
23
24MODE = 'quietmode'
25LISTEN = 'quietlisten'
26
27
28# WARNING: dependency on Announcer plugin!
29class QuietEmailDistributor(EmailDistributor):
30    """Specializes Announcer's email distributor to honor quiet mode."""
31    def distribute(self, transport, recipients, event):
32        if hasattr(event, 'author') and self._is_quiet_mode(event.author):
33            return
34        EmailDistributor.distribute(self, transport, recipients, event)
35
36    def _is_quiet_mode(self, user):
37        db = self.env.get_db_cnx()
38        cursor = db.cursor()
39        cursor.execute("""
40            SELECT value
41              FROM session_attribute
42             WHERE sid=%s
43               AND name=%s
44        """, (user, MODE))
45        result = cursor.fetchone()
46        if not result:
47            return False
48        return result[0] == '1'
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 req.perm.has_permission('QUIET_MODE') and \
104                (req.path_info.startswith('/ticket') or
105                 req.path_info.startswith('/newticket') or
106                 req.path_info.startswith('/changeset') or
107                 req.path_info.startswith('/query') or
108                 req.path_info.startswith('/report')):
109            href = req.href(MODE, 'toggle')
110            a = tag.a(self._get_label(req), href=href, id=MODE)
111            add_ctxtnav(req, a)
112            add_script(req, 'quiet/quiet.js')
113            add_stylesheet(req, 'quiet/quiet.css')
114            add_script_data(req, {'quiet': {'toggle': MODE,
115                                            'listen': LISTEN}})
116        return template, data, content_type
117
118
119class QuietAjaxModule(Component, QuietBase):
120    implements(IRequestHandler)
121
122    # IRequestHandler methods
123    def match_request(self, req):
124        return req.path_info.startswith('/' + MODE)
125
126    def process_request(self, req):
127        try:
128            action = req.path_info[req.path_info.rfind('/') + 1:]
129            is_quiet = self._set_quiet_action(req, action)
130            data = {'label': self._get_label(req, is_quiet),
131                    'is_quiet': is_quiet}
132            process_json(req, data)
133        except Exception:
134            process_error(req)
135
136
137class QuietListenerAjaxModule(Component):
138    implements(IRequestHandler)
139
140    # IRequestHandler methods
141    def match_request(self, req):
142        return req.path_info.startswith('/' + LISTEN)
143
144    def process_request(self, req):
145        try:
146            data = self._get_listeners(req)
147            process_json(req, data)
148        except Exception:
149            process_error(req)
150
151    def _get_listeners(self, req):
152        listeners = []
153        for key, action in self.env.config.options('quiet'):
154            if not key.endswith('.action'):
155                continue
156            num = key.split('.', 1)[0]
157            only, eq = self.env.config.get('quiet', num + '.only_if', ''), ''
158            if only and '=' in only:
159                only, eq = only.split('=', 1)
160            submit = self.env.config.get('quiet', num+'.submit',
161                                         'false').lower()
162            listeners.append({
163                'action': action,
164                'selector': self.env.config.get('quiet',
165                                                num + '.selector', ''),
166                'only': only, 'eq': eq,
167                'submit': submit == 'true',
168            })
169        return listeners
170
171
172# utils
173def process_json(req, data):
174    try:
175        process_msg(req, 200, 'application/json', json.dumps(data))
176    except Exception:
177        process_error(req)
178
179
180def process_error(req):
181    import traceback
182    msg = "Oops...\n" + traceback.format_exc() + "\n"
183    process_msg(req, 500, 'text/plain', msg)
184
185
186def process_msg(req, code, type, msg):
187    req.send_response(code)
188    req.send_header('Content-Type', type)
189    req.send_header('Content-Length', len(msg))
190    req.end_headers()
191    req.write(msg)
Note: See TracBrowser for help on using the repository browser.