diff -r c1db312f6768 htdocs/css/ticket.css
|
a
|
b
|
|
| 51 | 51 | font-size: 100%; |
| 52 | 52 | font-weight: normal; |
| 53 | 53 | } |
| | 54 | span.commentcmd { |
| | 55 | padding-top: 0.5em; |
| | 56 | float: right; |
| | 57 | font-size: 85%; |
| | 58 | } |
| 54 | 59 | #changelog .changes { list-style: square; margin-left: 2em; padding: 0 } |
| 55 | 60 | #changelog .comment { margin-left: 2em } |
| 56 | 61 | |
diff -r c1db312f6768 htdocs/css/trac.css
|
a
|
b
|
h1 :link, h1 :visited ,h2 :link, h2 :vis |
| 40 | 40 | h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited, |
| 41 | 41 | h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited { |
| 42 | 42 | color: inherit; |
| | 43 | } |
| | 44 | |
| | 45 | /* Heading anchors */ |
| | 46 | .anchor:link, .anchor:visited { |
| | 47 | border: none; |
| | 48 | color: #d7d7d7; |
| | 49 | font-size: .8em; |
| | 50 | vertical-align: text-top; |
| | 51 | visibility: hidden; |
| | 52 | } |
| | 53 | h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, |
| | 54 | h4:hover .anchor, h5:hover .anchor, h6:hover .anchor { |
| | 55 | visibility: visible; |
| 43 | 56 | } |
| 44 | 57 | |
| 45 | 58 | @media screen { |
diff -r c1db312f6768 htdocs/css/wiki.css
|
a
|
b
|
|
| 21 | 21 | #overview .multi { color: #999 } |
| 22 | 22 | #overview .ipnr { color: #999; font-size: 80% } |
| 23 | 23 | #overview .comment { padding: 1em 0 0 } |
| 24 | | |
| 25 | | /* Heading anchors */ |
| 26 | | .anchor:link, .anchor:visited { |
| 27 | | border: none; |
| 28 | | color: #d7d7d7; |
| 29 | | font-size: .8em; |
| 30 | | vertical-align: text-top; |
| 31 | | visibility: hidden; |
| 32 | | } |
| 33 | | h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, |
| 34 | | h4:hover .anchor, h5:hover .anchor, h6:hover .anchor { |
| 35 | | visibility: visible; |
| 36 | | } |
| 37 | 24 | |
| 38 | 25 | /* Styles for the page history table |
| 39 | 26 | (extends the styles for "table.listing") */ |
diff -r c1db312f6768 templates/ticket.cs
|
a
|
b
|
|
| 100 | 100 | <?cs if:len(ticket.changes) ?><h2>Change History</h2> |
| 101 | 101 | <div id="changelog"><?cs |
| 102 | 102 | each:change = ticket.changes ?> |
| 103 | | <h3 id="change_<?cs var:name(change) ?>" class="change"><?cs |
| 104 | | var:change.date ?>: Modified by <?cs var:change.author ?></h3><?cs |
| | 103 | <h3 <?cs if:change.cnum ?>id="comment:<?cs var:change.cnum ?>"<?cs /if ?> |
| | 104 | class="change"><?cs |
| | 105 | var:change.date ?>: Modified by <?cs var:change.author ?><?cs |
| | 106 | if:change.cnum ?><a href="#comment:<?cs var:change.cnum ?>" class="anchor" |
| | 107 | title="Permalink to comment:<?cs var:change.cnum ?>"> |
| | 108 | ¶</a> |
| | 109 | <span class="commentcmd"><a href="<?cs var:ticket.href ?>?replyto=<?cs var:change.cnum ?>#comment" |
| | 110 | title="Reply to this comment" ?>[Reply]</a></span><?cs |
| | 111 | /if ?></h3><?cs |
| 105 | 112 | if:len(change.fields) ?> |
| 106 | 113 | <ul class="changes"><?cs |
| 107 | 114 | each:field = change.fields ?> |
diff -r c1db312f6768 trac/ticket/api.py
|
a
|
b
|
class TicketSystem(Component): |
| 186 | 186 | |
| 187 | 187 | def get_link_resolvers(self): |
| 188 | 188 | return [('bug', self._format_link), |
| 189 | | ('ticket', self._format_link)] |
| | 189 | ('ticket', self._format_link), |
| | 190 | ('comment', self._format_comment_link)] |
| 190 | 191 | |
| 191 | 192 | def get_wiki_syntax(self): |
| 192 | 193 | yield ( |
| … |
… |
class TicketSystem(Component): |
| 215 | 216 | return html.A(class_='missing ticket', rel='nofollow', |
| 216 | 217 | href=formatter.href.ticket(target))[label] |
| 217 | 218 | |
| | 219 | def _format_comment_link(self, formatter, ns, target, label): |
| | 220 | type, id, cnum = 'ticket', '1', 0 |
| | 221 | href = None |
| | 222 | if ':' in target: |
| | 223 | elts = target.split(':') |
| | 224 | if len(elts) == 3: |
| | 225 | type, id, cnum = elts |
| | 226 | href = formatter.href(type, id) |
| | 227 | else: |
| | 228 | # FIXME: the formatter should know which object the text being |
| | 229 | # formatted belongs to |
| | 230 | if formatter.req: |
| | 231 | path_info = formatter.req.path_info.strip('/').split('/', 2) |
| | 232 | if len(path_info) == 2: |
| | 233 | type, id = path_info[:2] |
| | 234 | href = formatter.href(type, id) |
| | 235 | cnum = target |
| | 236 | if href: |
| | 237 | return html.A(label, href="%s#comment:%s" % (href, cnum), |
| | 238 | title="Comment %s for %s:%s" % (cnum, type, id)) |
| | 239 | else: |
| | 240 | return label |
| | 241 | |
| 218 | 242 | # ISearchSource methods |
| 219 | 243 | |
| 220 | 244 | def get_search_filters(self, req): |
diff -r c1db312f6768 trac/ticket/model.py
|
a
|
b
|
class Ticket(object): |
| 265 | 265 | db = self._get_db(db) |
| 266 | 266 | cursor = db.cursor() |
| 267 | 267 | if when: |
| 268 | | cursor.execute("SELECT time,author,field,oldvalue,newvalue " |
| | 268 | cursor.execute("SELECT time,author,field,oldvalue,newvalue,1 " |
| 269 | 269 | "FROM ticket_change WHERE ticket=%s AND time=%s " |
| 270 | 270 | "UNION " |
| 271 | | "SELECT time,author,'attachment',null,filename " |
| | 271 | "SELECT time,author,'attachment',null,filename,0 " |
| 272 | 272 | "FROM attachment WHERE id=%s AND time=%s " |
| 273 | 273 | "UNION " |
| 274 | | "SELECT time,author,'comment',null,description " |
| | 274 | "SELECT time,author,'comment',null,description,0 " |
| 275 | 275 | "FROM attachment WHERE id=%s AND time=%s " |
| 276 | 276 | "ORDER BY time", |
| 277 | 277 | (self.id, when, str(self.id), when, self.id, when)) |
| 278 | 278 | else: |
| 279 | | cursor.execute("SELECT time,author,field,oldvalue,newvalue " |
| | 279 | cursor.execute("SELECT time,author,field,oldvalue,newvalue,1 " |
| 280 | 280 | "FROM ticket_change WHERE ticket=%s " |
| 281 | 281 | "UNION " |
| 282 | | "SELECT time,author,'attachment',null,filename " |
| | 282 | "SELECT time,author,'attachment',null,filename,0 " |
| 283 | 283 | "FROM attachment WHERE id=%s " |
| 284 | 284 | "UNION " |
| 285 | | "SELECT time,author,'comment',null,description " |
| | 285 | "SELECT time,author,'comment',null,description,0 " |
| 286 | 286 | "FROM attachment WHERE id=%s " |
| 287 | 287 | "ORDER BY time", (self.id, str(self.id), self.id)) |
| 288 | 288 | log = [] |
| 289 | | for t, author, field, oldvalue, newvalue in cursor: |
| 290 | | log.append((int(t), author, field, oldvalue or '', newvalue or '')) |
| | 289 | for t, author, field, oldvalue, newvalue, permanent in cursor: |
| | 290 | log.append((int(t), author, field, oldvalue or '', newvalue or '', |
| | 291 | permanent)) |
| 291 | 292 | return log |
| 292 | 293 | |
| 293 | 294 | def delete(self, db=None): |
diff -r c1db312f6768 trac/ticket/notification.py
|
a
|
b
|
class TicketNotifyEmail(NotifyEmail): |
| 75 | 75 | changes = '' |
| 76 | 76 | if not self.newticket and modtime: # Ticket change |
| 77 | 77 | changelog = ticket.get_changelog(modtime) |
| 78 | | for date, author, field, old, new in changelog: |
| | 78 | for date, author, field, old, new, permanent in changelog: |
| 79 | 79 | self.hdf.set_unescaped('ticket.change.author', author) |
| 80 | 80 | pfx = 'ticket.change.%s' % field |
| 81 | 81 | newv = '' |
diff -r c1db312f6768 trac/ticket/web_ui.py
|
a
|
b
|
class TicketModule(TicketModuleBase): |
| 226 | 226 | |
| 227 | 227 | ticket = Ticket(self.env, id, db=db) |
| 228 | 228 | reporter_id = get_reporter_id(req) |
| | 229 | replyto = req.args.get('replyto') |
| 229 | 230 | |
| 230 | 231 | if req.method == 'POST': |
| 231 | 232 | if not req.args.has_key('preview'): |
| … |
… |
class TicketModule(TicketModuleBase): |
| 253 | 254 | # Store a timestamp in order to detect "mid air collisions" |
| 254 | 255 | req.hdf['ticket.ts'] = ticket.time_changed |
| 255 | 256 | |
| 256 | | self._insert_ticket_data(req, db, ticket, reporter_id) |
| | 257 | self._insert_ticket_data(req, db, ticket, reporter_id, replyto) |
| 257 | 258 | |
| 258 | 259 | # If the ticket is being shown in the context of a query, add |
| 259 | 260 | # links to help navigate in the query result set |
| … |
… |
class TicketModule(TicketModuleBase): |
| 426 | 427 | |
| 427 | 428 | req.redirect(req.href.ticket(ticket.id)) |
| 428 | 429 | |
| 429 | | def _insert_ticket_data(self, req, db, ticket, reporter_id): |
| | 430 | def _insert_ticket_data(self, req, db, ticket, reporter_id, replyto): |
| 430 | 431 | """Insert ticket data into the hdf""" |
| 431 | 432 | req.hdf['ticket'] = ticket.values |
| 432 | 433 | req.hdf['ticket.id'] = ticket.id |
| … |
… |
class TicketModule(TicketModuleBase): |
| 462 | 463 | changelog = ticket.get_changelog(db=db) |
| 463 | 464 | curr_author = None |
| 464 | 465 | curr_date = 0 |
| | 466 | cnum = 0 |
| 465 | 467 | changes = [] |
| 466 | | for date, author, field, old, new in changelog: |
| | 468 | for date, author, field, old, new, permanent in changelog: |
| 467 | 469 | if date != curr_date or author != curr_author: |
| 468 | 470 | changes.append({ |
| 469 | 471 | 'date': format_datetime(date), |
| … |
… |
class TicketModule(TicketModuleBase): |
| 472 | 474 | }) |
| 473 | 475 | curr_date = date |
| 474 | 476 | curr_author = author |
| | 477 | if permanent: |
| | 478 | cnum += 1 |
| | 479 | changes[-1]['cnum'] = cnum |
| 475 | 480 | if field == 'comment': |
| | 481 | if permanent and replyto == str(cnum): |
| | 482 | req.hdf['ticket.comment'] = '\n'.join( |
| | 483 | ['Replying to [comment:%s %s:]' % (replyto, author)] + |
| | 484 | ['>%s' % line for line in new.splitlines()] + ['']) |
| 476 | 485 | changes[-1]['comment'] = wiki_to_html(new, self.env, req, db) |
| 477 | 486 | elif field == 'description': |
| 478 | 487 | changes[-1]['fields'][field] = '' |