| 1 | from trac.web.api import ITemplateStreamFilter |
|---|
| 2 | from trac.core import * |
|---|
| 3 | from genshi.core import * |
|---|
| 4 | from genshi.builder import tag |
|---|
| 5 | from trac.perm import PermissionError |
|---|
| 6 | try: |
|---|
| 7 | set |
|---|
| 8 | except NameError: |
|---|
| 9 | from sets import Set as set # Python 2.3 fallback |
|---|
| 10 | |
|---|
| 11 | from genshi.filters.transform import Transformer |
|---|
| 12 | import re |
|---|
| 13 | import dbhelper |
|---|
| 14 | |
|---|
| 15 | from trac.ticket.report import ReportModule |
|---|
| 16 | from trac.util.datefmt import format_datetime, format_time |
|---|
| 17 | import csv |
|---|
| 18 | from trac.web.api import RequestDone |
|---|
| 19 | from trac.web.chrome import add_script |
|---|
| 20 | from trac.web.api import IRequestFilter |
|---|
| 21 | |
|---|
| 22 | class ReportsFilter(Component): |
|---|
| 23 | """This component Removed rows from the report that require the |
|---|
| 24 | management screen to supply values""" |
|---|
| 25 | implements(IRequestFilter) |
|---|
| 26 | def pre_process_request(self, req, handler): |
|---|
| 27 | return handler |
|---|
| 28 | |
|---|
| 29 | def post_process_request(self, req, template, data, content_type): |
|---|
| 30 | if template == 'report_list.html': |
|---|
| 31 | add_script(req, "billing/report_filter.js") |
|---|
| 32 | return (template, data, content_type) |
|---|
| 33 | |
|---|
| 34 | # This can go away once they fix http://genshi.edgewall.org/ticket/136 |
|---|
| 35 | # At that point we should use Transformer.filter |
|---|
| 36 | # THIS IS STILL SOLVING PROBLEMS WELL AFTER THAT TICKET HAS BEEN CLOSED - A new ticket #290 [1000] has fixed the bug, but is |
|---|
| 37 | # not the trac default yet |
|---|
| 38 | # Without this (using the default filter) I was getting omitted closing tags for some tags (Based on whitespace afaict) |
|---|
| 39 | class FilterTransformation(object): |
|---|
| 40 | """Apply a normal stream filter to the selection. The filter is called once |
|---|
| 41 | for each contiguous block of marked events.""" |
|---|
| 42 | |
|---|
| 43 | def __init__(self, filter): |
|---|
| 44 | """Create the transform. |
|---|
| 45 | |
|---|
| 46 | :param filter: The stream filter to apply. |
|---|
| 47 | """ |
|---|
| 48 | self.filter = filter |
|---|
| 49 | |
|---|
| 50 | def __call__(self, stream): |
|---|
| 51 | """Apply the transform filter to the marked stream. |
|---|
| 52 | |
|---|
| 53 | :param stream: The marked event stream to filter |
|---|
| 54 | """ |
|---|
| 55 | def flush(queue): |
|---|
| 56 | if queue: |
|---|
| 57 | for event in self.filter(queue): |
|---|
| 58 | yield event |
|---|
| 59 | del queue[:] |
|---|
| 60 | |
|---|
| 61 | queue = [] |
|---|
| 62 | for mark, event in stream: |
|---|
| 63 | if mark: |
|---|
| 64 | queue.append(event) |
|---|
| 65 | else: |
|---|
| 66 | for e in flush(queue): |
|---|
| 67 | yield None,e |
|---|
| 68 | yield None,event |
|---|
| 69 | for event in flush(queue): |
|---|
| 70 | yield None,event |
|---|
| 71 | |
|---|
| 72 | billing_report_regex = re.compile("\{(?P<reportid>\d*)\}") |
|---|
| 73 | def report_id_from_text(text): |
|---|
| 74 | m = billing_report_regex.match(text) |
|---|
| 75 | if m: |
|---|
| 76 | return int(m.groupdict()["reportid"]) |
|---|
| 77 | |
|---|
| 78 | def get_billing_reports(comp): |
|---|
| 79 | billing_reports = set() |
|---|
| 80 | rows = dbhelper.get_all(comp.env, "SELECT id FROM custom_report")[1] |
|---|
| 81 | if rows: |
|---|
| 82 | billing_reports = set([x[0] for x in rows]) |
|---|
| 83 | return billing_reports |
|---|
| 84 | |
|---|
| 85 | class ReportScreenFilter(Component): |
|---|
| 86 | """Hides TandE reports even when you just go to the url""" |
|---|
| 87 | implements(ITemplateStreamFilter) |
|---|
| 88 | def __init__(self): |
|---|
| 89 | self.billing_reports = get_billing_reports(self) |
|---|
| 90 | self.log.debug('ReportScreenFilter: self.billing_reports= %r' % self.billing_reports) |
|---|
| 91 | |
|---|
| 92 | def filter_stream(self, req, method, filename, stream, data): |
|---|
| 93 | if not filename in ('report_view.html', 'report_list.html'): |
|---|
| 94 | return stream |
|---|
| 95 | reportid = [None] |
|---|
| 96 | |
|---|
| 97 | def idhelper(strm): |
|---|
| 98 | header = strm[0][1] |
|---|
| 99 | if not reportid[0]: |
|---|
| 100 | self.log.debug("ReportScreenFilter: helper: %s %s %s"%(strm,header,report_id_from_text(header))) |
|---|
| 101 | reportid[0] = report_id_from_text(header) |
|---|
| 102 | for kind, data, pos in strm: |
|---|
| 103 | yield kind, data, pos |
|---|
| 104 | |
|---|
| 105 | def permhelper(strm): |
|---|
| 106 | id = reportid[0] |
|---|
| 107 | self.log.debug("ReportScreenFilter: id:%s, in bill: %s has perm:%s" % (id, id in self.billing_reports, req.perm.has_permission("TIME_VIEW"))) |
|---|
| 108 | if id and id in self.billing_reports and not req.perm.has_permission("TIME_VIEW"): |
|---|
| 109 | self.log.debug("ReportScreenFilter: No time view, prevent render") |
|---|
| 110 | msg = "YOU MUST HAVE TIME_VIEW PERMSSIONS TO VIEW THIS REPORT" |
|---|
| 111 | for kind, data, pos in tag.span(msg).generate(): |
|---|
| 112 | yield kind, data, pos |
|---|
| 113 | else: |
|---|
| 114 | for kind, data, pos in strm: |
|---|
| 115 | yield kind, data, pos |
|---|
| 116 | |
|---|
| 117 | self.log.debug("ReportScreenFilter: About to begin filtering of billing reports without permissions") |
|---|
| 118 | stream = stream | Transformer('//div[@id="content"]/h1/text()').apply(FilterTransformation(idhelper)) |
|---|
| 119 | stream = stream | Transformer('//div[@id="content"]').apply(FilterTransformation(permhelper)) |
|---|
| 120 | return stream |
|---|
| 121 | |
|---|
| 122 | ## ENFORCE PERMISSIONS ON report exports |
|---|
| 123 | |
|---|
| 124 | billing_report_fname_regex = re.compile("report_(?P<reportid>\d*)") |
|---|
| 125 | def report_id_from_filename(text): |
|---|
| 126 | if text: |
|---|
| 127 | m = billing_report_fname_regex.match(text) |
|---|
| 128 | if m: |
|---|
| 129 | return int(m.groupdict()["reportid"]) |
|---|
| 130 | return -1; |
|---|
| 131 | |
|---|
| 132 | unwrapped_send_csv = ReportModule._send_csv |
|---|
| 133 | def _send_csv(self, req, cols, rows, sep=',', mimetype='text/plain', |
|---|
| 134 | filename=None): |
|---|
| 135 | self.env.log.debug("T&E: In Wrapped _send_csv") |
|---|
| 136 | id = report_id_from_filename(filename) |
|---|
| 137 | reports = get_billing_reports(self) |
|---|
| 138 | if id in reports and not req.perm.has_permission("TIME_VIEW"): |
|---|
| 139 | raise PermissionError("You must have TIME_VIEW permission in order to view this report") |
|---|
| 140 | unwrapped_send_csv(self, req, cols, rows, sep, mimetype, filename) |
|---|
| 141 | |
|---|
| 142 | ReportModule._send_csv = _send_csv |
|---|