source: timingandestimationplugin/branches/trac1.0-Permissions/timingandestimationplugin/tande_filters.py @ 14424

Last change on this file since 14424 was 14424, checked in by Russ Tyndall, 9 years ago

fix bad variable reference in csv export filters re #12191

File size: 8.0 KB
Line 
1from trac.web.api import ITemplateStreamFilter
2from trac.perm import IPermissionRequestor
3from trac.core import *
4from genshi.core import *
5import genshi
6from genshi.builder import tag
7
8from genshi.filters.transform import Transformer
9from blackmagic import *
10from trac.ticket.query import QueryModule
11
12from StringIO import StringIO
13import csv
14from trac.mimeview.api import Context
15from trac.resource import Resource
16from trac.web.chrome import  Chrome
17
18import sys, re
19if sys.version_info < (2, 4, 0): 
20    from sets import Set as set
21
22# also in blackmagic ... not sure how to guarantee that one of them
23# will be run in time other than to do it in both places
24def textOf(self, **keys):
25    return self.render('text', None, **keys)
26Stream.textOf = textOf
27
28## MONKEY PATCH THE QUERY MODULE CSV EXPORT FN TO ENFORCE PERMISSIONS
29def new_csv_export(self, req, query, sep=',', mimetype='text/plain'):
30    self.log.debug("T&E plugin has overridden QueryModule.csv_export so to enforce field permissions")
31
32    ## find the columns that should be hidden
33    hidden_fields = []
34    fields = self.config.getlist(csection, 'fields', [])
35    self.log.debug('QueryModule.csv_export: found : %s' % fields)
36    for field in fields:
37        perms = self.config.getlist(csection, '%s.permission' % field, [])
38        #self.log.debug('QueryModule.csv_export: read permission config: %s has %s' % (field, perms))
39        for (perm, denial) in [s.split(":") for s in perms] :
40            perm = perm.upper()
41            #self.log.debug('QueryModule.csv_export: testing permission: %s:%s should act= %s' %
42            #               (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS")))
43            if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove","hide"]:
44                hidden_fields.append(field)
45
46    ## END find the columns that should be hidden
47
48    ### !!!    BEGIN COPIED CONTENT - from trac1.0/trac/ticket/query.py
49    content = StringIO()
50    content.write('\xef\xbb\xbf')   # BOM
51    cols = query.get_columns()
52    ### !!!    T&E patch
53    cols = [col for col in cols if col not in hidden_fields]
54    ### !!!END T&E patch
55    writer = csv.writer(content, delimiter=sep, quoting=csv.QUOTE_MINIMAL)
56    writer.writerow([unicode(c).encode('utf-8') for c in cols])
57
58    context = web_context(req)
59    results = query.execute(req)
60    for result in results:
61        ticket = Resource('ticket', result['id'])
62        if 'TICKET_VIEW' in req.perm(ticket):
63            values = []
64            for col in cols:
65                value = result[col]
66                if col in ('cc', 'owner', 'reporter'):
67                    value = Chrome(self.env).format_emails(
68                                context.child(ticket), value)
69                elif col in query.time_fields:
70                    value = format_datetime(value, '%Y-%m-%d %H:%M:%S',
71                                            tzinfo=req.tz)
72                values.append(unicode(value).encode('utf-8'))
73            writer.writerow(values)
74    return (content.getvalue(), '%s;charset=utf-8' % mimetype)
75
76
77QueryModule.export_csv = new_csv_export
78
79class TicketFormatFilter(Component):
80    """Filtering the streams to alter the base format of the ticket"""
81    implements(ITemplateStreamFilter)
82
83    def filter_stream(self, req, method, filename, stream, data):
84        self.log.debug("TicketFormatFilter executing") 
85        if not filename == 'ticket.html':
86            self.log.debug("TicketFormatFilter not the correct template")
87            return stream
88       
89        self.log.debug("TicketFormatFilter disabling totalhours and removing header hours")
90        stream = disable_field(stream, "totalhours")
91        stream = remove_header(stream, "hours")
92        return stream
93
94def denied_fields(comp, req):
95    fields = comp.config.getlist(csection, 'fields', [])
96    for field in fields:
97        comp.log.debug('found : %s' % field)
98        perms = comp.config.getlist(csection, '%s.permission' % field, [])
99        comp.log.debug('read permission config: %s has %s' % (field, perms))
100        for (perm, denial) in [s.split(":") for s in perms] :
101            perm = perm.upper()
102            comp.log.debug('testing permission: %s:%s should act= %s' %
103                           (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS")))
104            if (not req.perm.has_permission(perm) or perm == "ALWAYS") \
105                    and denial.lower() in ["remove","hide"]:
106                label = comp.env.config.get('ticket-custom',field+'.label', field).lower().strip()
107                yield (field, label)
108
109class QueryColumnPermissionFilter(Component):
110    """ Filtering the stream to remove """
111    implements(ITemplateStreamFilter)   
112   
113    ## ITemplateStreamFilter
114   
115    def filter_stream(self, req, method, filename, stream, data):
116        if not filename == "query.html":
117            self.log.debug('Not a query returning')
118            return stream
119
120        def make_col_helper(field):
121            def column_helper (column_stream):
122                s =  Stream(column_stream)
123                val = s.select('//input/@value').render()
124                if val.lower() != field.lower(): #if we are the field just skip it
125                    #identity stream filter
126                    for kind, data, pos in s:
127                        yield kind, data, pos       
128            return column_helper
129
130        for (field, label) in denied_fields(self, req):
131            # remove from the list of addable
132            stream = stream | Transformer(
133                '//select[@id="add_filter"]/option[@value="%s"]' % field
134                ).replace(" ")
135
136            # remove from the list of columns
137            stream = stream | Transformer(
138                '//fieldset[@id="columns"]/div/label'
139                ).filter(make_col_helper(field))
140                   
141            # remove from the results table
142            stream = stream | Transformer(
143                '//th[@class="%s"]' % field
144                ).replace(" ")
145            stream = stream | Transformer(
146                '//td[@class="%s"]' % field
147                ).replace(" ")
148           
149            # remove from the filters
150            stream = stream | Transformer(
151                '//tr[@class="%s"]' % field
152                ).replace(" ")
153        return stream
154
155commasRE = re.compile(r',\s(,\s)+', re.I)
156class TimelinePermissionFilter(Component):
157    """ Filtering the stream to remove fields from the timeline of changes """
158    implements(ITemplateStreamFilter)
159   
160    ## ITemplateStreamFilter
161   
162    def filter_stream(self, req, method, filename, stream, data):
163        if not filename == "timeline.html":
164            self.log.debug('Not a timeline, returning')
165            return stream
166        denied = [label for (field, label) in denied_fields(self, req)]
167        def helper(field_stream):
168            try:
169                s = Stream(field_stream)
170                # without None as the second value we get str instead of unicode
171                # and that causes things to break sometimes
172                f = s.select('//text()').textOf(strip_markup=True).lower()
173                self.log.debug('Timeline Filter: is %r in %r, skip?%r',
174                               f, denied, f in denied )
175                if f not in denied: #if we are the field just skip it
176                #identity stream filter
177                    for kind, data, pos in s:
178                        yield kind, data, pos
179            except Exception, e:
180                self.log.exception('Timeline: Stream Filter Exception');
181                raise e
182
183        def comma_cleanup(stream):
184            text = Stream(stream).textOf()
185            self.log.debug( 'Timeline: Commas %r %r' , text, commasRE.sub( text, ', ' ) );
186            text = commasRE.sub( ', ' , text)
187            for kind, data, pos in tag(text):
188                yield kind, data, pos
189
190        stream = stream | Transformer('//dd[@class="editedticket"]/i').filter(helper)
191        stream = stream | Transformer('//dd[@class="editedticket"]/text()').filter(comma_cleanup)
192                   
193        return stream
Note: See TracBrowser for help on using the repository browser.