diff --git a/docs/settings.rst b/docs/settings.rst index b6a5f03b..a8132c82 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -75,15 +75,15 @@ These changes are visible throughout django-helpdesk **Default:** ``HELPDESK_SHOW_CHANGE_PASSWORD = False`` - **HELPDESK_FOLLOWUP_MOD** Allow user to override default layout for 'followups' (work in progress) - + **Default:** ``HELPDESK_FOLLOWUP_MOD = False`` - **HELPDESK_CUSTOM_WELCOME** Show custom welcome message in dashboard? - + **Default:** ``HELPDESK_CUSTOM_WELCOME = False`` - **HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE ** Auto-subscribe user to ticket as a 'CC' if (s)he responds to a ticket? - + **Default:** ``HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE = False`` - **HELPDESK_EMAIL_SUBJECT_TEMPLATE ** Subject template for templated emails. ``%(subject)s`` represents the subject wording from the email template (e.g. "(Closed)"). @@ -97,28 +97,35 @@ Options shown on public pages These options only change display of items on public-facing pages, not staff pages. - **HELPDESK_VIEW_A_TICKET_PUBLIC** Show 'View a Ticket' section on public page? - + **Default:** ``HELPDESK_VIEW_A_TICKET_PUBLIC = True`` - **HELPDESK_SUBMIT_A_TICKET_PUBLIC** Show 'submit a ticket' section & form on public page? - + **Default:** ``HELPDESK_SUBMIT_A_TICKET_PUBLIC = True`` - **HELPDESK_SHOW_KB_ON_HOMEPAGE** Should we should the KB categories on the homepage? - + **Default:** ``HELPDESK_SHOW_KB_ON_HOMEPAGE = False`` Options that change ticket updates ---------------------------------- -- **HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE** Allow non-staff users to interact with tickets? This will also change how 'staff_member_required' +- **HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE** Allow non-staff users to interact with tickets? This will also change how 'staff_member_required' in staff.py will be defined. - + **Default:** ``HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = False`` +- **HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK** Apply a custom authorisation logic when defining 'staff_member_required' in staff.py. + If set, `HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE` will be ignored when determining staff access. + The value should be a function accepting the active user as a parameter and returning True if the user is considered helpdesk + staff. + + **Default:** ``HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = None`` + - **HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP** Show edit buttons in ticket follow ups? - + **Default:** ``HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP = True`` - **HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP** Show delete buttons in ticket follow ups if user is 'superuser'? diff --git a/helpdesk/settings.py b/helpdesk/settings.py index 52fb4900..7ba2acb5 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -2,7 +2,7 @@ Default settings for django-helpdesk. """ - +import warnings from django.conf import settings @@ -60,10 +60,17 @@ HELPDESK_SUBMIT_A_TICKET_PUBLIC = getattr(settings, 'HELPDESK_SUBMIT_A_TICKET_PU ''' options for update_ticket views ''' -# allow non-staff users to interact with tickets? this will also change how 'staff_member_required' +# allow non-staff users to interact with tickets? this will also change how 'staff_member_required' # in staff.py will be defined. HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = getattr(settings, 'HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE', False) +# apply a custom authorisation logic when defining 'staff_member_required' in staff.py. +HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = getattr(settings, 'HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK', None) +if HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK and HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: + warnings.warn( + "The HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE and HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK settings cannot be both defined. " + "Only HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK will be considered in determining staff access.", RuntimeWarning) + # show edit buttons in ticket follow ups. HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP = getattr(settings, 'HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP', True) @@ -73,10 +80,10 @@ HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP = getattr(settings, 'HELPDESK_SH # make all updates public by default? this will hide the 'is this update public' checkbox HELPDESK_UPDATE_PUBLIC_DEFAULT = getattr(settings, 'HELPDESK_UPDATE_PUBLIC_DEFAULT', False) -# only show staff users in ticket owner drop-downs +# only show staff users in ticket owner drop-downs HELPDESK_STAFF_ONLY_TICKET_OWNERS = getattr(settings, 'HELPDESK_STAFF_ONLY_TICKET_OWNERS', False) -# only show staff users in ticket cc drop-down +# only show staff users in ticket cc drop-down HELPDESK_STAFF_ONLY_TICKET_CC = getattr(settings, 'HELPDESK_STAFF_ONLY_TICKET_CC', False) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index e491c2d8..0aaa2cfe 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -39,8 +39,10 @@ from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTi from helpdesk.lib import send_templated_mail, query_to_dict, apply_query, safe_template_context from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency from helpdesk import settings as helpdesk_settings - -if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: + +if helpdesk_settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK: + staff_member_required = user_passes_test(helpdesk_settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK) +elif helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: # treat 'normal' users like 'staff' staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active) else: @@ -69,7 +71,7 @@ def dashboard(request): # closed & resolved tickets, assigned to current user tickets_closed_resolved = Ticket.objects.filter( - assigned_to=request.user, + assigned_to=request.user, status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS]) unassigned_tickets = Ticket.objects.filter( @@ -107,7 +109,7 @@ def dashboard(request): GROUP BY queue, name ORDER BY q.id; """) - + dash_tickets = query_to_dict(cursor.fetchall(), cursor.description) return render_to_response('helpdesk/dashboard.html', @@ -200,7 +202,7 @@ def view_ticket(request, ticket_id): if request.GET.has_key('take'): # Allow the user to assign the ticket to themselves whilst viewing it. - + # Trick the update_ticket() view into thinking it's being called with # a valid POST. request.POST = { @@ -261,7 +263,7 @@ view_ticket = staff_member_required(view_ticket) def return_ticketccstring_and_show_subscribe(user, ticket): ''' used in view_ticket() and followup_edit()''' - # create the ticketcc_string and check whether current user is already + # create the ticketcc_string and check whether current user is already # subscribed username = user.username.upper() useremail = user.email.upper() @@ -448,7 +450,7 @@ def update_ticket(request, ticket_id, public=False): messages_sent_to = [] - # ticket might have changed above, so we re-instantiate context with the + # ticket might have changed above, so we re-instantiate context with the # (possibly) updated ticket. context = safe_template_context(ticket) context.update( @@ -457,7 +459,7 @@ def update_ticket(request, ticket_id, public=False): ) if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))): - + if f.new_status == Ticket.RESOLVED_STATUS: template = 'resolved_' @@ -712,7 +714,7 @@ def ticket_list(request): or request.GET.has_key('status') or request.GET.has_key('q') or request.GET.has_key('sort') - or request.GET.has_key('sortreverse') + or request.GET.has_key('sortreverse') ): # Fall-back if no querying is being done, force the list to only @@ -751,7 +753,7 @@ def ticket_list(request): date_from = request.GET.get('date_from') if date_from: query_params['filtering']['created__gte'] = date_from - + date_to = request.GET.get('date_to') if date_to: query_params['filtering']['created__lte'] = date_to @@ -842,7 +844,7 @@ def edit_ticket(request, ticket_id): return HttpResponseRedirect(ticket.get_absolute_url()) else: form = EditTicketForm(instance=ticket) - + return render_to_response('helpdesk/edit_ticket.html', RequestContext(request, { 'form': form, @@ -854,7 +856,7 @@ def create_ticket(request): assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by('username') else: assignable_users = User.objects.filter(is_active=True).order_by('username') - + if request.method == 'POST': form = TicketForm(request.POST, request.FILES) form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()] @@ -955,7 +957,7 @@ def run_report(request, report): return HttpResponseRedirect(reverse("helpdesk_report_index")) report_queryset = Ticket.objects.all().select_related() - + from_saved_query = False saved_query = None @@ -993,7 +995,7 @@ def run_report(request, report): _('Nov'), _('Dec'), ) - + first_ticket = Ticket.objects.all().order_by('created')[0] first_month = first_ticket.created.month first_year = first_ticket.created.year @@ -1106,15 +1108,15 @@ def run_report(request, report): if report == 'daysuntilticketclosedbymonth': summarytable2[metric1, metric2] += metric3 - + table = [] - + if report == 'daysuntilticketclosedbymonth': for key in summarytable2.keys(): summarytable[key] = summarytable2[key] / summarytable[key] header1 = sorted(set(list( i.encode('utf-8') for i,_ in summarytable.keys() ))) - + column_headings = [col1heading] + possible_options # Pivot the data so that 'header1' fields are always first column @@ -1330,11 +1332,11 @@ def calc_basic_ticket_stats(Ticket): date_30_str = date_30.strftime('%Y-%m-%d') date_60_str = date_60.strftime('%Y-%m-%d') - # > 0 & <= 30 + # > 0 & <= 30 ota_le_30 = all_open_tickets.filter(created__gte = date_30_str) N_ota_le_30 = len(ota_le_30) - # >= 30 & <= 60 + # >= 30 & <= 60 ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60_str, created__lte = date_30_str) N_ota_le_60_ge_30 = len(ota_le_60_ge_30) @@ -1357,7 +1359,7 @@ def calc_basic_ticket_stats(Ticket): average_nbr_days_until_ticket_closed_last_60_days = calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days) # put together basic stats - basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, + basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, 'average_nbr_days_until_ticket_closed_last_60_days': average_nbr_days_until_ticket_closed_last_60_days, 'open_ticket_stats': ots, }