django-orchestra/orchestra/contrib/issues/admin.py

324 lines
11 KiB
Python
Raw Normal View History

2014-05-08 16:59:35 +00:00
from django import forms
2015-05-19 13:27:04 +00:00
from django.conf.urls import url
2014-05-08 16:59:35 +00:00
from django.contrib import admin
from django.urls import reverse
2014-05-08 16:59:35 +00:00
from django.db import models
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.html import format_html, strip_tags
from django.utils.safestring import mark_safe
2014-05-08 16:59:35 +00:00
from django.utils.translation import ugettext_lazy as _
from markdown import markdown
2015-05-22 13:15:06 +00:00
from orchestra.admin import ExtendedModelAdmin
2014-10-27 13:29:02 +00:00
from orchestra.admin.utils import admin_link, admin_colored, wrap_admin_view, admin_date
2015-04-05 10:46:24 +00:00
from orchestra.contrib.contacts.models import Contact
2014-05-08 16:59:35 +00:00
from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets,
2014-10-27 13:29:02 +00:00
mark_as_unread, mark_as_read, set_default_queue)
2014-05-08 16:59:35 +00:00
from .filters import MyTicketsListFilter, TicketStateListFilter
from .forms import MessageInlineForm, TicketForm
from .helpers import get_ticket_changes, markdown_formated_changes, filter_actions
from .models import Ticket, Queue, Message
PRIORITY_COLORS = {
2014-05-08 16:59:35 +00:00
Ticket.HIGH: 'red',
Ticket.MEDIUM: 'darkorange',
Ticket.LOW: 'green',
}
STATE_COLORS = {
2014-05-08 16:59:35 +00:00
Ticket.NEW: 'grey',
Ticket.IN_PROGRESS: 'darkorange',
Ticket.FEEDBACK: 'purple',
Ticket.RESOLVED: 'green',
Ticket.REJECTED: 'firebrick',
Ticket.CLOSED: 'grey',
}
class MessageReadOnlyInline(admin.TabularInline):
model = Message
extra = 0
can_delete = False
2015-04-24 11:39:20 +00:00
fields = ('content_html',)
readonly_fields = ('content_html',)
2014-05-08 16:59:35 +00:00
class Media:
css = {
'all': ('orchestra/css/hide-inline-id.css',)
}
@mark_safe
2014-05-13 13:46:40 +00:00
def content_html(self, msg):
2014-05-08 16:59:35 +00:00
context = {
2014-05-13 13:46:40 +00:00
'number': msg.number,
2014-09-26 15:05:20 +00:00
'time': admin_date('created_at')(msg),
2014-07-23 18:28:40 +00:00
'author': admin_link('author')(msg) if msg.author else msg.author_name,
2014-05-08 16:59:35 +00:00
}
summary = _("#%(number)i Updated by %(author)s about %(time)s") % context
2014-05-08 16:59:35 +00:00
header = '<strong style="color:#666;">%s</strong><hr />' % summary
2014-05-13 13:46:40 +00:00
content = markdown(msg.content)
2014-05-08 16:59:35 +00:00
content = content.replace('>\n', '>')
content = '<div style="padding-left:20px;">%s</div>' % content
2014-05-08 16:59:35 +00:00
return header + content
content_html.short_description = _("Content")
2014-05-08 16:59:35 +00:00
def has_add_permission(self, request):
return False
2014-05-08 16:59:35 +00:00
def has_delete_permission(self, request, obj=None):
return False
class MessageInline(admin.TabularInline):
model = Message
extra = 1
max_num = 1
form = MessageInlineForm
can_delete = False
2015-04-24 11:39:20 +00:00
fields = ('content',)
2014-05-08 16:59:35 +00:00
def get_formset(self, request, obj=None, **kwargs):
""" hook request.user on the inline form """
self.form.user = request.user
return super(MessageInline, self).get_formset(request, obj, **kwargs)
2014-07-08 15:19:15 +00:00
def get_queryset(self, request):
2014-05-08 16:59:35 +00:00
""" Don't show any message """
2014-07-08 15:19:15 +00:00
qs = super(MessageInline, self).get_queryset(request)
2014-05-08 16:59:35 +00:00
return qs.none()
class TicketInline(admin.TabularInline):
2015-04-24 11:39:20 +00:00
fields = (
2014-05-08 16:59:35 +00:00
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
2014-09-26 15:05:20 +00:00
'colored_priority', 'created', 'updated'
2015-04-24 11:39:20 +00:00
)
readonly_fields = (
2014-05-08 16:59:35 +00:00
'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
2014-09-26 15:05:20 +00:00
'colored_priority', 'created', 'updated'
2015-04-24 11:39:20 +00:00
)
2014-05-08 16:59:35 +00:00
model = Ticket
extra = 0
max_num = 0
2014-07-21 15:43:36 +00:00
creator_link = admin_link('creator')
owner_link = admin_link('owner')
2014-09-26 15:05:20 +00:00
created = admin_link('created_at')
updated = admin_link('updated_at')
2014-07-24 09:53:34 +00:00
colored_state = admin_colored('state', colors=STATE_COLORS, bold=False)
colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
@mark_safe
2014-05-08 16:59:35 +00:00
def ticket_id(self, instance):
2014-07-23 18:28:40 +00:00
return '<b>%s</b>' % admin_link()(instance)
2014-05-08 16:59:35 +00:00
ticket_id.short_description = '#'
2015-05-22 13:15:06 +00:00
class TicketAdmin(ExtendedModelAdmin):
2015-04-24 11:39:20 +00:00
list_display = (
2014-05-08 16:59:35 +00:00
'unbold_id', 'bold_subject', 'display_creator', 'display_owner',
2014-09-26 15:05:20 +00:00
'display_queue', 'display_priority', 'display_state', 'updated'
2015-04-24 11:39:20 +00:00
)
2014-05-08 16:59:35 +00:00
list_display_links = ('unbold_id', 'bold_subject')
2015-04-24 11:39:20 +00:00
list_filter = (
2016-03-31 16:02:50 +00:00
MyTicketsListFilter, 'queue', 'priority', TicketStateListFilter,
2015-04-24 11:39:20 +00:00
)
2014-05-08 16:59:35 +00:00
default_changelist_filters = (
2016-03-31 16:02:50 +00:00
('state', 'OPEN'),
2014-05-08 16:59:35 +00:00
)
2014-09-26 15:05:20 +00:00
date_hierarchy = 'created_at'
2015-04-24 11:39:20 +00:00
search_fields = (
2014-05-08 16:59:35 +00:00
'id', 'subject', 'creator__username', 'creator__email', 'queue__name',
'owner__username'
2015-04-24 11:39:20 +00:00
)
actions = (
2014-05-08 16:59:35 +00:00
mark_as_unread, mark_as_read, 'delete_selected', reject_tickets,
resolve_tickets, close_tickets, take_tickets
2015-04-24 11:39:20 +00:00
)
sudo_actions = ('delete_selected',)
change_view_actions = (
2014-05-08 16:59:35 +00:00
resolve_tickets, close_tickets, reject_tickets, take_tickets
2015-04-24 11:39:20 +00:00
)
2014-05-08 16:59:35 +00:00
# change_form_template = "admin/orchestra/change_form.html"
form = TicketForm
2015-04-24 11:39:20 +00:00
add_inlines = ()
inlines = (MessageReadOnlyInline, MessageInline)
2014-05-08 16:59:35 +00:00
readonly_fields = (
'display_summary', 'display_queue', 'display_owner', 'display_state',
'display_priority'
)
readonly_fieldsets = (
(None, {
'fields': ('display_summary',
('display_queue', 'display_owner'),
('display_state', 'display_priority'),
'display_description')
}),
)
fieldsets = readonly_fieldsets + (
('Update', {
2014-05-13 13:46:40 +00:00
'classes': ('collapse',),
2014-05-08 16:59:35 +00:00
'fields': ('subject',
('queue', 'owner',),
('state', 'priority'),
'description')
}),
)
add_fieldsets = (
(None, {
'fields': ('subject',
('queue', 'owner',),
('state', 'priority'),
'description')
}),
)
2014-10-23 15:38:46 +00:00
list_select_related = ('queue', 'owner', 'creator')
2014-05-08 16:59:35 +00:00
class Media:
css = {
'all': ('issues/css/ticket-admin.css',)
}
js = (
'issues/js/ticket-admin.js',
)
2014-07-21 15:43:36 +00:00
display_creator = admin_link('creator')
display_queue = admin_link('queue')
display_owner = admin_link('owner')
updated = admin_date('updated_at')
2014-07-24 09:53:34 +00:00
display_state = admin_colored('state', colors=STATE_COLORS, bold=False)
display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
@mark_safe
2014-05-08 16:59:35 +00:00
def display_summary(self, ticket):
2014-05-13 13:46:40 +00:00
context = {
2014-07-23 18:28:40 +00:00
'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
2014-09-26 15:05:20 +00:00
'created': admin_date('created_at')(ticket),
2014-05-13 13:46:40 +00:00
'updated': '',
}
msg = ticket.messages.last()
if msg:
context.update({
2014-09-26 15:05:20 +00:00
'updated': admin_date('created_at')(msg),
2014-07-21 15:43:36 +00:00
'updater': admin_link('author')(self, msg) if msg.author else msg.author_name,
2014-05-13 13:46:40 +00:00
})
context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
2014-05-08 16:59:35 +00:00
display_summary.short_description = 'Summary'
2014-05-08 16:59:35 +00:00
def unbold_id(self, ticket):
""" Unbold id if ticket is read """
if ticket.is_read_by(self.user):
return format_html('<span style="font-weight:normal;font-size:11px;">{}</span>', ticket.pk)
2014-05-08 16:59:35 +00:00
return ticket.pk
unbold_id.short_description = "#"
unbold_id.admin_order_field = 'id'
2014-05-08 16:59:35 +00:00
def bold_subject(self, ticket):
""" Bold subject when tickets are unread for request.user """
if ticket.is_read_by(self.user):
return ticket.subject
return format_html("<strong class='unread'>{}</strong>", ticket.subject)
2014-05-08 16:59:35 +00:00
bold_subject.short_description = _("Subject")
bold_subject.admin_order_field = 'subject'
2014-05-08 16:59:35 +00:00
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
if db_field.name == 'subject':
kwargs['widget'] = forms.TextInput(attrs={'size':'120'})
return super(TicketAdmin, self).formfield_for_dbfield(db_field, **kwargs)
2014-05-08 16:59:35 +00:00
def save_model(self, request, obj, *args, **kwargs):
""" Define creator for new tickets """
if not obj.pk:
obj.creator = request.user
super(TicketAdmin, self).save_model(request, obj, *args, **kwargs)
obj.mark_as_read_by(request.user)
2014-05-08 16:59:35 +00:00
def get_urls(self):
""" add markdown preview url """
2015-05-19 13:27:04 +00:00
return [
url(r'^preview/$',
wrap_admin_view(self, self.message_preview_view))
] + super(TicketAdmin, self).get_urls()
2014-05-08 16:59:35 +00:00
def add_view(self, request, form_url='', extra_context=None):
""" Do not sow message inlines """
return super(TicketAdmin, self).add_view(request, form_url, extra_context)
2014-05-08 16:59:35 +00:00
def change_view(self, request, object_id, form_url='', extra_context=None):
""" Change view actions based on ticket state """
ticket = get_object_or_404(Ticket, pk=object_id)
# Change view actions based on ticket state
self.change_view_actions = filter_actions(self, ticket, request)
if request.method == 'POST':
# Hack: Include the ticket changes on the request.POST
# other approaches get really messy
changes = get_ticket_changes(self, request, ticket)
if changes:
content = markdown_formated_changes(changes)
2015-04-02 16:14:55 +00:00
content += request.POST['messages-2-0-content']
request.POST['messages-2-0-content'] = content
2014-05-08 16:59:35 +00:00
ticket.mark_as_read_by(request.user)
context = {'title': "Issue #%i - %s" % (ticket.id, ticket.subject)}
context.update(extra_context or {})
return super(TicketAdmin, self).change_view(request, object_id, form_url=form_url,
extra_context=context)
2014-05-08 16:59:35 +00:00
def changelist_view(self, request, extra_context=None):
# Hook user for bold_subject
self.user = request.user
return super(TicketAdmin,self).changelist_view(request, extra_context=extra_context)
2014-05-08 16:59:35 +00:00
def message_preview_view(self, request):
""" markdown preview render via ajax """
data = request.POST.get("data")
2014-09-26 15:05:20 +00:00
data_formated = markdown(strip_tags(data))
2014-05-08 16:59:35 +00:00
return HttpResponse(data_formated)
class QueueAdmin(admin.ModelAdmin):
2015-04-24 11:39:20 +00:00
list_display = ('name', 'default', 'num_tickets')
actions = (set_default_queue,)
inlines = (TicketInline,)
ordering = ('name',)
2014-05-08 16:59:35 +00:00
class Media:
css = {
'all': ('orchestra/css/hide-inline-id.css',)
}
2014-05-08 16:59:35 +00:00
def num_tickets(self, queue):
2014-07-22 21:47:01 +00:00
num = queue.tickets__count
2014-05-08 16:59:35 +00:00
url = reverse('admin:issues_ticket_changelist')
2016-03-31 16:02:50 +00:00
url += '?queue=%i' % queue.pk
return format_html('<a href="{}">{}</a>', url, num)
2014-05-08 16:59:35 +00:00
num_tickets.short_description = _("Tickets")
num_tickets.admin_order_field = 'tickets__count'
2014-05-13 13:46:40 +00:00
def get_list_display(self, request):
""" show notifications """
list_display = list(self.list_display)
2014-10-27 13:29:02 +00:00
for value, verbose in Contact.EMAIL_USAGES:
2014-05-13 13:46:40 +00:00
def display_notify(queue, notify=value):
return notify in queue.notify
display_notify.short_description = verbose
display_notify.boolean = True
list_display.append(display_notify)
return list_display
2014-07-08 15:19:15 +00:00
def get_queryset(self, request):
qs = super(QueueAdmin, self).get_queryset(request)
2014-05-08 16:59:35 +00:00
qs = qs.annotate(models.Count('tickets'))
return qs
admin.site.register(Ticket, TicketAdmin)
admin.site.register(Queue, QueueAdmin)