/usr/share/pyshared/tracopt/ticket/deleter.py is in trac 1.0.1-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | # -*- coding: utf-8 -*-
#
# Copyright (C) 2010 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
from genshi.builder import tag
from genshi.filters import Transformer
from genshi.filters.transform import StreamBuffer
from trac.core import Component, TracError, implements
from trac.ticket.model import Ticket
from trac.ticket.web_ui import TicketModule
from trac.util import get_reporter_id
from trac.util.datefmt import from_utimestamp
from trac.util.presentation import captioned_button
from trac.util.translation import _
from trac.web.api import IRequestFilter, IRequestHandler, ITemplateStreamFilter
from trac.web.chrome import ITemplateProvider, add_notice, add_stylesheet
class TicketDeleter(Component):
"""Ticket and ticket comment deleter.
This component allows deleting ticket comments and complete tickets. For
users having `TICKET_ADMIN` permission, it adds a "Delete" button next to
each "Reply" button on the page. The button in the ticket description
requests deletion of the complete ticket, and the buttons in the change
history request deletion of a single comment.
'''Comment and ticket deletion are irreversible (and therefore
''dangerous'') operations.''' For that reason, a confirmation step is
requested. The confirmation page shows the ticket box (in the case of a
ticket deletion) or the ticket change (in the case of a comment deletion).
"""
implements(ITemplateProvider, ITemplateStreamFilter, IRequestFilter,
IRequestHandler)
# ITemplateProvider methods
def get_htdocs_dirs(self):
return []
def get_templates_dirs(self):
from pkg_resources import resource_filename
return [resource_filename(__name__, 'templates')]
# ITemplateStreamFilter methods
def filter_stream(self, req, method, filename, stream, data):
if filename not in ('ticket.html', 'ticket_preview.html'):
return stream
ticket = data.get('ticket')
if not (ticket and ticket.exists
and 'TICKET_ADMIN' in req.perm(ticket.resource)):
return stream
# Insert "Delete" buttons for ticket description and each comment
def delete_ticket():
return tag.form(
tag.div(
tag.input(type='hidden', name='action', value='delete'),
tag.input(type='submit',
value=captioned_button(req, u'–', # 'EN DASH'
_("Delete")),
title=_('Delete ticket'),
class_="trac-delete"),
class_="inlinebuttons"),
action='#', method='get')
def delete_comment():
for event in buffer:
cnum, cdate = event[1][1].get('id')[12:].split('-', 1)
return tag.form(
tag.div(
tag.input(type='hidden', name='action',
value='delete-comment'),
tag.input(type='hidden', name='cnum', value=cnum),
tag.input(type='hidden', name='cdate', value=cdate),
tag.input(type='submit',
value=captioned_button(req, u'–', # 'EN DASH'
_("Delete")),
title=_('Delete comment %(num)s', num=cnum),
class_="trac-delete"),
class_="inlinebuttons"),
action='#', method='get')
buffer = StreamBuffer()
return stream | Transformer('//div[@class="description"]'
'/h3[@id="comment:description"]') \
.after(delete_ticket).end() \
.select('//div[starts-with(@class, "change")]/@id') \
.copy(buffer).end() \
.select('//div[starts-with(@class, "change") and @id]'
'/div[@class="trac-ticket-buttons"]') \
.prepend(delete_comment)
# IRequestFilter methods
def pre_process_request(self, req, handler):
if handler is not TicketModule(self.env):
return handler
action = req.args.get('action')
if action in ('delete', 'delete-comment'):
return self
else:
return handler
def post_process_request(self, req, template, data, content_type):
return template, data, content_type
# IRequestHandler methods
def match_request(self, req):
return False
def process_request(self, req):
id = int(req.args.get('id'))
req.perm('ticket', id).require('TICKET_ADMIN')
ticket = Ticket(self.env, id)
action = req.args['action']
cnum = req.args.get('cnum')
if req.method == 'POST':
if 'cancel' in req.args:
href = req.href.ticket(id)
if action == 'delete-comment':
href += '#comment:%s' % cnum
req.redirect(href)
if action == 'delete':
ticket.delete()
add_notice(req, _('The ticket #%(id)s has been deleted.',
id=ticket.id))
req.redirect(req.href())
elif action == 'delete-comment':
cdate = from_utimestamp(long(req.args.get('cdate')))
ticket.delete_change(cdate=cdate)
add_notice(req, _('The ticket comment %(num)s on ticket '
'#%(id)s has been deleted.',
num=cnum, id=ticket.id))
req.redirect(req.href.ticket(id))
tm = TicketModule(self.env)
data = tm._prepare_data(req, ticket)
tm._insert_ticket_data(req, ticket, data,
get_reporter_id(req, 'author'), {})
data.update(action=action, cdate=None)
if action == 'delete-comment':
data['cdate'] = req.args.get('cdate')
cdate = from_utimestamp(long(data['cdate']))
for change in data['changes']:
if change.get('date') == cdate:
data['change'] = change
data['cnum'] = change.get('cnum')
break
else:
raise TracError(_('Comment %(num)s not found', num=cnum))
add_stylesheet(req, 'common/css/ticket.css')
return 'ticket_delete.html', data, None
|