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
354 354 def get_permission_actions(self): 355 355 return ['TICKET_APPEND', 'TICKET_CREATE', 'TICKET_CHGPROP', 356 356 'TICKET_VIEW', 'TICKET_EDIT_CC', 'TICKET_EDIT_DESCRIPTION', 357 'TICKET_EDIT_COMMENT', 357 358 ('TICKET_MODIFY', ['TICKET_APPEND', 'TICKET_CHGPROP']), 359 ('TICKET_DELETE_COMMENT', ['TICKET_EDIT_COMMENT']), 358 360 ('TICKET_ADMIN', ['TICKET_CREATE', 'TICKET_MODIFY', 359 361 'TICKET_VIEW', 'TICKET_EDIT_CC', 360 'TICKET_EDIT_DESCRIPTION'])] 362 'TICKET_EDIT_DESCRIPTION', 363 'TICKET_DELETE_COMMENT'])] 361 364 362 365 # IWikiSyntaxProvider methods 363 366 -
trac/ticket/web_ui.py
39 39 from trac.timeline.api import ITimelineEventProvider 40 40 from trac.util import get_reporter_id 41 41 from trac.util.compat import any 42 from trac.util.datefmt import format_datetime, to_timestamp, utc 42 from trac.util.datefmt import parse_date, pretty_timedelta, to_datetime, \ 43 format_datetime, to_timestamp, utc 43 44 from trac.util.text import CRLF, shorten_line, obfuscate_email_address, \ 44 45 exception_to_unicode 45 46 from trac.util.presentation import separated … … 474 475 return self._render_history(req, ticket, data, text_fields) 475 476 elif action == 'diff': 476 477 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 478 507 # Do any action on the ticket? 479 508 actions = TicketSystem(self.env).get_available_actions( 480 509 req, ticket) … … 1214 1243 """Insert ticket data into the template `data`""" 1215 1244 replyto = req.args.get('replyto') 1216 1245 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') 1217 1250 data['version'] = ticket.resource.version 1218 1251 data['description_change'] = None 1219 1252 … … 1442 1475 current['cnum'] = autonum 1443 1476 # some common processing for fields 1444 1477 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)) 1445 1487 current['comment'] = new 1446 1488 if old: 1447 1489 if '.' in old: # retrieve parent.child relationship -
trac/ticket/model.py
363 363 364 364 for listener in TicketSystem(self.env).change_listeners: 365 365 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() 366 370 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() 367 386 368 387 def simplify_whitespace(name): 369 388 """Strip spaces and remove duplicate spaces within names""" -
trac/ticket/templates/ticket.html
48 48 <a href="#comment:$cnum">$prefix$cnum</a> 49 49 </py:def> 50 50 51 <py:def function="display_change(change )">51 <py:def function="display_change(change, edit_cnum=0)"> 52 52 <ul py:if="change.fields" class="changes"> 53 53 <li py:for="field_name, field in change.fields.items()"> 54 54 <strong>${field_name}</strong> … … 69 69 </py:choose> 70 70 </li> 71 71 </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"> 73 73 ${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> 74 77 </div> 75 78 </py:def> 76 79 … … 204 207 <py:if test="ticket.exists and changes"> 205 208 <h2>Change History</h2> 206 209 <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"> 208 211 <div class="change"> 209 212 <h3 class="change"> 210 213 <span class="threading" py:if="'cnum' in change" … … 224 227 Changed ${dateinfo(change.date)} ago by ${authorinfo(change.author)} 225 228 </h3> 226 229 <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> 229 234 </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> 231 270 </div> 232 </ form>271 </py:for> 233 272 </div> 234 273 </py:if> 235 274 </py:if> -
trac/htdocs/css/ticket.css
76 76 } 77 77 78 78 #changelog { border: 1px outset #996; padding: 1em } 79 #changelog .commentbuttons { 80 display: inline; 81 } 79 82 #preview { border: 1px solid #d7d7d7; padding: 1em } 80 83 #preview h3, #changelog h3 { 81 84 border-bottom: 1px solid #d7d7d7;
