Edgewall Software

Ticket #454: comment_edit_delete.patch

File comment_edit_delete.patch, 11.6 KB (added by David Sorber <baranovich@…>, 3 years ago)
  • trac/ticket/api.py

     
    354354    def get_permission_actions(self): 
    355355        return ['TICKET_APPEND', 'TICKET_CREATE', 'TICKET_CHGPROP', 
    356356                'TICKET_VIEW', 'TICKET_EDIT_CC', 'TICKET_EDIT_DESCRIPTION', 
     357                'TICKET_EDIT_COMMENT', 
    357358                ('TICKET_MODIFY', ['TICKET_APPEND', 'TICKET_CHGPROP']), 
     359                ('TICKET_DELETE_COMMENT', ['TICKET_EDIT_COMMENT']), 
    358360                ('TICKET_ADMIN', ['TICKET_CREATE', 'TICKET_MODIFY', 
    359361                                  'TICKET_VIEW', 'TICKET_EDIT_CC', 
    360                                   'TICKET_EDIT_DESCRIPTION'])] 
     362                                  'TICKET_EDIT_DESCRIPTION', 
     363                                  'TICKET_DELETE_COMMENT'])] 
    361364 
    362365    # IWikiSyntaxProvider methods 
    363366 
  • trac/ticket/web_ui.py

     
    3939from trac.timeline.api import ITimelineEventProvider 
    4040from trac.util import get_reporter_id 
    4141from trac.util.compat import any 
    42 from trac.util.datefmt import format_datetime, to_timestamp, utc 
     42from trac.util.datefmt import parse_date, pretty_timedelta, to_datetime, \ 
     43                              format_datetime, to_timestamp, utc 
    4344from trac.util.text import CRLF, shorten_line, obfuscate_email_address, \ 
    4445                           exception_to_unicode 
    4546from trac.util.presentation import separated 
     
    474475                return self._render_history(req, ticket, data, text_fields) 
    475476            elif action == 'diff': 
    476477                return self._render_diff(req, ticket, data, text_fields) 
    477         elif req.method == 'POST': # 'Preview' or 'Submit' 
     478        elif req.method == 'POST': # 'Preview' or 'Submit' or Comment Edit/Del 
     479         
     480            # Cancel comment edit or delete 
     481            if 'cancel_comment' in req.args: 
     482                req.redirect(req.href.ticket(ticket.id))                 
     483            # Edit a ticket comment 
     484            elif ('edit_comment' in req.args and  
     485                  'TICKET_EDIT_COMMENT' in req.perm(ticket.resource)): 
     486                author = req.args.get('comment_author') 
     487                if (author == req.authname or  
     488                   'TRAC_ADMIN' in req.perm(ticket.resource)): 
     489                    comment = req.args.get('edited_comment') 
     490                    comment += "^^%s&%s^^" % (to_timestamp(datetime.now(utc)),  
     491                                              get_reporter_id(req, 'author')) 
     492                    when = req.args.get('edit_comment_when') 
     493                    when_ts = to_timestamp(parse_date(when, req.tz)) 
     494                    ticket.edit_comment(comment, when_ts) 
     495                    req.redirect(req.href.ticket(ticket.id)) 
     496            # Delete a ticket comment 
     497            elif ('delete_comment' in req.args and  
     498                  'TICKET_DELETE_COMMENT' in req.perm(ticket.resource)):              
     499                author = req.args.get('comment_author') 
     500                if (author == req.authname or  
     501                    'TRAC_ADMIN' in req.perm(ticket.resource)):   
     502                    when = req.args.get('delete_comment_when')                
     503                    when_ts = to_timestamp(parse_date(when, req.tz)) 
     504                    ticket.delete_comment(when_ts) 
     505                    req.redirect(req.href.ticket(ticket.id)) 
     506         
    478507            # Do any action on the ticket? 
    479508            actions = TicketSystem(self.env).get_available_actions( 
    480509                req, ticket) 
     
    12141243        """Insert ticket data into the template `data`""" 
    12151244        replyto = req.args.get('replyto') 
    12161245        data['replyto'] = replyto 
     1246        if req.args.get('cnum_edit'): 
     1247            data['cnum_edit'] = req.args.get('cnum_edit') 
     1248        elif req.args.get('cnum_del'): 
     1249            data['cnum_del'] = req.args.get('cnum_del') 
    12171250        data['version'] = ticket.resource.version 
    12181251        data['description_change'] = None 
    12191252 
     
    14421475                    current['cnum'] = autonum 
    14431476            # some common processing for fields 
    14441477            if field == 'comment': 
     1478                # find comments that have been edited and create pretty 
     1479                # "edited by" messages for them 
     1480                if new.endswith('^^'): 
     1481                    m = re.search('\^\^(.+?)\&(.+?)\^\^$', new) 
     1482                    ts = int(m.group(1)) 
     1483                    user = m.group(2) 
     1484                    new = new[:m.start(0)] 
     1485                    current['comment_edited'] = ("\n\n''Edited %s ago by %s.''"  
     1486                        % (pretty_timedelta(to_datetime(ts)), user)) 
    14451487                current['comment'] = new 
    14461488                if old: 
    14471489                    if '.' in old: # retrieve parent.child relationship 
  • trac/ticket/model.py

     
    363363 
    364364        for listener in TicketSystem(self.env).change_listeners: 
    365365            listener.ticket_deleted(self) 
     366         
     367    def edit_comment(self, cmt, ts, db=None): 
     368        db, handle_ta = self._get_db_for_write(db) 
     369        cursor = db.cursor() 
    366370 
     371        cursor.execute("UPDATE ticket_change SET newvalue=%s " 
     372                       "WHERE ticket=%s AND time=%s AND field='comment'", 
     373                       (cmt, self.id, ts)) 
     374        if handle_ta: 
     375            db.commit() 
     376         
     377    def delete_comment(self, ts, db=None): 
     378        db, handle_ta = self._get_db_for_write(db) 
     379        cursor = db.cursor() 
     380         
     381        cursor.execute("DELETE FROM ticket_change " 
     382                       "WHERE ticket=%s AND time=%s AND field='comment'", 
     383                       (self.id, ts)) 
     384        if handle_ta: 
     385            db.commit()         
    367386 
    368387def simplify_whitespace(name): 
    369388    """Strip spaces and remove duplicate spaces within names""" 
  • trac/ticket/templates/ticket.html

     
    4848      <a href="#comment:$cnum">$prefix$cnum</a> 
    4949    </py:def> 
    5050 
    51     <py:def function="display_change(change)"> 
     51    <py:def function="display_change(change, edit_cnum=0)"> 
    5252      <ul py:if="change.fields" class="changes"> 
    5353        <li py:for="field_name, field in change.fields.items()"> 
    5454          <strong>${field_name}</strong> 
     
    6969          </py:choose> 
    7070        </li> 
    7171      </ul> 
    72       <div py:if="'comment' in change" class="comment searchable" xml:space="preserve"> 
     72      <div py:if="'comment' in change and str(change.cnum) != edit_cnum" class="comment searchable" xml:space="preserve"> 
    7373        ${wiki_to_html(context, change.comment, escape_newlines=preserve_newlines)} 
     74        <py:if test="change.comment_edited"> 
     75          ${wiki_to_html(context, change.comment_edited, escape_newlines=preserve_newlines)} 
     76        </py:if> 
    7477      </div> 
    7578    </py:def> 
    7679 
     
    204207        <py:if test="ticket.exists and changes"> 
    205208          <h2>Change History</h2> 
    206209          <div id="changelog"> 
    207             <form py:for="change in changes" method="get" action="#comment" class="printableform"> 
     210            <py:for each="change in changes" class="printableform"> 
    208211              <div class="change"> 
    209212                <h3 class="change"> 
    210213                  <span class="threading" py:if="'cnum' in change" 
     
    224227                  Changed ${dateinfo(change.date)} ago by ${authorinfo(change.author)} 
    225228                </h3> 
    226229                <div py:if="'cnum' in change and 'TICKET_APPEND' in perm(ticket.resource)" class="inlinebuttons"> 
    227                   <input type="hidden" name="replyto" value="${change.cnum}" /> 
    228                   <input type="submit" value="${_('Reply')}" title="Reply to comment ${change.cnum}" /> 
     230                  <form method="get" action="#comment"> 
     231                    <input type="hidden" name="replyto" value="${change.cnum}" /> 
     232                    <input type="submit" value="${_('Reply')}" title="Reply to comment ${change.cnum}" /> 
     233                  </form> 
    229234                </div> 
    230                 ${display_change(change)} 
     235                ${display_change(change, ('TICKET_DELETE_COMMENT' in perm(ticket.resource) and cnum_edit or 0))} 
     236                <py:choose> 
     237                  <py:when test="'TICKET_EDIT_COMMENT' in perm(ticket.resource) and str(change.cnum) == cnum_edit"> 
     238                    <form method="post"> 
     239                      <p><textarea name="edited_comment" class="wikitext" rows="10" cols="78">${change.comment}</textarea></p> 
     240                      <input type="hidden" name="edit_comment_when" value="${change.date.isoformat()}" /> 
     241                      <input type="hidden" name="comment_author" value="${change.author}" />     
     242                      <input type="submit" name="edit_comment" value="${_('Submit changes')}" title="Submit changes to comment ${change.cnum}" /> 
     243                      <input type="submit" name="cancel_comment" value="${_('Cancel')}" title="Cancel comment edit" /> 
     244                    </form> 
     245                  </py:when> 
     246                  <py:when test="'TICKET_DELETE_COMMENT' in perm(ticket.resource) and str(change.cnum) == cnum_del"> 
     247                    <h4>Comment deletion is permanent. Are you sure you want to delete this comment?</h4> 
     248                    <form method="post"> 
     249                      <input type="hidden" name="delete_comment_when" value="${change.date.isoformat()}" /> 
     250                      <input type="hidden" name="comment_author" value="${change.author}" /> 
     251                      <input type="submit" name="delete_comment" value="${_('Delete')}" title="Delete comment ${change.cnm}" /> 
     252                      <input type="submit" name="cancel_comment" value="${_('Cancel')}" title="Cancel comment delete" /> 
     253                    </form> 
     254                  </py:when> 
     255                  <py:when test="change.comment"> 
     256                    <form py:if="'TICKET_EDIT_COMMENT' in perm(ticket.resource) and  
     257                                 (authname == change.author or 'TRAC_ADMIN' in perm(ticket.resource))"  
     258                          method="get" action="#comment:${change.cnum}" class="commentbuttons"> 
     259                      <input type="hidden" name="cnum_edit" value="${change.cnum}" /> 
     260                      <input type="submit" value="${_('Edit')}" title="Edit comment ${change.cnum}" /> 
     261                    </form> 
     262                    <form py:if="'TICKET_DELETE_COMMENT' in perm(ticket.resource) and  
     263                                 (authname == change.author or 'TRAC_ADMIN' in perm(ticket.resource))"  
     264                          method="get" action="#comment:${change.cnum}" class="commentbuttons"> 
     265                      <input type="hidden" name="cnum_del" value="${change.cnum}" /> 
     266                      <input type="submit" value="${_('Delete')}" title="Delete comment ${change.cnum}" /> 
     267                    </form>        
     268                  </py:when> 
     269                </py:choose> 
    231270              </div> 
    232             </form> 
     271            </py:for> 
    233272          </div> 
    234273        </py:if> 
    235274      </py:if> 
  • trac/htdocs/css/ticket.css

     
    7676} 
    7777 
    7878#changelog { border: 1px outset #996; padding: 1em } 
     79#changelog .commentbuttons { 
     80 display: inline; 
     81} 
    7982#preview { border: 1px solid #d7d7d7; padding: 1em } 
    8083#preview h3, #changelog h3 { 
    8184 border-bottom: 1px solid #d7d7d7;