| [2538] | 1 | from trac.web.api import ITemplateStreamFilter |
|---|
| [4260] | 2 | from trac.perm import IPermissionRequestor |
|---|
| [2538] | 3 | from trac.core import * |
|---|
| 4 | from genshi.core import * |
|---|
| 5 | from genshi.builder import tag |
|---|
| [6269] | 6 | |
|---|
| [2538] | 7 | from genshi.filters.transform import Transformer |
|---|
| [4260] | 8 | from blackmagic import * |
|---|
| [4279] | 9 | from trac.ticket.query import QueryModule |
|---|
| [2538] | 10 | |
|---|
| [4279] | 11 | from StringIO import StringIO |
|---|
| 12 | import csv |
|---|
| 13 | from trac.mimeview.api import Context |
|---|
| 14 | from trac.resource import Resource |
|---|
| 15 | |
|---|
| [8130] | 16 | from trac.web.chrome import Chrome |
|---|
| 17 | |
|---|
| [6269] | 18 | import sys |
|---|
| 19 | if sys.version_info < (2, 4, 0): |
|---|
| 20 | from sets import Set as set |
|---|
| 21 | |
|---|
| [4279] | 22 | ## MONKEY PATCH THE QUERY MODULE CSV EXPORT FN TO ENFORCE PERMISSIONS |
|---|
| 23 | def new_csv_export(self, req, query, sep=',', mimetype='text/plain'): |
|---|
| 24 | self.log.debug("T&E plugin has overridden QueryModule.csv_export so to enforce field permissions") |
|---|
| 25 | |
|---|
| 26 | ## find the columns that should be hidden |
|---|
| 27 | hidden_fields = [] |
|---|
| 28 | fields = self.config.getlist(csection, 'fields', []) |
|---|
| 29 | self.log.debug('QueryModule.csv_export: found : %s' % fields) |
|---|
| 30 | for field in fields: |
|---|
| 31 | perms = self.config.getlist(csection, '%s.permission' % field, []) |
|---|
| 32 | #self.log.debug('QueryModule.csv_export: read permission config: %s has %s' % (field, perms)) |
|---|
| 33 | for (perm, denial) in [s.split(":") for s in perms] : |
|---|
| 34 | perm = perm.upper() |
|---|
| 35 | #self.log.debug('QueryModule.csv_export: testing permission: %s:%s should act= %s' % |
|---|
| 36 | # (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS"))) |
|---|
| 37 | if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove","hide"]: |
|---|
| 38 | hidden_fields.append(field) |
|---|
| 39 | ## END find the columns that should be hidden |
|---|
| 40 | |
|---|
| 41 | content = StringIO() |
|---|
| 42 | cols = query.get_columns() |
|---|
| 43 | writer = csv.writer(content, delimiter=sep) |
|---|
| 44 | writer = csv.writer(content, delimiter=sep, quoting=csv.QUOTE_MINIMAL) |
|---|
| 45 | writer.writerow([unicode(c).encode('utf-8') for c in cols if c not in hidden_fields]) |
|---|
| 46 | |
|---|
| 47 | context = Context.from_request(req) |
|---|
| 48 | results = query.execute(req, self.env.get_db_cnx()) |
|---|
| 49 | self.log.debug('QueryModule.csv_export: hidding columns %s' % hidden_fields) |
|---|
| 50 | for result in results: |
|---|
| 51 | ticket = Resource('ticket', result['id']) |
|---|
| 52 | if 'TICKET_VIEW' in req.perm(ticket): |
|---|
| 53 | values = [] |
|---|
| 54 | for col in cols: |
|---|
| 55 | if col not in hidden_fields: |
|---|
| 56 | value = result[col] |
|---|
| 57 | if col in ('cc', 'reporter'): |
|---|
| 58 | value = Chrome(self.env).format_emails(context(ticket), |
|---|
| 59 | value) |
|---|
| 60 | values.append(unicode(value).encode('utf-8')) |
|---|
| 61 | writer.writerow(values) |
|---|
| 62 | return (content.getvalue(), '%s;charset=utf-8' % mimetype) |
|---|
| 63 | |
|---|
| 64 | QueryModule.export_csv = new_csv_export |
|---|
| 65 | |
|---|
| [4264] | 66 | class TicketFormatFilter(Component): |
|---|
| 67 | """Filtering the streams to alter the base format of the ticket""" |
|---|
| 68 | implements(ITemplateStreamFilter) |
|---|
| [2538] | 69 | |
|---|
| 70 | def filter_stream(self, req, method, filename, stream, data): |
|---|
| [4264] | 71 | self.log.debug("TicketFormatFilter executing") |
|---|
| [4260] | 72 | if not filename == 'ticket.html': |
|---|
| [4264] | 73 | self.log.debug("TicketFormatFilter not the correct template") |
|---|
| [4260] | 74 | return stream |
|---|
| 75 | |
|---|
| [4264] | 76 | self.log.debug("TicketFormatFilter disabling totalhours and removing header hours") |
|---|
| [4260] | 77 | stream = disable_field(stream, "totalhours") |
|---|
| 78 | stream = remove_header(stream, "hours") |
|---|
| [4264] | 79 | return stream |
|---|
| [2538] | 80 | |
|---|
| [3960] | 81 | |
|---|
| [4264] | 82 | class QueryColumnPermissionFilter(Component): |
|---|
| 83 | """ Filtering the stream to remove """ |
|---|
| [4270] | 84 | implements(ITemplateStreamFilter) |
|---|
| [4264] | 85 | |
|---|
| 86 | ## ITemplateStreamFilter |
|---|
| 87 | |
|---|
| 88 | def filter_stream(self, req, method, filename, stream, data): |
|---|
| 89 | if not filename == "query.html": |
|---|
| 90 | self.log.debug('Not a query returning') |
|---|
| 91 | return stream |
|---|
| 92 | |
|---|
| 93 | def make_col_helper(field): |
|---|
| 94 | def column_helper (column_stream): |
|---|
| 95 | s = Stream(column_stream) |
|---|
| 96 | val = s.select('//input/@value').render() |
|---|
| 97 | if val.lower() != field.lower(): #if we are the field just skip it |
|---|
| 98 | #identity stream filter |
|---|
| 99 | for kind, data, pos in s: |
|---|
| 100 | yield kind, data, pos |
|---|
| 101 | return column_helper |
|---|
| 102 | |
|---|
| 103 | fields = self.config.getlist(csection, 'fields', []) |
|---|
| 104 | for field in fields: |
|---|
| 105 | self.log.debug('found : %s' % field) |
|---|
| 106 | perms = self.config.getlist(csection, '%s.permission' % field, []) |
|---|
| 107 | self.log.debug('read permission config: %s has %s' % (field, perms)) |
|---|
| 108 | for (perm, denial) in [s.split(":") for s in perms] : |
|---|
| 109 | perm = perm.upper() |
|---|
| 110 | self.log.debug('testing permission: %s:%s should act= %s' % |
|---|
| 111 | (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS"))) |
|---|
| [4279] | 112 | if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove","hide"]: |
|---|
| [4264] | 113 | # remove from the list of addable |
|---|
| 114 | stream = stream | Transformer( |
|---|
| 115 | '//select[@id="add_filter"]/option[@value="%s"]' % field |
|---|
| 116 | ).replace(" ") |
|---|
| 117 | |
|---|
| 118 | # remove from the list of columns |
|---|
| 119 | stream = stream | Transformer( |
|---|
| 120 | '//fieldset[@id="columns"]/div/label' |
|---|
| 121 | ).filter(make_col_helper(field)) |
|---|
| 122 | |
|---|
| 123 | #remove from the results table |
|---|
| 124 | stream = stream | Transformer( |
|---|
| 125 | '//th[@class="%s"]' % field |
|---|
| 126 | ).replace(" ") |
|---|
| 127 | stream = stream | Transformer( |
|---|
| 128 | '//td[@class="%s"]' % field |
|---|
| 129 | ).replace(" ") |
|---|
| 130 | |
|---|
| 131 | # remove from the filters |
|---|
| 132 | stream = stream | Transformer( |
|---|
| 133 | '//tr[@class="%s"]' % field |
|---|
| 134 | ).replace(" ") |
|---|
| 135 | |
|---|
| 136 | |
|---|
| 137 | return stream |
|---|