diff --git a/trac/ticket/report.py b/trac/ticket/report.py
|
a
|
b
|
|
| 34 | 34 | from trac.util.text import to_unicode, unicode_urlencode |
| 35 | 35 | from trac.util.translation import _ |
| 36 | 36 | from trac.web.api import IRequestHandler, RequestDone |
| 37 | | from trac.web.chrome import add_ctxtnav, add_link, add_stylesheet, \ |
| | 37 | from trac.web.chrome import add_ctxtnav, add_link, add_script, \ |
| | 38 | add_stylesheet, add_warning, \ |
| 38 | 39 | INavigationContributor, Chrome |
| 39 | 40 | from trac.wiki import IWikiSyntaxProvider, WikiParser |
| 40 | 41 | |
| … |
… |
|
| 219 | 220 | |
| 220 | 221 | def _render_view(self, req, db, id): |
| 221 | 222 | """Retrieve the report results and pre-process them for rendering.""" |
| 222 | | try: |
| 223 | | args = self.get_var_args(req) |
| 224 | | except ValueError,e: |
| 225 | | raise TracError(_('Report failed: %(error)s', error=e)) |
| 226 | | |
| 227 | 223 | if id == -1: |
| 228 | 224 | # If no particular report was requested, display |
| 229 | 225 | # a list of available reports instead |
| … |
… |
|
| 241 | 237 | raise ResourceNotFound( |
| 242 | 238 | _('Report %(num)s does not exist.', num=id), |
| 243 | 239 | _('Invalid Report Number')) |
| | 240 | |
| | 241 | try: |
| | 242 | args = self.get_var_args(req) |
| | 243 | except ValueError,e: |
| | 244 | raise TracError(_('Report failed: %(error)s', error=e)) |
| 244 | 245 | |
| 245 | 246 | # If this is a saved custom query. redirect to the query module |
| 246 | 247 | # |
| … |
… |
|
| 294 | 295 | user = req.args.get('USER', None) |
| 295 | 296 | |
| 296 | 297 | try: |
| 297 | | cols, results, num_items = self.execute_paginated_report( |
| 298 | | req, db, id, sql, args, limit, offset) |
| | 298 | cols, results, num_items, missing_args = \ |
| | 299 | self.execute_paginated_report(req, db, id, sql, args, limit, |
| | 300 | offset) |
| 299 | 301 | results = [list(row) for row in results] |
| 300 | 302 | numrows = len(results) |
| 301 | 303 | |
| … |
… |
|
| 497 | 499 | del req.session[var] |
| 498 | 500 | except (ValueError, KeyError): |
| 499 | 501 | pass |
| | 502 | if len(data['args']) > 1: |
| | 503 | add_script(req, 'common/js/folding.js') |
| | 504 | if missing_args: |
| | 505 | add_warning(req, _( |
| | 506 | 'The following arguments are missing: %(args)s', |
| | 507 | args=", ".join(missing_args))) |
| 500 | 508 | return 'report_view.html', data, None |
| 501 | 509 | |
| 502 | 510 | def add_alternate_links(self, req, args): |
| … |
… |
|
| 527 | 535 | |
| 528 | 536 | def execute_paginated_report(self, req, db, id, sql, args, |
| 529 | 537 | limit=0, offset=0): |
| 530 | | sql, args = self.sql_sub_vars(sql, args, db) |
| | 538 | sql, args, missing_args = self.sql_sub_vars(sql, args, db) |
| 531 | 539 | if not sql: |
| 532 | 540 | raise TracError(_('Report %(num)s has no SQL query.', num=id)) |
| 533 | 541 | self.log.debug('Executing report with SQL "%s"' % sql) |
| … |
… |
|
| 579 | 587 | |
| 580 | 588 | db.rollback() |
| 581 | 589 | |
| 582 | | return cols, info, num_items |
| | 590 | return cols, info, num_items, missing_args |
| 583 | 591 | |
| 584 | 592 | def get_var_args(self, req): |
| 585 | 593 | report_args = {} |
| … |
… |
|
| 598 | 606 | if db is None: |
| 599 | 607 | db = self.env.get_db_cnx() |
| 600 | 608 | values = [] |
| | 609 | missing_args = [] |
| 601 | 610 | def add_value(aname): |
| 602 | 611 | try: |
| 603 | 612 | arg = args[aname] |
| 604 | 613 | except KeyError: |
| 605 | | raise TracError(_("Dynamic variable '%(name)s' not defined.", |
| 606 | | name='$%s' % aname)) |
| | 614 | arg = args[str(aname)] = '' |
| | 615 | missing_args.append(aname) |
| 607 | 616 | values.append(arg) |
| 608 | 617 | |
| 609 | 618 | var_re = re.compile("[$]([A-Z]+)") |
| … |
… |
|
| 634 | 643 | sql_io.write(repl_literal(expr)) |
| 635 | 644 | else: |
| 636 | 645 | sql_io.write(var_re.sub(repl, expr)) |
| 637 | | return sql_io.getvalue(), values |
| | 646 | return sql_io.getvalue(), values, missing_args |
| 638 | 647 | |
| 639 | 648 | def _send_csv(self, req, cols, rows, sep=',', mimetype='text/plain', |
| 640 | 649 | filename=None): |
diff --git a/trac/ticket/templates/report_view.html b/trac/ticket/templates/report_view.html
|
a
|
b
|
|
| 7 | 7 | <xi:include href="layout.html" /> |
| 8 | 8 | <head> |
| 9 | 9 | <title>$title</title> |
| | 10 | <script type="text/javascript" py:if="report.id != -1 and len(args) > 1"> |
| | 11 | jQuery(document).ready(function($) { |
| | 12 | $("fieldset legend").enableFolding(false); |
| | 13 | }); |
| | 14 | </script> |
| 10 | 15 | </head> |
| 11 | 16 | |
| 12 | 17 | <body> |
| 13 | 18 | <div id="content" class="report"> |
| 14 | | |
| 15 | 19 | <h1>$title |
| 16 | 20 | <span py:if="numrows and report.id != -1" class="numrows">($numrows matches)</span> |
| 17 | 21 | </h1> |
| … |
… |
|
| 19 | 23 | <div py:if="description" id="description" xml:space="preserve"> |
| 20 | 24 | ${wiki_to_html(context, description)} |
| 21 | 25 | </div> |
| | 26 | |
| | 27 | <form py:if="report.id != -1 and len(args) > 1" method="get" action=""> |
| | 28 | <fieldset id="filters" > |
| | 29 | <legend class="foldable">Arguments</legend> |
| | 30 | <table summary="Report arguments"> |
| | 31 | <tbody> |
| | 32 | <tr style="height: 1px"><td colspan="2"></td></tr> |
| | 33 | </tbody> |
| | 34 | <tbody py:for="arg in args" py:if="arg != 'USER'"> |
| | 35 | <tr> |
| | 36 | <th scope="row"><label>${arg}</label></th> |
| | 37 | <td class="filter"> |
| | 38 | <input type="text" name="${arg}" value="${req.args[arg] or None}" size="42" /> |
| | 39 | </td> |
| | 40 | </tr> |
| | 41 | </tbody> |
| | 42 | <tbody> |
| | 43 | <tr class="actions"> |
| | 44 | <td class="actions" colspan="2" style="text-align: right"> |
| | 45 | <input type="submit" value="Update" /> |
| | 46 | </td> |
| | 47 | </tr> |
| | 48 | </tbody> |
| | 49 | </table> |
| | 50 | </fieldset> |
| | 51 | </form> |
| 22 | 52 | |
| 23 | 53 | <div py:if="report.id != -1" class="buttons"> |
| 24 | 54 | <form py:if="'REPORT_MODIFY' in perm(report.resource)" action="" method="get"> |