Edgewall Software

Ticket #2703: comment_permalink_and_reply_to.diff

File comment_permalink_and_reply_to.diff, 9.8 KB (added by cboos, 6 years ago)

Added permalinks for ticket comments and a reply-to comment feature. On trunk@3280.

  • htdocs/css/ticket.css

    diff -r c1db312f6768 htdocs/css/ticket.css
    a b  
    5151 font-size: 100%; 
    5252 font-weight: normal; 
    5353} 
     54span.commentcmd {  
     55 padding-top: 0.5em;  
     56 float: right;  
     57 font-size: 85%; 
     58} 
    5459#changelog .changes { list-style: square; margin-left: 2em; padding: 0 } 
    5560#changelog .comment { margin-left: 2em } 
    5661 
  • htdocs/css/trac.css

    diff -r c1db312f6768 htdocs/css/trac.css
    a b h1 :link, h1 :visited ,h2 :link, h2 :vis 
    4040h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited, 
    4141h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited { 
    4242 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} 
     53h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, 
     54h4:hover .anchor, h5:hover .anchor, h6:hover .anchor { 
     55 visibility: visible; 
    4356} 
    4457 
    4558@media screen { 
  • htdocs/css/wiki.css

    diff -r c1db312f6768 htdocs/css/wiki.css
    a b  
    2121#overview .multi { color: #999 } 
    2222#overview .ipnr { color: #999; font-size: 80% } 
    2323#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 } 
    3724 
    3825/* Styles for the page history table 
    3926   (extends the styles for "table.listing") */ 
  • templates/ticket.cs

    diff -r c1db312f6768 templates/ticket.cs
    a b  
    100100<?cs if:len(ticket.changes) ?><h2>Change History</h2> 
    101101<div id="changelog"><?cs 
    102102 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    &para;</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 
    105112  if:len(change.fields) ?> 
    106113   <ul class="changes"><?cs 
    107114   each:field = change.fields ?> 
  • trac/ticket/api.py

    diff -r c1db312f6768 trac/ticket/api.py
    a b class TicketSystem(Component): 
    186186 
    187187    def get_link_resolvers(self): 
    188188        return [('bug', self._format_link), 
    189                 ('ticket', self._format_link)] 
     189                ('ticket', self._format_link), 
     190                ('comment', self._format_comment_link)] 
    190191 
    191192    def get_wiki_syntax(self): 
    192193        yield ( 
    class TicketSystem(Component): 
    215216        return html.A(class_='missing ticket', rel='nofollow', 
    216217                      href=formatter.href.ticket(target))[label] 
    217218 
     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  
    218242    # ISearchSource methods 
    219243 
    220244    def get_search_filters(self, req): 
  • trac/ticket/model.py

    diff -r c1db312f6768 trac/ticket/model.py
    a b class Ticket(object): 
    265265        db = self._get_db(db) 
    266266        cursor = db.cursor() 
    267267        if when: 
    268             cursor.execute("SELECT time,author,field,oldvalue,newvalue " 
     268            cursor.execute("SELECT time,author,field,oldvalue,newvalue,1 " 
    269269                           "FROM ticket_change WHERE ticket=%s AND time=%s " 
    270270                           "UNION " 
    271                            "SELECT time,author,'attachment',null,filename " 
     271                           "SELECT time,author,'attachment',null,filename,0 " 
    272272                           "FROM attachment WHERE id=%s AND time=%s " 
    273273                           "UNION " 
    274                            "SELECT time,author,'comment',null,description " 
     274                           "SELECT time,author,'comment',null,description,0 " 
    275275                           "FROM attachment WHERE id=%s AND time=%s " 
    276276                           "ORDER BY time", 
    277277                           (self.id, when, str(self.id), when, self.id, when)) 
    278278        else: 
    279             cursor.execute("SELECT time,author,field,oldvalue,newvalue " 
     279            cursor.execute("SELECT time,author,field,oldvalue,newvalue,1 " 
    280280                           "FROM ticket_change WHERE ticket=%s " 
    281281                           "UNION " 
    282                            "SELECT time,author,'attachment',null,filename " 
     282                           "SELECT time,author,'attachment',null,filename,0 " 
    283283                           "FROM attachment WHERE id=%s " 
    284284                           "UNION " 
    285                            "SELECT time,author,'comment',null,description " 
     285                           "SELECT time,author,'comment',null,description,0 " 
    286286                           "FROM attachment WHERE id=%s " 
    287287                           "ORDER BY time", (self.id,  str(self.id), self.id)) 
    288288        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)) 
    291292        return log 
    292293 
    293294    def delete(self, db=None): 
  • trac/ticket/notification.py

    diff -r c1db312f6768 trac/ticket/notification.py
    a b class TicketNotifyEmail(NotifyEmail): 
    7575        changes = '' 
    7676        if not self.newticket and modtime:  # Ticket change 
    7777            changelog = ticket.get_changelog(modtime) 
    78             for date, author, field, old, new in changelog: 
     78            for date, author, field, old, new, permanent in changelog: 
    7979                self.hdf.set_unescaped('ticket.change.author', author) 
    8080                pfx = 'ticket.change.%s' % field 
    8181                newv = '' 
  • trac/ticket/web_ui.py

    diff -r c1db312f6768 trac/ticket/web_ui.py
    a b class TicketModule(TicketModuleBase): 
    226226 
    227227        ticket = Ticket(self.env, id, db=db) 
    228228        reporter_id = get_reporter_id(req) 
     229        replyto = req.args.get('replyto') 
    229230 
    230231        if req.method == 'POST': 
    231232            if not req.args.has_key('preview'): 
    class TicketModule(TicketModuleBase): 
    253254            # Store a timestamp in order to detect "mid air collisions" 
    254255            req.hdf['ticket.ts'] = ticket.time_changed 
    255256 
    256         self._insert_ticket_data(req, db, ticket, reporter_id) 
     257        self._insert_ticket_data(req, db, ticket, reporter_id, replyto) 
    257258 
    258259        # If the ticket is being shown in the context of a query, add 
    259260        # links to help navigate in the query result set 
    class TicketModule(TicketModuleBase): 
    426427 
    427428        req.redirect(req.href.ticket(ticket.id)) 
    428429 
    429     def _insert_ticket_data(self, req, db, ticket, reporter_id): 
     430    def _insert_ticket_data(self, req, db, ticket, reporter_id, replyto): 
    430431        """Insert ticket data into the hdf""" 
    431432        req.hdf['ticket'] = ticket.values 
    432433        req.hdf['ticket.id'] = ticket.id 
    class TicketModule(TicketModuleBase): 
    462463        changelog = ticket.get_changelog(db=db) 
    463464        curr_author = None 
    464465        curr_date   = 0 
     466        cnum = 0 
    465467        changes = [] 
    466         for date, author, field, old, new in changelog: 
     468        for date, author, field, old, new, permanent in changelog: 
    467469            if date != curr_date or author != curr_author: 
    468470                changes.append({ 
    469471                    'date': format_datetime(date), 
    class TicketModule(TicketModuleBase): 
    472474                }) 
    473475                curr_date = date 
    474476                curr_author = author 
     477                if permanent: 
     478                    cnum += 1 
     479                    changes[-1]['cnum'] = cnum 
    475480            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()] + ['']) 
    476485                changes[-1]['comment'] = wiki_to_html(new, self.env, req, db) 
    477486            elif field == 'description': 
    478487                changes[-1]['fields'][field] = ''