04/09: fixed searching ticket, and deleting/bulk deleting tickets
This commit is contained in:
+78
-3
@@ -408,7 +408,7 @@ def all_tickets():
|
||||
from sqlalchemy import func
|
||||
submitter_alias = db.aliased(User)
|
||||
assignee_alias = db.aliased(User)
|
||||
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '', 'g')
|
||||
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '')
|
||||
q = (
|
||||
q
|
||||
.outerjoin(submitter_alias, submitter_alias.id == Ticket.created_by_id)
|
||||
@@ -431,6 +431,44 @@ def all_tickets():
|
||||
search=search)
|
||||
|
||||
|
||||
@admin_bp.route('/tickets/<int:ticket_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_ticket(ticket_id):
|
||||
"""Permanently delete a single ticket and all its related data.
|
||||
|
||||
Cascades handled by SQLAlchemy relationships (cascade='all, delete-orphan'):
|
||||
comments, attachments (rows), notifications, history, satisfaction, links.
|
||||
Physical attachment files on disk are deleted explicitly before the commit.
|
||||
"""
|
||||
ticket = db.session.get(Ticket, ticket_id) or abort(404)
|
||||
|
||||
ticket_number = ticket.ticket_number
|
||||
ticket_title = ticket.title
|
||||
|
||||
# Delete physical attachment files from disk before removing DB rows
|
||||
upload_dir = current_app.config.get('UPLOAD_FOLDER', '')
|
||||
for attachment in ticket.attachments.all():
|
||||
if attachment.stored_name and upload_dir:
|
||||
filepath = os.path.join(upload_dir, attachment.stored_name)
|
||||
if os.path.isfile(filepath):
|
||||
try:
|
||||
os.remove(filepath)
|
||||
except OSError as exc:
|
||||
logger.warning(f'[TICKET DELETE] Could not remove file '
|
||||
f'{filepath}: {exc}')
|
||||
|
||||
log_action(current_user.id, 'admin_ticket_delete', 'ticket', ticket.id,
|
||||
f'ticket_number={ticket_number} title={ticket_title!r}')
|
||||
db.session.delete(ticket)
|
||||
db.session.commit()
|
||||
|
||||
logger.info(f'[TICKET DELETE] ticket_number={ticket_number} '
|
||||
f'ticket_id={ticket_id} by admin_id={current_user.id}')
|
||||
flash(f'Ticket {ticket_number} has been permanently deleted.', 'success')
|
||||
return redirect(url_for('admin.all_tickets'))
|
||||
|
||||
|
||||
# ─── Knowledge Base Management ────────────────────────────────────────────────
|
||||
|
||||
@admin_bp.route('/kb')
|
||||
@@ -793,7 +831,7 @@ def export_tickets():
|
||||
from sqlalchemy import func
|
||||
submitter_alias = db.aliased(User)
|
||||
assignee_alias = db.aliased(User)
|
||||
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '', 'g')
|
||||
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '')
|
||||
q = (
|
||||
q
|
||||
.outerjoin(submitter_alias, submitter_alias.id == Ticket.created_by_id)
|
||||
@@ -955,16 +993,53 @@ def bulk_ticket_action():
|
||||
flash('No tickets selected.', 'warning')
|
||||
return redirect(url_for('admin.all_tickets'))
|
||||
|
||||
valid_actions = ('resolve', 'close', 'assign_me', 'unassign')
|
||||
valid_actions = ('resolve', 'close', 'assign_me', 'unassign', 'delete')
|
||||
if action not in valid_actions:
|
||||
flash('Invalid action.', 'danger')
|
||||
return redirect(url_for('admin.all_tickets'))
|
||||
|
||||
# Bulk delete is admin-only
|
||||
if action == 'delete' and not current_user.is_admin:
|
||||
flash('Only administrators may delete tickets.', 'danger')
|
||||
return redirect(url_for('admin.all_tickets'))
|
||||
|
||||
tickets = Ticket.query.filter(Ticket.id.in_(ticket_ids)).all()
|
||||
now = datetime.utcnow()
|
||||
count = 0
|
||||
notif_tickets = [] # collect for post-commit notifications
|
||||
|
||||
# ── Bulk delete: handle separately so we can early-return cleanly ─────────
|
||||
if action == 'delete':
|
||||
upload_dir = current_app.config.get('UPLOAD_FOLDER', '')
|
||||
for ticket in tickets:
|
||||
for attachment in ticket.attachments.all():
|
||||
if attachment.stored_name and upload_dir:
|
||||
filepath = os.path.join(upload_dir, attachment.stored_name)
|
||||
if os.path.isfile(filepath):
|
||||
try:
|
||||
os.remove(filepath)
|
||||
except OSError as exc:
|
||||
logger.warning(f'[BULK DELETE] Could not remove file '
|
||||
f'{filepath}: {exc}')
|
||||
log_action(current_user.id, 'admin_ticket_delete', 'ticket',
|
||||
ticket.id,
|
||||
f'bulk=true ticket_number={ticket.ticket_number} title={ticket.title!r}')
|
||||
db.session.delete(ticket)
|
||||
count += 1
|
||||
|
||||
db.session.commit()
|
||||
logger.info(f'[BULK DELETE] deleted={count} ticket_ids={ticket_ids} '
|
||||
f'by admin_id={current_user.id}')
|
||||
flash(f'{count} ticket{"s" if count != 1 else ""} permanently deleted.', 'success')
|
||||
return redirect(url_for('admin.all_tickets',
|
||||
status = request.form.get('status', ''),
|
||||
priority = request.form.get('priority', ''),
|
||||
assigned = request.form.get('assigned', ''),
|
||||
q = request.form.get('q', ''),
|
||||
page = request.form.get('page', 1),
|
||||
))
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
for ticket in tickets:
|
||||
old_status = ticket.status
|
||||
old_assigned = ticket.assigned_to_id
|
||||
|
||||
Reference in New Issue
Block a user