source: privatereportsplugin/trunk/privatereports/privatereports.py @ 16857

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

PrivateReports 0.4dev: Fix Genshi UnicodeError

Fixes #13292.

File size: 10.3 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2010-2012 Michael Henke <michael.henke@she.net>
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. The terms
8# are also available at http://trac.edgewall.org/wiki/TracLicense.
9#
10
11from types import ListType, StringTypes
12
13from trac import __version__ as trac_version
14from trac.admin import IAdminPanelProvider
15from trac.core import Component, ExtensionPoint, TracError, implements
16from trac.env import IEnvironmentSetupParticipant
17from trac.perm import (
18    PermissionSystem, IPermissionGroupProvider, IPermissionRequestor
19)
20from trac.ticket.report import ReportModule
21from trac.web.chrome import ITemplateProvider
22from trac.web.api import ITemplateStreamFilter, IRequestFilter
23
24from genshi.filters import Transformer
25from genshi.filters.transform import StreamBuffer
26from genshi.input import HTML
27
28
29class PrivateReports(Component):
30    group_providers = ExtensionPoint(IPermissionGroupProvider)
31
32    implements(ITemplateStreamFilter, IEnvironmentSetupParticipant,
33               IAdminPanelProvider, ITemplateProvider, IRequestFilter,
34               IPermissionRequestor)
35
36    # IRequestFilter methods
37
38    def pre_process_request(self, req, handler):
39        if not isinstance(handler, ReportModule):
40            return handler
41        report_id = req.args.get('id')
42        if not report_id or self._has_permission(req.authname, report_id):
43            return handler
44        else:
45            self.log.debug("User %s doesn't have permission to view report %s "
46                           % (req.authname, report_id))
47            raise TracError("You don't have permission to access this report",
48                            "No Permission")
49
50    def post_process_request(self, req, template, data, content_type):
51        return template, data, content_type
52
53    # ITemplateProvider methods
54
55    def get_htdocs_dirs(self):
56        return []
57
58    def get_templates_dirs(self):
59        from pkg_resources import resource_filename
60        return [resource_filename(__name__, 'templates')]
61
62    # IAdminPanelProvider methods
63
64    def get_admin_panels(self, req):
65        if 'TRAC_ADMIN' in req.perm:
66            yield ('reports', 'Reports',
67                   'privatereports', 'Private Reports')
68
69    def render_admin_panel(self, req, cat, page, path_info):
70        if page == 'privatereports':
71            reports = self._get_reports()
72            data = {
73                'reports': reports
74            }
75            if req.method == 'POST':
76                report_id = req.args.get('report_id')
77                try:
78                    report_id = int(report_id)
79                except ValueError:
80                    req.redirect(self.env.href.admin.reports('privatereports'))
81                if req.args.get('add'):
82                    new_permission = req.args.get('newpermission')
83                    if new_permission is None or \
84                            new_permission.isupper() is False:
85                        req.redirect(
86                            self.env.href.admin.reports('privatereports'))
87                    self._insert_report_permission(report_id, new_permission)
88                    data['report_permissions'] = \
89                        self._get_report_permissions(report_id) or ''
90                    data['show_report'] = report_id
91                elif req.args.get('remove'):
92                    arg_report_permissions = req.args.get('report_permissions')
93                    if arg_report_permissions is None:
94                        req.redirect(
95                            self.env.href.admin.reports('privatereports'))
96                    report_permissions = \
97                        self._get_report_permissions(report_id)
98                    report_permissions = set(report_permissions)
99                    to_remove = set()
100                    if type(arg_report_permissions) in StringTypes:
101                        to_remove.update([arg_report_permissions])
102                    elif type(arg_report_permissions) == ListType:
103                        to_remove.update(arg_report_permissions)
104                    else:
105                        req.redirect(
106                            self.env.href.admin.reports('privatereports'))
107                    report_permissions = report_permissions - to_remove
108                    self._alter_report_permissions(report_id,
109                                                   report_permissions)
110                    data['report_permissions'] = report_permissions or ''
111                    data['show_report'] = report_id
112                elif req.args.get('show'):
113                    report_permissions = \
114                        self._get_report_permissions(report_id)
115                    data['report_permissions'] = report_permissions or ''
116                    data['show_report'] = report_id
117            else:
118                report_permissions = \
119                    self._get_report_permissions(reports[0][1])
120                data['report_permissions'] = report_permissions or ''
121            return 'admin_privatereports.html', data
122
123    # IEnvironmentSetupParticipant methods
124
125    def environment_created(self):
126        db = self.env.get_db_cnx()
127        if self.environment_needs_upgrade(db):
128            self.upgrade_environment(db)
129
130    def environment_needs_upgrade(self, db):
131        cursor = db.cursor()
132        try:
133            cursor.execute("SELECT report_id, permission FROM private_report")
134            return False
135        except:
136            return True
137
138    def upgrade_environment(self, db):
139        cursor = db.cursor()
140        try:
141            cursor.execute("DROP TABLE IF EXISTS private_report")
142            db.commit()
143        except:
144            cursor.connection.rollback()
145        try:
146            cursor = db.cursor()
147            cursor.execute("""
148                CREATE TABLE private_report(report_id integer,
149                  permission text)""")
150            db.commit()
151        except:
152            cursor.connection.rollback()
153
154    # ITemplateStreamFilter methods
155
156    def filter_stream(self, req, method, filename, stream, data):
157        if filename != 'report_list.html':
158            return stream
159        stream_buffer = StreamBuffer()
160
161        from pkg_resources import parse_version
162        if parse_version(trac_version) < parse_version('1.0'):
163            delimiter = '<tr>'
164            selector = '//tbody/tr'
165        else:
166            delimiter = '<div class="collapsed">'
167            selector = '//div[@class="reports"]/div'
168
169        def check_report_permission():
170            report_stream = unicode(stream_buffer)
171            reports_raw = report_stream.split(delimiter)
172            report_stream = ''
173            for row in reports_raw:
174                if row and 'View report' in row:
175                    # determine the report id
176                    s = row.find('/report/')
177                    if s == -1:
178                        continue
179                    e = row.find('\"', s)
180                    if e == -1:
181                        continue
182                    report_id = row[s+len('/report/'):e]
183                    if self._has_permission(req.authname, report_id):
184                        report_stream += row
185                    else:
186                        self.log.debug("Removing report %s from list because "
187                                       "%s doesn't have permission to view" %
188                                       (report_id, req.authname))
189                else:
190                    report_stream += row
191            return HTML(report_stream)
192        return stream | Transformer(selector) \
193            .copy(stream_buffer) \
194            .replace(check_report_permission)
195
196    # IPermissionRequestor methods
197
198    def get_permission_actions(self):
199        db = self.env.get_db_cnx()
200        cursor = db.cursor()
201        cursor.execute("""
202            SELECT permission FROM private_report GROUP BY permission""")
203        report_perms = []
204        try:
205            for permission in cursor.fetchall():
206                report_perms.append(permission[0])
207        except:
208            pass
209        return tuple(report_perms)
210
211    # Internal methods
212
213    def _has_permission(self, user, report_id):
214        report_permissions = self._get_report_permissions(report_id)
215        if not report_permissions:
216            return True
217        perms = PermissionSystem(self.env)
218        report_permissions = set(report_permissions)
219        user_perm = set(perms.get_user_permissions(user))
220        groups = set(self._get_user_groups(user))
221        user_perm.update(groups)
222        if report_permissions.intersection(user_perm) != set([]):
223            return True
224        return False
225
226    def _get_user_groups(self, user):
227        subjects = set([user])
228        for provider in self.group_providers:
229            subjects.update(provider.get_permission_groups(user) or [])
230        groups = []
231        db = self.env.get_db_cnx()
232        cursor = db.cursor()
233        cursor.execute("""
234            SELECT action FROM permission WHERE username = %s""", (user,))
235        rows = cursor.fetchall()
236        for action in rows:
237            if action[0].isupper():
238                groups.append(action[0])
239        return groups
240
241    def _get_reports(self):
242        db = self.env.get_db_cnx()
243        cursor = db.cursor()
244        cursor.execute("SELECT title, id FROM report")
245        reports = cursor.fetchall()
246        return reports
247
248    def _insert_report_permission(self, report_id, permission):
249        db = self.env.get_db_cnx()
250        cursor = db.cursor()
251        cursor.execute("""
252            INSERT INTO private_report(report_id, permission)
253            VALUES(%s, %s)""", (int(report_id), str(permission)))
254        db.commit()
255
256    def _alter_report_permissions(self, report_id, permissions):
257        db = self.env.get_db_cnx()
258        cursor = db.cursor()
259        cursor.execute("""
260            DELETE FROM private_report WHERE report_id=%s
261            """, (int(report_id),))
262        db.commit()
263        for permission in permissions:
264            self._insert_report_permission(report_id, permission)
265
266    def _get_report_permissions(self, report_id):
267        report_perms = []
268        db = self.env.get_db_cnx()
269        cursor = db.cursor()
270        cursor.execute("""
271            SELECT permission FROM private_report
272            WHERE report_id=%s GROUP BY permission""", (int(report_id),))
273        for perm in cursor.fetchall():
274            report_perms.append(perm[0])
275        return report_perms
Note: See TracBrowser for help on using the repository browser.