=== htdocs/css/ticket.css
==================================================================
|
|
|
|
| 21 | 21 | #ticket th { color: #996; font-weight: normal; text-align: left } |
| 22 | 22 | #ticket hr { color: #dd9; border-color: #dd9; border-width: 1px 0 0; height: 1px; margin: .5em 0 0 } |
| 23 | 23 | |
| | 24 | #ticket .category { font-size: 200% } |
| | 25 | |
| 24 | 26 | #attachments { border: 1px outset #996; padding: 1em } |
| 25 | 27 | #attachments .attachments { list-style: square; margin-left: 2em; padding: 0 } |
| 26 | 28 | |
=== scripts/trac-admin
==================================================================
|
|
|
|
| 706 | 706 | self._do_wiki_import(filename, page, cursor) |
| 707 | 707 | |
| 708 | 708 | |
| | 709 | ## (Ticket) Category |
| | 710 | _help_category = [('category list', 'Show possible ticket categories'), |
| | 711 | ('category add <value>', 'Add a category value option'), |
| | 712 | ('category change <value> <newvalue>', |
| | 713 | 'Change a category value'), |
| | 714 | ('category remove <value>', 'Remove category value')] |
| | 715 | |
| | 716 | def complete_category (self, text, line, begidx, endidx): |
| | 717 | if begidx == 16: |
| | 718 | comp = self.get_enum_list ('category') |
| | 719 | elif begidx < 15: |
| | 720 | comp = ['list','add','change','remove'] |
| | 721 | return self.word_complete(text, comp) |
| | 722 | |
| | 723 | def do_category(self, line): |
| | 724 | self._do_enum('category', line) |
| | 725 | |
| 709 | 726 | ## (Ticket) Priority |
| 710 | 727 | _help_priority = [('priority list', 'Show possible ticket priorities'), |
| 711 | 728 | ('priority add <value>', 'Add a priority value option'), |
=== templates/newticket.cs
==================================================================
|
|
|
|
| 9 | 9 | |
| 10 | 10 | <div id="content" class="ticket"> |
| 11 | 11 | |
| 12 | | <h3>Create New Ticket:</h3> |
| 13 | 12 | <form id="newticket" action="<?cs var:cgi_location ?>#preview" method="post"> |
| | 13 | <h3>Create New Ticket of <label for="category">category:</label><?cs |
| | 14 | call:hdf_select(enums.category, "category", newticket.category) ?> |
| | 15 | </h3> |
| 14 | 16 | <div class="field"> |
| 15 | 17 | <label for="reporter">Your email or username:</label><br /> |
| 16 | 18 | <input type="text" id="reporter" name="reporter" size="40" value="<?cs |
=== templates/query.cs
==================================================================
|
|
|
|
| 2 | 2 | <?cs include:"header.cs" ?> |
| 3 | 3 | <?cs include:"macros.cs" ?> |
| 4 | 4 | |
| 5 | | <div id="ctxtnav" class="nav"><?cs if:query.edit_href ?> |
| 6 | | <ul> |
| 7 | | <li class="last"><a href="<?cs var:query.edit_href ?>">Refine Query</a></li> |
| | 5 | <div id="ctxtnav" class="nav"> |
| | 6 | <ul><?cs |
| | 7 | if:query.edit_href ?> |
| | 8 | <li class="last"><a href="<?cs var:query.edit_href ?>">Refine Query</a></li><?cs |
| | 9 | /if ?><?cs |
| | 10 | if:query.clear_href ?> |
| | 11 | <li class="last"><a href="<?cs var:query.clear_href ?>">Clear Query</a></li><?cs |
| | 12 | /if ?> |
| 8 | 13 | </ul> |
| 9 | | <?cs /if ?></div> |
| | 14 | </div> |
| 10 | 15 | |
| 11 | 16 | <div id="content" class="query"> |
| 12 | 17 | <h1><?cs var:title ?></h1> |
| … |
… |
|
| 20 | 25 | <?cs if:query.desc ?><input type="hidden" name="desc" value="1" /><?cs /if ?> |
| 21 | 26 | <legend>Ticket Properties</legend> |
| 22 | 27 | <div> |
| | 28 | <label for="category" accesskey="t">Category:</label> |
| | 29 | <?cs call:hdf_select_multiple(query.options.category, 'category', 4) ?> |
| | 30 | </div> |
| | 31 | <div> |
| 23 | 32 | <label for="component" accesskey="c">Component:</label> |
| 24 | 33 | <?cs call:hdf_select_multiple(query.options.component, 'component', 4) ?> |
| 25 | 34 | </div> |
| … |
… |
|
| 112 | 121 | /if ?> matched this query.</p> |
| 113 | 122 | <table id="tktlist" class="listing"> |
| 114 | 123 | <thead><tr><?cs each:header = query.headers ?><?cs |
| 115 | | if:name(header) == 0 ?><th class="ticket<?cs |
| | 124 | if:header.name == 'id' ?><th class="ticket<?cs |
| 116 | 125 | if:header.order ?> <?cs var:header.order ?><?cs /if ?>"> |
| 117 | 126 | <a href="<?cs var:header.href ?>" title="Sort by ID (<?cs |
| 118 | 127 | if:header.order == 'asc' ?>descending<?cs |
| … |
… |
|
| 131 | 140 | if:name(result) % 2 ?>odd<?cs else ?>even<?cs /if ?> <?cs |
| 132 | 141 | var:result.priority ?>"> |
| 133 | 142 | <?cs each:header = query.headers ?><?cs |
| 134 | | if:name(header) == 0 ?> |
| 135 | | <td class="ticket"><a href="<?cs var:result.href ?>" title="View ticket"><?cs |
| | 143 | if:header.name == 'id' ?> |
| | 144 | <td class="ticket"><a href="<?cs var:result.href ?>" title="View ticket">#<?cs |
| 136 | 145 | var:result.id ?></a></td><?cs |
| 137 | 146 | else ?> |
| 138 | 147 | <td><?cs if:header.name == 'summary' ?> |
=== templates/ticket.cs
==================================================================
|
|
|
|
| 32 | 32 | |
| 33 | 33 | <div id="ticket"> |
| 34 | 34 | <div class="date"><?cs var:ticket.opened ?></div> |
| 35 | | <h1>Ticket #<?cs var:ticket.id ?> <?cs |
| | 35 | <h1><?cs var:ticket.category ?> Ticket #<?cs var:ticket.id ?> <?cs |
| 36 | 36 | if:ticket.status == 'closed' ?>(Closed: <?cs var:ticket.resolution ?>)<?cs |
| 37 | 37 | elif:ticket.status != 'new' ?>(<?cs var:ticket.status ?>)<?cs |
| 38 | 38 | /if ?></h1> |
| … |
… |
|
| 166 | 166 | var:ticket.summary ?>" /><?cs |
| 167 | 167 | if $trac.acl.TICKET_ADMIN ?> |
| 168 | 168 | <br /> |
| | 169 | <label for="category">Category:</label><?cs |
| | 170 | call:hdf_select(enums.category, "category", ticket.category) ?> |
| | 171 | <br /> |
| 169 | 172 | <label for="description">Description:</label> |
| 170 | 173 | <div style="float: left"> |
| 171 | 174 | <textarea id="description" name="description" rows="10" cols="68"><?cs |
=== trac/Query.py
==================================================================
|
|
|
|
| 59 | 59 | results.append({ |
| 60 | 60 | 'id': id, |
| 61 | 61 | 'href': self.env.href.ticket(id), |
| | 62 | 'category': row['category'], |
| 62 | 63 | 'summary': util.escape(row['summary'] or '(no summary)'), |
| 63 | 64 | 'status': row['status'] or '', |
| 64 | 65 | 'component': row['component'] or '', |
| … |
… |
|
| 78 | 79 | if self.args.has_key('search'): |
| 79 | 80 | self.req.redirect(self.env.href.query(constraints, order, desc, |
| 80 | 81 | action='view')) |
| | 82 | if self.args.has_key('clear'): |
| | 83 | self.req.redirect(self.env.href.query()) |
| 81 | 84 | |
| 82 | 85 | action = self.args.get('action') |
| 83 | 86 | if not action and not constraints: |
| … |
… |
|
| 91 | 94 | |
| 92 | 95 | def _render_editor(self, constraints, order, desc): |
| 93 | 96 | self.req.hdf.setValue('title', 'Custom Query') |
| | 97 | self.req.hdf.setValue('query.clear_href', self.env.href.query(action='edit')) |
| 94 | 98 | util.add_to_hdf(constraints, self.req.hdf, 'query.constraints') |
| 95 | 99 | self.req.hdf.setValue('query.order', order) |
| 96 | 100 | if desc: self.req.hdf.setValue('query.desc', '1') |
| … |
… |
|
| 112 | 116 | del constraints[field] |
| 113 | 117 | |
| 114 | 118 | cursor = self.db.cursor() |
| | 119 | add_options('category', constraints, 'query.options.', cursor, |
| | 120 | "SELECT name FROM enum WHERE type='category' ORDER BY value") |
| 115 | 121 | add_options('status', constraints, 'query.options.', cursor, |
| 116 | 122 | "SELECT name FROM enum WHERE type='status' ORDER BY value") |
| 117 | 123 | add_options('resolution', constraints, 'query.options.', cursor, |
| … |
… |
|
| 146 | 152 | |
| 147 | 153 | # FIXME: the user should be able to configure which columns should |
| 148 | 154 | # be displayed |
| 149 | | headers = [ 'id', 'summary', 'status', 'component', 'owner' ] |
| | 155 | headers = [ 'category', 'id', 'summary', 'status', 'component', 'owner' ] |
| 150 | 156 | cols = headers |
| 151 | 157 | if not 'priority' in cols: |
| 152 | 158 | cols.append('priority') |
| … |
… |
|
| 176 | 182 | "(id=%s.ticket AND %s.name='%s')" |
| 177 | 183 | % (k, k, k, k)) |
| 178 | 184 | |
| 179 | | for col in [c for c in ['status', 'resolution', 'priority', 'severity'] |
| | 185 | for col in [c for c in ['category', 'status', 'resolution', 'priority', 'severity'] |
| 180 | 186 | if c in cols]: |
| 181 | 187 | sql.append(" INNER JOIN (SELECT name AS %s_name, value AS %s_value " \ |
| 182 | 188 | "FROM enum WHERE type='%s')" \ |
| … |
… |
|
| 194 | 200 | if clauses: |
| 195 | 201 | sql.append(" WHERE " + " AND ".join(clauses)) |
| 196 | 202 | |
| 197 | | if order in ['status', 'resolution', 'priority', 'severity']: |
| | 203 | if order in ['category', 'status', 'resolution', 'priority', 'severity']: |
| 198 | 204 | sql.append(" ORDER BY %s_value" % order) |
| 199 | 205 | else: |
| 200 | 206 | sql.append(" ORDER BY " + order) |
=== trac/Ticket.py
==================================================================
|
|
|
|
| 37 | 37 | class Ticket(UserDict): |
| 38 | 38 | std_fields = ['time', 'component', 'severity', 'priority', 'milestone', |
| 39 | 39 | 'reporter', 'owner', 'cc', 'url', 'version', 'status', 'resolution', |
| 40 | | 'keywords', 'summary', 'description'] |
| | 40 | 'keywords', 'summary', 'description', 'category'] |
| 41 | 41 | |
| 42 | 42 | def __init__(self, *args): |
| 43 | 43 | UserDict.__init__(self) |
| … |
… |
|
| 303 | 303 | self.env.get_config('ticket', 'default_component')) |
| 304 | 304 | ticket.setdefault('milestone', |
| 305 | 305 | self.env.get_config('ticket', 'default_milestone')) |
| | 306 | ticket.setdefault('category', |
| | 307 | self.env.get_config('ticket', 'default_category')) |
| 306 | 308 | ticket.setdefault('priority', |
| 307 | 309 | self.env.get_config('ticket', 'default_priority')) |
| 308 | 310 | ticket.setdefault('severity', |
| … |
… |
|
| 393 | 395 | util.hdf_add_if_missing(self.req.hdf, 'ticket.components', ticket['component']) |
| 394 | 396 | util.hdf_add_if_missing(self.req.hdf, 'ticket.milestones', ticket['milestone']) |
| 395 | 397 | util.hdf_add_if_missing(self.req.hdf, 'ticket.versions', ticket['version']) |
| | 398 | util.hdf_add_if_missing(self.req.hdf, 'enums.category', ticket['category']) |
| 396 | 399 | util.hdf_add_if_missing(self.req.hdf, 'enums.priority', ticket['priority']) |
| 397 | 400 | util.hdf_add_if_missing(self.req.hdf, 'enums.severity', ticket['severity']) |
| 398 | 401 | util.hdf_add_if_missing(self.req.hdf, 'enums.resolution', 'fixed') |
=== trac/core.py
==================================================================
|
|
|
|
| 199 | 199 | pass |
| 200 | 200 | |
| 201 | 201 | def populate_hdf(hdf, env, db, req): |
| | 202 | sql_to_hdf(db, "SELECT name FROM enum WHERE type='category' " |
| | 203 | "ORDER BY value", hdf, 'enums.category') |
| 202 | 204 | sql_to_hdf(db, "SELECT name FROM enum WHERE type='priority' " |
| 203 | 205 | "ORDER BY value", hdf, 'enums.priority') |
| 204 | 206 | sql_to_hdf(db, "SELECT name FROM enum WHERE type='severity' " |
=== trac/db_default.py
==================================================================
|
|
|
|
| 21 | 21 | |
| 22 | 22 | |
| 23 | 23 | # Database version identifier. Used for automatic upgrades. |
| 24 | | db_version = 7 |
| | 24 | db_version = 8 |
| 25 | 25 | |
| 26 | 26 | def __mkreports(reps): |
| 27 | 27 | """Utility function used to create report data in same syntax as the |
=== trac/Timeline.py
==================================================================
|
|
|
|
| 56 | 56 | q = [] |
| 57 | 57 | if changeset: |
| 58 | 58 | q.append("SELECT time, rev AS idata, '' AS tdata, 1 AS type, " |
| 59 | | " message, author " |
| | 59 | " message, author, '' AS category " |
| 60 | 60 | "FROM revision WHERE time>=%s AND time<=%s" % |
| 61 | 61 | (start, stop)) |
| 62 | 62 | if tickets: |
| 63 | 63 | q.append("SELECT time, id AS idata, '' AS tdata, 2 AS type, " |
| 64 | | "summary AS message, reporter AS author " |
| | 64 | " summary AS message, reporter AS author, category " |
| 65 | 65 | "FROM ticket WHERE time>=%s AND time<=%s" % |
| 66 | 66 | (start, stop)) |
| 67 | | q.append("SELECT time, ticket AS idata, '' AS tdata, 4 AS type, " |
| 68 | | "'' AS message, author " |
| 69 | | "FROM ticket_change WHERE field='status' " |
| 70 | | "AND newvalue='reopened' AND time>=%s AND time<=%s" % |
| | 67 | q.append("SELECT t1.time, t1.ticket AS idata, '' AS tdata, 4 AS type, " |
| | 68 | " '' AS message, t1.author, t0.category AS category " |
| | 69 | "FROM ticket_change t1 " |
| | 70 | " LEFT JOIN ticket t0 ON t0.id = t1.ticket " |
| | 71 | "WHERE t1.field='status' " |
| | 72 | "AND t1.newvalue='reopened' AND t1.time>=%s AND t1.time<=%s" % |
| 71 | 73 | (start, stop)) |
| 72 | 74 | q.append("SELECT t1.time AS time, t1.ticket AS idata," |
| 73 | 75 | " t2.newvalue AS tdata, 3 AS type," |
| 74 | | " t3.newvalue AS message, t1.author AS author" |
| | 76 | " t3.newvalue AS message, t1.author AS author, t0.category AS category" |
| 75 | 77 | " FROM ticket_change t1" |
| | 78 | " LEFT JOIN ticket t0 ON t0.id = t1.ticket" |
| 76 | 79 | " INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" |
| 77 | 80 | " AND t1.time = t2.time" |
| 78 | 81 | " LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" |
| … |
… |
|
| 82 | 85 | " AND t1.time >= %s AND t1.time <= %s" % (start,stop)) |
| 83 | 86 | if wiki: |
| 84 | 87 | q.append("SELECT time, -1 AS idata, name AS tdata, 5 AS type, " |
| 85 | | "comment AS message, author " |
| | 88 | "comment AS message, author, '' AS category " |
| 86 | 89 | "FROM wiki WHERE time>=%s AND time<=%s" % |
| 87 | 90 | (start, stop)) |
| 88 | 91 | if milestone: |
| 89 | 92 | q.append("SELECT time, -1 AS idata, '' AS tdata, 6 AS type, " |
| 90 | | "name AS message, '' AS author " |
| | 93 | "name AS message, '' AS author, '' AS category " |
| 91 | 94 | "FROM milestone WHERE time>=%s AND time<=%s" % |
| 92 | 95 | (start, stop)) |
| 93 | 96 | |
| … |
… |
|
| 113 | 116 | 'tdata': row['tdata'], |
| 114 | 117 | 'type': int(row['type']), |
| 115 | 118 | 'message': row['message'] or '', |
| | 119 | 'category': row['category'], |
| 116 | 120 | 'author': util.escape(row['author'] or 'anonymous') |
| 117 | 121 | } |
| 118 | 122 | |