From 6ae7b60ef40bf3111b1332951cc13aae0eab79cb Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 25 Dec 2019 11:21:47 +0300 Subject: [PATCH 01/67] fix error format time_spent --- helpdesk/lib.py | 6 +++--- helpdesk/models.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helpdesk/lib.py b/helpdesk/lib.py index 9a48af5b..0895ab7b 100644 --- a/helpdesk/lib.py +++ b/helpdesk/lib.py @@ -161,9 +161,9 @@ def format_time_spent(time_spent): """ if time_spent: - time_spent = "{0:02d}h:{0:02d}m".format( - int(time_spent.total_seconds() // 3600), - int((time_spent.total_seconds() % 3600) / 60) + time_spent = "{0:02d}h:{1:02d}m".format( + time_spent.seconds // 3600, + time_spent.seconds // 60 ) else: time_spent = "" diff --git a/helpdesk/models.py b/helpdesk/models.py index 339f4c9f..c724a0c3 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -35,9 +35,9 @@ from .templated_email import send_templated_mail def format_time_spent(time_spent): if time_spent: - time_spent = "{0:02d}h:{0:02d}m".format( - int(time_spent.total_seconds() // (3600)), - int((time_spent.total_seconds() % 3600) / 60) + time_spent = "{0:02d}h:{1:02d}m".format( + time_spent.seconds // 3600, + time_spent.seconds // 60 ) else: time_spent = "" From f72fbfa024034174b6a3a4738d4cd96e65f32d53 Mon Sep 17 00:00:00 2001 From: Arkadiy Korotaev Date: Sat, 4 Jan 2020 08:50:02 +0100 Subject: [PATCH 02/67] fix: avoid exception of using function get_tickets_in_queues as property --- helpdesk/views/staff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index c1a9b388..2d96208d 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -839,7 +839,7 @@ def ticket_list(request): if filter: try: - ticket = huser.get_tickets_in_queues.get(**filter) + ticket = huser.get_tickets_in_queues().get(**filter) return HttpResponseRedirect(ticket.staff_url) except Ticket.DoesNotExist: # Go on to standard keyword searching From 84456aca238b7109e06ea4c4c452cd81fd07dc34 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Mon, 6 Jan 2020 20:23:35 -0500 Subject: [PATCH 03/67] Fixed syntax error --- helpdesk/management/commands/escalate_tickets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/management/commands/escalate_tickets.py b/helpdesk/management/commands/escalate_tickets.py index 122333f0..edbf7307 100644 --- a/helpdesk/management/commands/escalate_tickets.py +++ b/helpdesk/management/commands/escalate_tickets.py @@ -105,7 +105,7 @@ def escalate_tickets(queues, verbose): t.send( {'submitter': ('escalated_submitter', context), 'ticket_cc': ('escalated_cc', context), - 'assigned_to': ('escalated_owner', context)} + 'assigned_to': ('escalated_owner', context)}, fail_silently=True, ) From 377e1c325098cb29b74075e04e4edf1383ca1969 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Mon, 6 Jan 2020 22:18:48 -0500 Subject: [PATCH 04/67] Added better support for custom user models by not requiring a first_name attribute --- helpdesk/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpdesk/serializers.py b/helpdesk/serializers.py index c3b311b5..ef5ed27d 100644 --- a/helpdesk/serializers.py +++ b/helpdesk/serializers.py @@ -46,8 +46,8 @@ class DatatablesTicketSerializer(serializers.ModelSerializer): def get_assigned_to(self, obj): if obj.assigned_to: - if obj.assigned_to.first_name: - return (obj.assigned_to.first_name) + if obj.assigned_to.get_full_name(): + return (obj.assigned_to.get_full_name()) else: return (obj.assigned_to.email) else: From 896307582a95140054196816cfb20ce0b799f862 Mon Sep 17 00:00:00 2001 From: Arkadiy Korotaev Date: Tue, 7 Jan 2020 13:47:36 +0100 Subject: [PATCH 05/67] fix: Force HELPDESK_PUBLIC_TICKET_QUEUE for anon tickets Before: we set initial value for the widget and had it hidden. So user could still change the queue with some HTML knowledge. Now: we drop the field at all and assign queue directly, utterly ignoring the POST request content for "queue" field. --- helpdesk/forms.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index bd6b4603..aec25d10 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -192,8 +192,12 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): self.customfield_to_field(field, instanceargs) + def _get_queue(self): + # this procedure is re-defined for anon submission form + return Queue.objects.get(id=int(self.cleaned_data['queue'])) + def _create_ticket(self): - queue = Queue.objects.get(id=int(self.cleaned_data['queue'])) + queue = self._get_queue() ticket = Ticket(title=self.cleaned_data['title'], submitter_email=self.cleaned_data['submitter_email'], @@ -338,15 +342,29 @@ class PublicTicketForm(AbstractTicketForm): Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) - if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE'): - self.fields['queue'].widget = forms.HiddenInput() + del self.fields['queue'] + else: + self.fields['queue'].choices = [ + ('', '--------') + ] + [ + (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True) + ] if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_PRIORITY'): self.fields['priority'].widget = forms.HiddenInput() if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_DUE_DATE'): self.fields['due_date'].widget = forms.HiddenInput() - self.fields['queue'].choices = [('', '--------')] + [ - (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)] + + def _get_queue(self): + if getattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE', None): + # force queue to be the pre-defined one + # (only for anon submissions) + return Queue.objects.filter( + slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE + ).first() + else: + # get the queue user entered + return Queue.objects.get(id=int(self.cleaned_data['queue'])) def save(self): """ From 867b1ebc59bfedda70120b8b86185f3b8bbb73f1 Mon Sep 17 00:00:00 2001 From: umgelurgel Date: Mon, 13 Jan 2020 20:54:47 +0100 Subject: [PATCH 06/67] Update the model name in the fixtures. --- .gitignore | 1 + demo/demodesk/fixtures/demo.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9e6dab0f..162dbae9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ docs/doctrees/* .pydevproject .directory *.swp +.idea # ignore demo attachments that user might have added helpdesk/attachments/ diff --git a/demo/demodesk/fixtures/demo.json b/demo/demodesk/fixtures/demo.json index ee491702..b1565fea 100644 --- a/demo/demodesk/fixtures/demo.json +++ b/demo/demodesk/fixtures/demo.json @@ -7,9 +7,9 @@ {"model": "helpdesk.followup", "pk": 2, "fields": {"ticket": 2, "date": "2017-03-20T04:54:53.031Z", "title": "Ticket Opened", "comment": "Something else with some other product. Not a big deal.", "public": true, "user": 1, "new_status": null}}, {"model": "helpdesk.ticket", "pk": 3, "fields": {"title": "Something with an attachment", "queue": 1, "created": "2017-03-20T05:14:36.320Z", "modified": "2017-03-20T05:28:28.695Z", "submitter_email": "helpdesk@example.com", "assigned_to": null, "status": 1, "on_hold": false, "description": "WHOA!", "resolution": null, "priority": 1, "due_date": null, "last_escalation": null}}, {"model": "helpdesk.followup", "pk": 3, "fields": {"ticket": 3, "date": "2017-03-20T05:14:36.345Z", "title": "Ticket Opened", "comment": "WHOA!", "public": true, "user": 1, "new_status": null}}, - {"model": "helpdesk.attachment", "pk": 1, "fields": {"followup": 3, "file": "helpdesk/attachments/DH-3/3/someinfo.txt", "filename": "someinfo.txt", "mime_type": "text/plain", "size": 56}}, + {"model": "helpdesk.followupattachment", "pk": 1, "fields": {"followup": 3, "file": "helpdesk/attachments/DH-3/3/someinfo.txt", "filename": "someinfo.txt", "mime_type": "text/plain", "size": 56}}, {"model": "helpdesk.followup", "pk": 4, "fields": {"ticket": 3, "date": "2017-03-20T05:28:28.458Z", "title": "Comment", "comment": "An image attachment goes here!", "public": true, "user": 1, "new_status": null}}, - {"model": "helpdesk.attachment", "pk": 2, "fields": {"followup": 4, "file": "helpdesk/attachments/DH-3/4/helpdesk.png", "filename": "helpdesk.png", "mime_type": "image/png", "size": 30229}}, + {"model": "helpdesk.followupattachment", "pk": 2, "fields": {"followup": 4, "file": "helpdesk/attachments/DH-3/4/helpdesk.png", "filename": "helpdesk.png", "mime_type": "image/png", "size": 30229}}, {"model": "helpdesk.kbcategory", "pk": 1, "fields": {"title": "KB Cat 1", "slug": "kbcat1", "description": "Some category of KB info"}}, {"model": "helpdesk.kbcategory", "pk": 2, "fields": {"title": "KB Cat 2", "slug": "kbcat2", "description": "Here is another category. Enjoy!"}}, {"model": "helpdesk.kbitem", "pk": 1, "fields": {"category": 1, "title": "Django-Helpdesk", "question": "What is Django-Helpdesk?", "answer": "An open source helpdesk written in python using the awesome django framework.", "votes": 0, "recommendations": 0, "last_updated": "2017-04-02T19:02:17.213Z"}}, From 8985fc759d2d78ee0f38ed8b0ffd35b0d0975cae Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 18 Oct 2019 17:38:23 +0200 Subject: [PATCH 07/67] Remove hardcoded pk from test suit --- helpdesk/tests/test_attachments.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/helpdesk/tests/test_attachments.py b/helpdesk/tests/test_attachments.py index 0b029d73..9b71a962 100644 --- a/helpdesk/tests/test_attachments.py +++ b/helpdesk/tests/test_attachments.py @@ -94,7 +94,11 @@ class AttachmentUnitTests(TestCase): 'content-type': 'text/utf8', } self.test_file = SimpleUploadedFile.from_dict(self.file_attrs) - self.follow_up = models.FollowUp(ticket=models.Ticket(queue=models.Queue())) + self.follow_up = models.FollowUp.objects.create( + ticket=models.Ticket.objects.create( + queue=models.Queue.objects.create() + ) + ) @mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True) def test_unicode_attachment_filename(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save): @@ -109,6 +113,7 @@ class AttachmentUnitTests(TestCase): ) self.assertEqual(filename, self.file_attrs['filename']) +<<<<<<< HEAD # TODO: FIXME: what's wrong with this test that we get integrity errors? # @mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True) # def test_autofill(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save): @@ -122,6 +127,18 @@ class AttachmentUnitTests(TestCase): # self.assertEqual(obj.filename, self.file_attrs['filename']) # self.assertEqual(obj.size, len(self.file_attrs['content'])) # self.assertEqual(obj.mime_type, "text/plain") +======= + @mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True) + def test_autofill(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save): + """ check utf-8 data is parsed correctly """ + obj = models.FollowUpAttachment.objects.create( + followup=self.follow_up, + file=self.test_file + ) + self.assertEqual(obj.filename, self.file_attrs['filename']) + self.assertEqual(obj.size, len(self.file_attrs['content'])) + self.assertEqual(obj.mime_type, "text/plain") +>>>>>>> b899c97... Remove hardcoded pk from test suit def test_kbi_attachment(self, mock_att_save, mock_queue_save, mock_ticket_save): """ check utf-8 data is parsed correctly """ From 627f2ba21bd02b083992e683076bcd5bb526c080 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 18 Oct 2019 20:48:38 +0200 Subject: [PATCH 08/67] Add search button next to user emails --- helpdesk/templates/helpdesk/ticket_desc_table.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 409ad521..52b3000e 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -37,8 +37,10 @@ {% endifequal %} {% trans "Submitter E-Mail" %} - {{ ticket.submitter_email }} - {% if user.is_superuser %} {% if submitter_userprofile_url %}{% endif %} + {{ ticket.submitter_email }} + {% if user.is_superuser %} {% if submitter_userprofile_url %}{% endif %} + + {% endif %} From c4a1b9ed664dae25ee6bdf67fced0fff264ca098 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 18 Oct 2019 20:49:24 +0200 Subject: [PATCH 09/67] Move Query Results above Query Selection --- helpdesk/templates/helpdesk/ticket_list.html | 115 +++++++++---------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 2bc21393..fdc395b0 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -30,6 +30,63 @@ {% block helpdesk_body %} {% load in_list %} +
+
+ + {% trans "Query Results" %} +
+
+ {{ search_message|safe }} +
+ + + + + + + + + + + + + + +
 {% trans "Ticket" %}{% trans "Prority" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Due Date" %}{% trans "Owner" %}{% trans "Time Spent" %}
+ +

+ + + + + + +

+ +

+ + + +

+ {% csrf_token %}
+
+ +
+ +
@@ -162,64 +219,6 @@
- -
-
- - {% trans "Query Results" %} -
-
- {{ search_message|safe }} -
- - - - - - - - - - - - - - -
 {% trans "Ticket" %}{% trans "Prority" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Due Date" %}{% trans "Owner" %}{% trans "Time Spent" %}
- -

- - - - - - -

- -

- - - -

- {% csrf_token %}
-
- -
- - {% endblock %} From 6eee6d196c1721badc5c0aaae54ac8282b3b8cd2 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 21 Oct 2019 19:21:22 +0200 Subject: [PATCH 10/67] Add timeline view for ticket queries --- helpdesk/templates/helpdesk/ticket_list.html | 132 +++++++++++++------ helpdesk/urls.py | 5 + helpdesk/views/staff.py | 40 ++++++ 3 files changed, 137 insertions(+), 40 deletions(-) diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index fdc395b0..f9818f99 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -30,58 +30,110 @@ {% block helpdesk_body %} {% load in_list %} -
+
- - {% trans "Query Results" %} +
{{ search_message|safe }} -
- - - - - - - - - - - - - - -
 {% trans "Ticket" %}{% trans "Prority" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Due Date" %}{% trans "Owner" %}{% trans "Time Spent" %}
+
+
+ + + + + + + + + + + + + + + +
 {% trans "Ticket" %}{% trans "Prority" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Due Date" %}{% trans "Owner" %}{% trans "Time Spent" %}
-

+

- -

+

-

- - - -

- {% csrf_token %} +

+ + + +

+ {% csrf_token %} + +
+
+
+ + + + + + + + + +
+
diff --git a/helpdesk/urls.py b/helpdesk/urls.py index d5acabd5..059b088d 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -151,6 +151,11 @@ urlpatterns = [ url(r'^datatables_ticket_list/(?P{})$'.format(base64_pattern), staff.datatables_ticket_list, name="datatables_ticket_list"), + + url(r'^timeline_ticket_list/(?P{})$'.format(base64_pattern), + staff.timeline_ticket_list, + name="timeline_ticket_list"), + ] urlpatterns += [ diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 2d96208d..87e1b68a 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -975,6 +975,46 @@ def datatables_ticket_list(request, query): return (JsonResponse(result, status=status.HTTP_200_OK)) +@helpdesk_staff_member_required +@api_view(['GET']) +def timeline_ticket_list(request, query): + """ + Datatable on ticket_list.html uses this view from to get objects to display + on the table. query_tickets_by_args is at lib.py, DatatablesTicketSerializer is in + serializers.py. The serializers and this view use django-rest_framework methods + """ + tickets = get_query(query, HelpdeskUser(request.user)) + events = [] + + def mk_timeline_date(date): + return { + 'year': date.year, + 'month': date.month, + 'day': date.day, + 'hour': date.hour, + 'minute': date.minute, + 'second': date.second, + 'second': date.second, + } + for ticket in tickets: + for followup in ticket.followup_set.all(): + event = { + 'start_date': mk_timeline_date(followup.date), + 'text': { + 'headline': ticket.title + '
' + followup.title, + 'text': (followup.comment if followup.comment else _('No text')) + '
%s' % + (reverse('helpdesk:view', kwargs={'ticket_id': ticket.pk}), _("View ticket")), + }, + 'group': _('Messages'), + } + events.append(event) + + result = { + 'events': events, + } + return (JsonResponse(result, status=status.HTTP_200_OK)) + + @helpdesk_staff_member_required def edit_ticket(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) From b96d725239f1e69eb8b0e968ea8095f712c79404 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Wed, 30 Oct 2019 19:34:58 +0100 Subject: [PATCH 11/67] Refactor query code into customizable class --- helpdesk/query.py | 241 +++++++++++------- .../templates/helpdesk/filters/keywords.html | 2 +- helpdesk/templates/helpdesk/ticket_list.html | 4 +- .../tests/test_per_queue_staff_permission.py | 6 +- helpdesk/views/staff.py | 56 +--- 5 files changed, 162 insertions(+), 147 deletions(-) diff --git a/helpdesk/query.py b/helpdesk/query.py index a2263326..6a56baea 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -1,12 +1,16 @@ from django.db.models import Q from django.core.cache import cache - -from model_utils import Choices +from django.urls import reverse +from django.utils.translation import ugettext as _ from base64 import b64encode from base64 import b64decode import json +from model_utils import Choices + +from helpdesk.serializers import DatatablesTicketSerializer + def query_to_base64(query): """ @@ -47,60 +51,26 @@ def query_to_dict(results, descriptions): return output -def apply_query(queryset, params): - """ - Apply a dict-based set of filters & parameters to a queryset. - - queryset is a Django queryset, eg MyModel.objects.all() or - MyModel.objects.filter(user=request.user) - - params is a dictionary that contains the following: - filtering: A dict of Django ORM filters, eg: - {'user__id__in': [1, 3, 103], 'title__contains': 'foo'} - - search_string: A freetext search string - - sorting: The name of the column to sort by - """ - for key in params['filtering'].keys(): - filter = {key: params['filtering'][key]} - queryset = queryset.filter(**filter) - - search = params.get('search_string', '') - if search: - qset = ( - Q(title__icontains=search) | - Q(description__icontains=search) | - Q(resolution__icontains=search) | - Q(submitter_email__icontains=search) | - Q(ticketcustomfieldvalue__value__icontains=search) - ) - - queryset = queryset.filter(qset) - - sorting = params.get('sorting', None) - if sorting: - sortreverse = params.get('sortreverse', None) - if sortreverse: - sorting = "-%s" % sorting - queryset = queryset.order_by(sorting) - - return queryset +def get_search_filter_args(search): + if search.startswith('queue:'): + return Q(queue__title__icontains=search[len('queue:'):]) + if search.startswith('priority:'): + return Q(priority__icontains=search[len('priority:'):]) + return ( + Q(id__icontains=search) | + Q(title__icontains=search) | + Q(description__icontains=search) | + Q(priority__icontains=search) | + Q(resolution__icontains=search) | + Q(submitter_email__icontains=search) | + Q(assigned_to__email__icontains=search) | + Q(ticketcustomfieldvalue__value__icontains=search) | + Q(created__icontains=search) | + Q(due_date__icontains=search) + ) -def get_query(query, huser): - # Prefilter the allowed tickets - objects = cache.get(huser.user.email + query) - if objects is not None: - return objects - tickets = huser.get_tickets_in_queues().select_related() - query_params = query_from_base64(query) - ticket_qs = apply_query(tickets, query_params) - cache.set(huser.user.email + query, ticket_qs, timeout=3600) - return ticket_qs - - -ORDER_COLUMN_CHOICES = Choices( +DATATABLES_ORDER_COLUMN_CHOICES = Choices( ('0', 'id'), ('2', 'priority'), ('3', 'title'), @@ -112,45 +82,132 @@ ORDER_COLUMN_CHOICES = Choices( ) -def query_tickets_by_args(objects, order_by, **kwargs): - """ - This function takes in a list of ticket objects from the views and throws it - to the datatables on ticket_list.html. If a search string was entered, this - function filters existing dataset on search string and returns a filtered - filtered list. The `draw`, `length` etc parameters are for datatables to - display meta data on the table contents. The returning queryset is passed - to a Serializer called DatatablesTicketSerializer in serializers.py. - """ - draw = int(kwargs.get('draw', None)[0]) - length = int(kwargs.get('length', None)[0]) - start = int(kwargs.get('start', None)[0]) - search_value = kwargs.get('search[value]', None)[0] - order_column = kwargs.get('order[0][column]', None)[0] - order = kwargs.get('order[0][dir]', None)[0] +def get_query_class(): + from django.conf import settings - order_column = ORDER_COLUMN_CHOICES[order_column] - # django orm '-' -> desc - if order == 'desc': - order_column = '-' + order_column + def _get_query_class(): + return __Query__ + return getattr(settings, + 'HELPDESK_QUERY_CLASS', + _get_query_class)() - queryset = objects.all().order_by(order_by) - total = queryset.count() - if search_value: - queryset = queryset.filter(Q(id__icontains=search_value) | - Q(priority__icontains=search_value) | - Q(title__icontains=search_value) | - Q(queue__title__icontains=search_value) | - Q(status__icontains=search_value) | - Q(created__icontains=search_value) | - Q(due_date__icontains=search_value) | - Q(assigned_to__email__icontains=search_value)) +class __Query__: + def __init__(self, huser, base64query=None, query_params=None): + self.huser = huser + self.params = query_params if query_params else query_from_base64(base64query) + self.base64 = base64query if base64query else query_to_base64(query_params) + self.result = None - count = queryset.count() - queryset = queryset.order_by(order_column)[start:start + length] - return { - 'items': queryset, - 'count': count, - 'total': total, - 'draw': draw - } + def get_search_filter_args(self): + search = self.params.get('search_string', '') + return get_search_filter_args(search) + + def __run__(self, queryset): + """ + Apply a dict-based set of filters & parameters to a queryset. + + queryset is a Django queryset, eg MyModel.objects.all() or + MyModel.objects.filter(user=request.user) + + params is a dictionary that contains the following: + filtering: A dict of Django ORM filters, eg: + {'user__id__in': [1, 3, 103], 'title__contains': 'foo'} + + search_string: A freetext search string + + sorting: The name of the column to sort by + """ + for key in self.params['filtering'].keys(): + filter = {key: self.params['filtering'][key]} + queryset = queryset.filter(**filter) + queryset = queryset.filter(self.get_search_filter_args()) + sorting = self.params.get('sorting', None) + if sorting: + sortreverse = self.params.get('sortreverse', None) + if sortreverse: + sorting = "-%s" % sorting + queryset = queryset.order_by(sorting) + return queryset + + def refresh_query(self): + tickets = self.huser.get_tickets_in_queues().select_related() + ticket_qs = self.__run__(tickets) + cache.set(self.huser.user.email + self.base64, ticket_qs, timeout=3600) + return ticket_qs + + def get(self): + # Prefilter the allowed tickets + objects = cache.get(self.huser.user.email + self.base64) + if objects is not None: + return objects + return self.refresh_query() + + def get_datatables_context(self, **kwargs): + """ + This function takes in a list of ticket objects from the views and throws it + to the datatables on ticket_list.html. If a search string was entered, this + function filters existing dataset on search string and returns a filtered + filtered list. The `draw`, `length` etc parameters are for datatables to + display meta data on the table contents. The returning queryset is passed + to a Serializer called DatatablesTicketSerializer in serializers.py. + """ + objects = self.get() + order_by = '-date_created' + draw = int(kwargs.get('draw', None)[0]) + length = int(kwargs.get('length', None)[0]) + start = int(kwargs.get('start', None)[0]) + search_value = kwargs.get('search[value]', None)[0] + order_column = kwargs.get('order[0][column]', None)[0] + order = kwargs.get('order[0][dir]', None)[0] + + order_column = DATATABLES_ORDER_COLUMN_CHOICES[order_column] + # django orm '-' -> desc + if order == 'desc': + order_column = '-' + order_column + + queryset = objects.all().order_by(order_by) + total = queryset.count() + + if search_value: + queryset = queryset.filter(get_search_filter_args(search_value)) + + count = queryset.count() + queryset = queryset.order_by(order_column)[start:start + length] + return { + 'data': DatatablesTicketSerializer(queryset, many=True).data, + 'recordsFiltered': count, + 'recordsTotal': total, + 'draw': draw + } + + def get_timeline_context(self): + events = [] + + for ticket in self.get(): + for followup in ticket.followup_set.all(): + event = { + 'start_date': self.mk_timeline_date(followup.date), + 'text': { + 'headline': ticket.title + '
' + followup.title, + 'text': (followup.comment if followup.comment else _('No text')) + '
%s' % + (reverse('helpdesk:view', kwargs={'ticket_id': ticket.pk}), _("View ticket")), + }, + 'group': _('Messages'), + } + events.append(event) + + return { + 'events': events, + } + + def mk_timeline_date(self, date): + return { + 'year': date.year, + 'month': date.month, + 'day': date.day, + 'hour': date.hour, + 'minute': date.minute, + 'second': date.second, + 'second': date.second, + } diff --git a/helpdesk/templates/helpdesk/filters/keywords.html b/helpdesk/templates/helpdesk/filters/keywords.html index 60af3fd6..adf37a7e 100644 --- a/helpdesk/templates/helpdesk/filters/keywords.html +++ b/helpdesk/templates/helpdesk/filters/keywords.html @@ -9,5 +9,5 @@
-

{% trans "Keywords are case-insensitive, and will be looked for in the title, body and submitter fields." %}

+

{% trans "Keywords are case-insensitive, and will be looked for pretty much everywhere possible. Prepend with 'queue:' or 'priority:' to search by queue or priority." %}

diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index f9818f99..3e56eb7c 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -125,8 +125,8 @@ if(!timeline_loaded){ timeline = new TL.Timeline( 'timeline-embed', - '{% url 'helpdesk:timeline_ticket_list' urlsafe_query %}', - ) + '{% url 'helpdesk:timeline_ticket_list' urlsafe_query %}' + ); timeline_loaded = true; } }); diff --git a/helpdesk/tests/test_per_queue_staff_permission.py b/helpdesk/tests/test_per_queue_staff_permission.py index b70d7816..5c036322 100644 --- a/helpdesk/tests/test_per_queue_staff_permission.py +++ b/helpdesk/tests/test_per_queue_staff_permission.py @@ -6,7 +6,7 @@ from django.test.client import Client from helpdesk.models import Queue, Ticket from helpdesk import settings -from helpdesk.query import get_query +from helpdesk.query import __Query__ from helpdesk.user import HelpdeskUser @@ -166,7 +166,7 @@ class PerQueueStaffMembershipTestCase(TestCase): for identifier in self.IDENTIFIERS: self.client.login(username='User_%d' % identifier, password=str(identifier)) response = self.client.get(reverse('helpdesk:list')) - tickets = get_query(response.context['urlsafe_query'], HelpdeskUser(self.identifier_users[identifier])) + tickets = __Query__(HelpdeskUser(self.identifier_users[identifier]), base64query = response.context['urlsafe_query']).get() self.assertEqual( len(tickets), identifier * 2, @@ -186,7 +186,7 @@ class PerQueueStaffMembershipTestCase(TestCase): # Superuser self.client.login(username='superuser', password='superuser') response = self.client.get(reverse('helpdesk:list')) - tickets = get_query(response.context['urlsafe_query'], HelpdeskUser(self.superuser)) + tickets = __Query__(HelpdeskUser(self.superuser), base64query = response.context['urlsafe_query']).get() self.assertEqual( len(tickets), 6, diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 87e1b68a..c374b51b 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -27,18 +27,14 @@ from django.utils import timezone from django.views.generic.edit import FormView, UpdateView from helpdesk.query import ( + get_query_class, query_to_dict, - get_query, - apply_query, - query_tickets_by_args, query_to_base64, query_from_base64, ) from helpdesk.user import HelpdeskUser -from helpdesk.serializers import DatatablesTicketSerializer - from helpdesk.decorators import ( helpdesk_staff_member_required, helpdesk_superuser_required, is_helpdesk_staff @@ -71,6 +67,7 @@ import re User = get_user_model() +Query = get_query_class() if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: # treat 'normal' users like 'staff' @@ -896,7 +893,7 @@ def ticket_list(request): urlsafe_query = query_to_base64(query_params) - get_query(urlsafe_query, huser) + Query(huser, base64query=urlsafe_query).refresh_query() user_saved_queries = SavedSearch.objects.filter(Q(user=request.user) | Q(shared__exact=True)) @@ -964,55 +961,16 @@ def datatables_ticket_list(request, query): on the table. query_tickets_by_args is at lib.py, DatatablesTicketSerializer is in serializers.py. The serializers and this view use django-rest_framework methods """ - objects = get_query(query, HelpdeskUser(request.user)) - model_object = query_tickets_by_args(objects, '-date_created', **request.query_params) - serializer = DatatablesTicketSerializer(model_object['items'], many=True) - result = dict() - result['data'] = serializer.data - result['draw'] = model_object['draw'] - result['recordsTotal'] = model_object['total'] - result['recordsFiltered'] = model_object['count'] + query = Query(HelpdeskUser(request.user), base64query=query) + result = query.get_datatables_context(**request.query_params) return (JsonResponse(result, status=status.HTTP_200_OK)) @helpdesk_staff_member_required @api_view(['GET']) def timeline_ticket_list(request, query): - """ - Datatable on ticket_list.html uses this view from to get objects to display - on the table. query_tickets_by_args is at lib.py, DatatablesTicketSerializer is in - serializers.py. The serializers and this view use django-rest_framework methods - """ - tickets = get_query(query, HelpdeskUser(request.user)) - events = [] - - def mk_timeline_date(date): - return { - 'year': date.year, - 'month': date.month, - 'day': date.day, - 'hour': date.hour, - 'minute': date.minute, - 'second': date.second, - 'second': date.second, - } - for ticket in tickets: - for followup in ticket.followup_set.all(): - event = { - 'start_date': mk_timeline_date(followup.date), - 'text': { - 'headline': ticket.title + '
' + followup.title, - 'text': (followup.comment if followup.comment else _('No text')) + '
%s' % - (reverse('helpdesk:view', kwargs={'ticket_id': ticket.pk}), _("View ticket")), - }, - 'group': _('Messages'), - } - events.append(event) - - result = { - 'events': events, - } - return (JsonResponse(result, status=status.HTTP_200_OK)) + query = Query(HelpdeskUser(request.user), base64query=query) + return (JsonResponse(query.get_timeline_context(), status=status.HTTP_200_OK)) @helpdesk_staff_member_required From ef9c4c589a398fcd50ca9b8d6d0ff4668f0ea021 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 31 Oct 2019 12:43:35 +0100 Subject: [PATCH 12/67] Fix linting errors --- helpdesk/tests/test_per_queue_staff_permission.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpdesk/tests/test_per_queue_staff_permission.py b/helpdesk/tests/test_per_queue_staff_permission.py index 5c036322..cb6f1496 100644 --- a/helpdesk/tests/test_per_queue_staff_permission.py +++ b/helpdesk/tests/test_per_queue_staff_permission.py @@ -166,7 +166,7 @@ class PerQueueStaffMembershipTestCase(TestCase): for identifier in self.IDENTIFIERS: self.client.login(username='User_%d' % identifier, password=str(identifier)) response = self.client.get(reverse('helpdesk:list')) - tickets = __Query__(HelpdeskUser(self.identifier_users[identifier]), base64query = response.context['urlsafe_query']).get() + tickets = __Query__(HelpdeskUser(self.identifier_users[identifier]), base64query=response.context['urlsafe_query']).get() self.assertEqual( len(tickets), identifier * 2, @@ -186,7 +186,7 @@ class PerQueueStaffMembershipTestCase(TestCase): # Superuser self.client.login(username='superuser', password='superuser') response = self.client.get(reverse('helpdesk:list')) - tickets = __Query__(HelpdeskUser(self.superuser), base64query = response.context['urlsafe_query']).get() + tickets = __Query__(HelpdeskUser(self.superuser), base64query=response.context['urlsafe_query']).get() self.assertEqual( len(tickets), 6, From 2fbd4818c1b3cc1773f82fd5d38f2ac811d8f592 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 31 Oct 2019 14:40:12 +0100 Subject: [PATCH 13/67] filtering should not be mandatory --- helpdesk/query.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/helpdesk/query.py b/helpdesk/query.py index 6a56baea..88e19224 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -118,7 +118,7 @@ class __Query__: sorting: The name of the column to sort by """ - for key in self.params['filtering'].keys(): + for key in self.params.get('filtering', {}).keys(): filter = {key: self.params['filtering'][key]} queryset = queryset.filter(**filter) queryset = queryset.filter(self.get_search_filter_args()) @@ -130,15 +130,18 @@ class __Query__: queryset = queryset.order_by(sorting) return queryset + def get_cache_key(self): + return str(self.huser.user.pk) + ":" + self.base64 + def refresh_query(self): tickets = self.huser.get_tickets_in_queues().select_related() ticket_qs = self.__run__(tickets) - cache.set(self.huser.user.email + self.base64, ticket_qs, timeout=3600) + cache.set(self.get_cache_key(), ticket_qs, timeout=3600) return ticket_qs def get(self): # Prefilter the allowed tickets - objects = cache.get(self.huser.user.email + self.base64) + objects = cache.get(self.get_cache_key()) if objects is not None: return objects return self.refresh_query() @@ -189,7 +192,7 @@ class __Query__: event = { 'start_date': self.mk_timeline_date(followup.date), 'text': { - 'headline': ticket.title + '
' + followup.title, + 'headline': ticket.title + ' - ' + followup.title, 'text': (followup.comment if followup.comment else _('No text')) + '
%s' % (reverse('helpdesk:view', kwargs={'ticket_id': ticket.pk}), _("View ticket")), }, From d32d23e4e264f984d52519d38bc6f04ac59094e1 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 1 Nov 2019 17:03:17 +0100 Subject: [PATCH 14/67] Add OR syntax to search --- helpdesk/query.py | 29 +++++++++++-------- .../templates/helpdesk/filters/keywords.html | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/helpdesk/query.py b/helpdesk/query.py index 88e19224..a2fbe16e 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -56,18 +56,23 @@ def get_search_filter_args(search): return Q(queue__title__icontains=search[len('queue:'):]) if search.startswith('priority:'): return Q(priority__icontains=search[len('priority:'):]) - return ( - Q(id__icontains=search) | - Q(title__icontains=search) | - Q(description__icontains=search) | - Q(priority__icontains=search) | - Q(resolution__icontains=search) | - Q(submitter_email__icontains=search) | - Q(assigned_to__email__icontains=search) | - Q(ticketcustomfieldvalue__value__icontains=search) | - Q(created__icontains=search) | - Q(due_date__icontains=search) - ) + filter = Q() + for subsearch in search.split("OR"): + subsearch = subsearch.strip() + filter = ( + filter | + Q(id__icontains=subsearch) | + Q(title__icontains=subsearch) | + Q(description__icontains=subsearch) | + Q(priority__icontains=subsearch) | + Q(resolution__icontains=subsearch) | + Q(submitter_email__icontains=subsearch) | + Q(assigned_to__email__icontains=subsearch) | + Q(ticketcustomfieldvalue__value__icontains=subsearch) | + Q(created__icontains=subsearch) | + Q(due_date__icontains=subsearch) + ) + return filter DATATABLES_ORDER_COLUMN_CHOICES = Choices( diff --git a/helpdesk/templates/helpdesk/filters/keywords.html b/helpdesk/templates/helpdesk/filters/keywords.html index adf37a7e..6a3253fe 100644 --- a/helpdesk/templates/helpdesk/filters/keywords.html +++ b/helpdesk/templates/helpdesk/filters/keywords.html @@ -9,5 +9,5 @@
-

{% trans "Keywords are case-insensitive, and will be looked for pretty much everywhere possible. Prepend with 'queue:' or 'priority:' to search by queue or priority." %}

+

{% trans "Keywords are case-insensitive, and will be looked for pretty much everywhere possible. Prepend with 'queue:' or 'priority:' to search by queue or priority. You can also use the keyword OR to combine multiple searches." %}

From 3b5a7fe49ad70a643d84dfd6711a37995a0582d7 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 6 Dec 2019 15:05:57 +0100 Subject: [PATCH 15/67] Django <2 is no longer supported --- helpdesk/views/public.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 94960e58..f89de0b7 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -7,12 +7,7 @@ views/public.py - All public facing views, eg non-staff (no authentication required) views. """ from django.core.exceptions import ObjectDoesNotExist, PermissionDenied -try: - # Django 2.0+ - from django.urls import reverse -except ImportError: - # Django < 2 - from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render from django.utils.http import urlquote From 24b8e45f6634814f54215852f0cfa5bef6468638 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 6 Dec 2019 15:44:20 +0100 Subject: [PATCH 16/67] Add ability to hide fields in public ticket submission form using kwargs --- helpdesk/forms.py | 34 ++++++++++-------------------- helpdesk/tests/test_attachments.py | 16 -------------- helpdesk/views/public.py | 5 +++++ 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index aec25d10..af7ef438 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -337,34 +337,22 @@ class PublicTicketForm(AbstractTicketForm): help_text=_('We will e-mail you when your ticket is updated.'), ) - def __init__(self, *args, **kwargs): + def __init__(self, hidden_fields=(), *args, **kwargs): """ Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) - if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE'): - del self.fields['queue'] - else: - self.fields['queue'].choices = [ - ('', '--------') - ] + [ - (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True) - ] - if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_PRIORITY'): - self.fields['priority'].widget = forms.HiddenInput() - if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_DUE_DATE'): - self.fields['due_date'].widget = forms.HiddenInput() + field_hide_table = { + 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', + 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', + 'due_date': 'HELPDESK_PUBLIC_TICKET_DUE_DATE', + } + for (field, setting) in field_hide_table.items(): + if hasattr(settings, setting) or field in hidden_fields: + self.fields[field].widget = forms.HiddenInput() - def _get_queue(self): - if getattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE', None): - # force queue to be the pre-defined one - # (only for anon submissions) - return Queue.objects.filter( - slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE - ).first() - else: - # get the queue user entered - return Queue.objects.get(id=int(self.cleaned_data['queue'])) + self.fields['queue'].choices = [('', '--------')] + [ + (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)] def save(self): """ diff --git a/helpdesk/tests/test_attachments.py b/helpdesk/tests/test_attachments.py index 9b71a962..942a2353 100644 --- a/helpdesk/tests/test_attachments.py +++ b/helpdesk/tests/test_attachments.py @@ -113,21 +113,6 @@ class AttachmentUnitTests(TestCase): ) self.assertEqual(filename, self.file_attrs['filename']) -<<<<<<< HEAD - # TODO: FIXME: what's wrong with this test that we get integrity errors? - # @mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True) - # def test_autofill(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save): - # """ check utf-8 data is parsed correctly """ - # self.follow_up.pk = 100 - # self.follow_up.save() - # obj = models.FollowUpAttachment.objects.create( - # followup=self.follow_up, - # file=self.test_file - # ) - # self.assertEqual(obj.filename, self.file_attrs['filename']) - # self.assertEqual(obj.size, len(self.file_attrs['content'])) - # self.assertEqual(obj.mime_type, "text/plain") -======= @mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True) def test_autofill(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save): """ check utf-8 data is parsed correctly """ @@ -138,7 +123,6 @@ class AttachmentUnitTests(TestCase): self.assertEqual(obj.filename, self.file_attrs['filename']) self.assertEqual(obj.size, len(self.file_attrs['content'])) self.assertEqual(obj.mime_type, "text/plain") ->>>>>>> b899c97... Remove hardcoded pk from test suit def test_kbi_attachment(self, mock_att_save, mock_queue_save, mock_ticket_save): """ check utf-8 data is parsed correctly """ diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index f89de0b7..eebf9c41 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -88,6 +88,11 @@ class CreateTicketView(FormView): initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) return initial_data + def get_form_kwargs(self, *args, **kwargs): + kwargs = super().get_form_kwargs(*args, **kwargs) + kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') + return kwargs + def form_valid(self, form): request = self.request if text_is_spam(form.cleaned_data['body'], request): From aadfe08504f1d0a7dba391b8ef876fa1a0335db3 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 12 Dec 2019 15:22:12 +0100 Subject: [PATCH 17/67] Add iframe supporting view for submitting tickets --- helpdesk/templates/helpdesk/base-head.html | 74 +++++++++++++++++++ helpdesk/templates/helpdesk/base.html | 64 +--------------- helpdesk/templates/helpdesk/public_base.html | 38 +--------- .../helpdesk/public_create_ticket.html | 19 ++--- .../helpdesk/public_create_ticket_base.html | 14 ++++ .../helpdesk/public_create_ticket_iframe.html | 11 +++ helpdesk/urls.py | 4 + helpdesk/views/public.py | 11 ++- 8 files changed, 121 insertions(+), 114 deletions(-) create mode 100644 helpdesk/templates/helpdesk/base-head.html create mode 100644 helpdesk/templates/helpdesk/public_create_ticket_base.html create mode 100644 helpdesk/templates/helpdesk/public_create_ticket_iframe.html diff --git a/helpdesk/templates/helpdesk/base-head.html b/helpdesk/templates/helpdesk/base-head.html new file mode 100644 index 00000000..872afdfa --- /dev/null +++ b/helpdesk/templates/helpdesk/base-head.html @@ -0,0 +1,74 @@ +{% load i18n %} +{% load saved_queries %} +{% load load_helpdesk_settings %} +{% load static from staticfiles %} +{% with request|load_helpdesk_settings as helpdesk_settings %} +{% with user|saved_queries as user_saved_queries_ %} + + + + + + + +{% block helpdesk_title %}Helpdesk{% endblock %} :: {% trans "Powered by django-helpdesk" %} + + + +{% if helpdesk_settings.HELPDESK_USE_CDN %} + +{% else %} + +{% endif %} + + + + + + + + + + + +{% if helpdesk_settings.HELPDESK_USE_CDN %} + +{% else %} + +{% endif %} + + + + + +{% if user.id %} + + + + + + + + +{% endif %} +{% endwith %} +{% endwith %} diff --git a/helpdesk/templates/helpdesk/base.html b/helpdesk/templates/helpdesk/base.html index 7342d08f..751ca263 100644 --- a/helpdesk/templates/helpdesk/base.html +++ b/helpdesk/templates/helpdesk/base.html @@ -9,69 +9,7 @@ - - - - - - - {% block helpdesk_title %}Helpdesk{% endblock %} :: {% trans "Powered by django-helpdesk" %} - - - {% if helpdesk_settings.HELPDESK_USE_CDN %} - - {% else %} - - {% endif %} - - - - - - - - - - - - {% if helpdesk_settings.HELPDESK_USE_CDN %} - - {% else %} - - {% endif %} - - - - - - - - - - - - - - + {% include 'helpdesk/base-head.html' %} {% block helpdesk_head %}{% endblock %} diff --git a/helpdesk/templates/helpdesk/public_base.html b/helpdesk/templates/helpdesk/public_base.html index 24a2157f..b0aaa679 100644 --- a/helpdesk/templates/helpdesk/public_base.html +++ b/helpdesk/templates/helpdesk/public_base.html @@ -1,47 +1,13 @@ {% load i18n %} -{% load load_helpdesk_settings %} {% load static from staticfiles %} +{% load load_helpdesk_settings %} {% with request|load_helpdesk_settings as helpdesk_settings %} - - - - - - - {% block helpdesk_title %}{% trans 'Helpdesk' %}{% endblock %} :: {% trans "Powered by django-helpdesk" %} - - - {% if helpdesk_settings.HELPDESK_USE_CDN %} - - {% else %} - - {% endif %} - - - - - - - - - - - - - - - - - - + {% include 'helpdesk/base-head.html' %} {% block helpdesk_head %}{% endblock %} diff --git a/helpdesk/templates/helpdesk/public_create_ticket.html b/helpdesk/templates/helpdesk/public_create_ticket.html index a902e344..fca1d845 100644 --- a/helpdesk/templates/helpdesk/public_create_ticket.html +++ b/helpdesk/templates/helpdesk/public_create_ticket.html @@ -1,5 +1,5 @@ {% extends "helpdesk/public_base.html" %} -{% load i18n bootstrap4form %} +{% load i18n %} {% block helpdesk_title %}{% trans "Create Ticket" %}{% endblock %} @@ -11,20 +11,13 @@ {% endblock %} {% block helpdesk_body %} -{% if helpdesk_settings.HELPDESK_SUBMIT_A_TICKET_PUBLIC %} -
-
+
+
{% trans "Submit a Ticket" %}
-

{% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}

-
- {{ form|bootstrap4form }} - - {% csrf_token %}
+ {% include 'helpdesk/public_create_ticket_base.html' %}
-
-{% else %} -

{% trans "Public ticket submission is disabled. Please contact the administrator for assistance." %}

-{% endif %} +
+ {% endblock %} diff --git a/helpdesk/templates/helpdesk/public_create_ticket_base.html b/helpdesk/templates/helpdesk/public_create_ticket_base.html new file mode 100644 index 00000000..d82fd466 --- /dev/null +++ b/helpdesk/templates/helpdesk/public_create_ticket_base.html @@ -0,0 +1,14 @@ +{% load i18n bootstrap4form %} +{% load load_helpdesk_settings %} +{% with request|load_helpdesk_settings as helpdesk_settings %} + +{% if helpdesk_settings.HELPDESK_SUBMIT_A_TICKET_PUBLIC %} +

{% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}

+
+ {{ form|bootstrap4form }} + + {% csrf_token %}
+{% else %} +

{% trans "Public ticket submission is disabled. Please contact the administrator for assistance." %}

+{% endif %} +{% endwith %} diff --git a/helpdesk/templates/helpdesk/public_create_ticket_iframe.html b/helpdesk/templates/helpdesk/public_create_ticket_iframe.html new file mode 100644 index 00000000..a20f0722 --- /dev/null +++ b/helpdesk/templates/helpdesk/public_create_ticket_iframe.html @@ -0,0 +1,11 @@ +{% load i18n %} +{% load saved_queries %} + + {% include 'helpdesk/base-head.html' %} + + + + {% block helpdesk_body %} + {% include 'helpdesk/public_create_ticket_base.html' %} + {% endblock %} + diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 059b088d..c0d3223f 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -167,6 +167,10 @@ urlpatterns += [ public.create_ticket, name='submit'), + url(r'^tickets/submit_iframe/$', + public.CreateTicketIframeView.as_view(), + name='submit_iframe'), + url(r'^view/$', public.view_ticket, name='public_view'), diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index eebf9c41..1bade147 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -31,8 +31,7 @@ def create_ticket(request, *args, **kwargs): return CreateTicketView.as_view()(request, *args, **kwargs) -class CreateTicketView(FormView): - template_name = 'helpdesk/public_create_ticket.html' +class BaseCreateTicketView(FormView): form_class = PublicTicketForm def dispatch(self, *args, **kwargs): @@ -115,6 +114,14 @@ class CreateTicketView(FormView): request = self.request +class CreateTicketIframeView(BaseCreateTicketView): + template_name = 'helpdesk/public_create_ticket_iframe.html' + + +class CreateTicketView(BaseCreateTicketView): + template_name = 'helpdesk/public_create_ticket.html' + + class Homepage(CreateTicketView): template_name = 'helpdesk/public_homepage.html' From 810184298f30740f43b9b774c50f08d5d3a514de Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 12 Dec 2019 16:41:55 +0100 Subject: [PATCH 18/67] Include custom fields in public ticket form --- helpdesk/forms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index af7ef438..35d67046 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -342,6 +342,8 @@ class PublicTicketForm(AbstractTicketForm): Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) + self._add_form_custom_fields(False) + field_hide_table = { 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', From fbae21828117ea6834c6f17e3727d52aa832e4c2 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 12 Dec 2019 17:17:17 +0100 Subject: [PATCH 19/67] Add ability to autoset custom fields in public ticket form --- helpdesk/views/public.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 1bade147..5dca261a 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -21,7 +21,7 @@ from helpdesk.decorators import protect_view, is_helpdesk_staff import helpdesk.views.staff as staff from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam -from helpdesk.models import Ticket, Queue, UserSettings, KBCategory +from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory def create_ticket(request, *args, **kwargs): @@ -83,6 +83,8 @@ class BaseCreateTicketView(FormView): initial_data['submitter_email'] = request.user.email query_param_fields = ['submitter_email', 'title', 'body'] + custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] + query_param_fields += custom_fields for qpf in query_param_fields: initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) return initial_data From 38cedca5416b2f995677ea9518fb5661fab6dd82 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 12 Dec 2019 17:22:57 +0100 Subject: [PATCH 20/67] Can now hide custom fields using query param in public ticket form --- helpdesk/forms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 35d67046..6d3f5abb 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -349,8 +349,11 @@ class PublicTicketForm(AbstractTicketForm): 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', 'due_date': 'HELPDESK_PUBLIC_TICKET_DUE_DATE', } + for cf in CustomField.objects.filter(staff_only=False): + field_hide_table["custom_%s" % cf.name] = None + for (field, setting) in field_hide_table.items(): - if hasattr(settings, setting) or field in hidden_fields: + if (setting and hasattr(settings, setting)) or field in hidden_fields: self.fields[field].widget = forms.HiddenInput() self.fields['queue'].choices = [('', '--------')] + [ From 133075f0456cbd2e612f9b5626dc3564a4be4c93 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 16 Dec 2019 15:03:07 +0100 Subject: [PATCH 21/67] Fix #698 --- helpdesk/templates/helpdesk/ticket_cc_add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/templates/helpdesk/ticket_cc_add.html b/helpdesk/templates/helpdesk/ticket_cc_add.html index 0615b31d..07ee9f0f 100644 --- a/helpdesk/templates/helpdesk/ticket_cc_add.html +++ b/helpdesk/templates/helpdesk/ticket_cc_add.html @@ -34,7 +34,7 @@
-
+

{% trans 'Add Email' %}

From 8f59bfcc3c693086c29f6d71780dda3426ab27ea Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 6 Jan 2020 14:11:44 +0100 Subject: [PATCH 22/67] Update integration docs with info about iframe --- docs/integration.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/integration.rst b/docs/integration.rst index 59ab0538..159fde81 100644 --- a/docs/integration.rst +++ b/docs/integration.rst @@ -7,5 +7,12 @@ Django-helpdesk associates an email address with each submitted ticket. If you i - `title` - `body` - `submitter_email` + - `custom_` + +There is also a page under the url `/tickets/submit_iframe/` with the same behavior. + +Fields may be hidden by adding them to a comma separated `_hide_fieds_` query parameter. + +Here is an example url to get you started: `http://localhost:8000/desk/tickets/submit_iframe/?queue=1;custom_dpnk-user=http://lol.cz;submitter_email=foo@bar.cz;title=lol;_hide_fields_=title,queue,submitter_email`. This url sets the queue to 1, sets the custom field `dpnk-url` to `http://lol.cz` and submitter_email to `lol@baz.cz` and hides the title, queue, and submitter_email fields. Note that hidden fields should be set to a default. Note that these fields will continue to be user-editable despite being pre-filled. From 606098d9475c488fdd1fc878ae5c901a6af43cb5 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 6 Jan 2020 14:13:31 +0100 Subject: [PATCH 23/67] Fix field hiding and setting code for ticket submition qargs --- helpdesk/forms.py | 5 ++--- helpdesk/views/public.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 6d3f5abb..01a1187c 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -349,10 +349,9 @@ class PublicTicketForm(AbstractTicketForm): 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', 'due_date': 'HELPDESK_PUBLIC_TICKET_DUE_DATE', } - for cf in CustomField.objects.filter(staff_only=False): - field_hide_table["custom_%s" % cf.name] = None - for (field, setting) in field_hide_table.items(): + for field in self.fields.keys(): + setting = field_hide_table.get(field, None) if (setting and hasattr(settings, setting)) or field in hidden_fields: self.fields[field].widget = forms.HiddenInput() diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 5dca261a..d058d75b 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -82,7 +82,7 @@ class BaseCreateTicketView(FormView): if request.user.is_authenticated and request.user.email: initial_data['submitter_email'] = request.user.email - query_param_fields = ['submitter_email', 'title', 'body'] + query_param_fields = ['submitter_email', 'title', 'body', 'queue'] custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] query_param_fields += custom_fields for qpf in query_param_fields: From 5f29bb632ee2a2b91fe40d0614b066c0855c819b Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 6 Jan 2020 14:25:37 +0100 Subject: [PATCH 24/67] Associate queues with KB categories --- helpdesk/forms.py | 4 +++- helpdesk/models.py | 9 +++++++++ helpdesk/templates/helpdesk/kb_category.html | 4 ++++ helpdesk/views/public.py | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 01a1187c..a635a75b 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -337,7 +337,7 @@ class PublicTicketForm(AbstractTicketForm): help_text=_('We will e-mail you when your ticket is updated.'), ) - def __init__(self, hidden_fields=(), *args, **kwargs): + def __init__(self, hidden_fields=(), readonly_fields=(), *args, **kwargs): """ Add any (non-staff) custom fields that are defined to the form """ @@ -354,6 +354,8 @@ class PublicTicketForm(AbstractTicketForm): setting = field_hide_table.get(field, None) if (setting and hasattr(settings, setting)) or field in hidden_fields: self.fields[field].widget = forms.HiddenInput() + if field in readonly_fields: + self.fields[field].disabled = True self.fields['queue'].choices = [('', '--------')] + [ (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)] diff --git a/helpdesk/models.py b/helpdesk/models.py index c724a0c3..ec17b3ba 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -1216,6 +1216,15 @@ class KBCategory(models.Model): _('Description'), ) + queue = models.ForeignKey( + Queue, + blank=True, + null=True, + on_delete=models.CASCADE, + verbose_name=_('Default queue when creating a ticket after viewing this category.'), + ) + + def __str__(self): return '%s' % self.title diff --git a/helpdesk/templates/helpdesk/kb_category.html b/helpdesk/templates/helpdesk/kb_category.html index c34ea65c..f7a6a3e1 100644 --- a/helpdesk/templates/helpdesk/kb_category.html +++ b/helpdesk/templates/helpdesk/kb_category.html @@ -41,5 +41,9 @@
{% ifequal itemnumperrow 'three' %}
{% endifequal %} {% endfor %} +{% ifnotequal itemnumperrow 'three' %}
{% endifnotequal %} +{% if category.queue %} + +{% endif %} {% endblock %} diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index d058d75b..94820d13 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -92,6 +92,7 @@ class BaseCreateTicketView(FormView): def get_form_kwargs(self, *args, **kwargs): kwargs = super().get_form_kwargs(*args, **kwargs) kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') + kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',') return kwargs def form_valid(self, form): From c95b24780eade56be294723743d8e4df84417d82 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Tue, 7 Jan 2020 13:26:02 +0100 Subject: [PATCH 25/67] Fix voting for logged in users. Voting still broken for anon users --- .../migrations/0027_auto_20200107_1221.py | 71 +++++++++++++++++++ helpdesk/models.py | 9 ++- helpdesk/views/kb.py | 23 ++++-- 3 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 helpdesk/migrations/0027_auto_20200107_1221.py diff --git a/helpdesk/migrations/0027_auto_20200107_1221.py b/helpdesk/migrations/0027_auto_20200107_1221.py new file mode 100644 index 00000000..dbd50149 --- /dev/null +++ b/helpdesk/migrations/0027_auto_20200107_1221.py @@ -0,0 +1,71 @@ +# Generated by Django 2.2.6 on 2020-01-07 12:21 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('helpdesk', '0026_kbitem_attachments'), + ] + + operations = [ + migrations.AddField( + model_name='kbcategory', + name='queue', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='helpdesk.Queue', verbose_name='Default queue when creating a ticket after viewing this category.'), + ), + migrations.AddField( + model_name='kbitem', + name='downvoted_by', + field=models.ManyToManyField(related_name='downvotes', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='ticket', + name='kbitem', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='helpdesk.KBItem', verbose_name='Knowledge base item the user was viewing when they created this ticket.'), + ), + migrations.AlterField( + model_name='followupattachment', + name='filename', + field=models.CharField(blank=True, max_length=1000, verbose_name='Filename'), + ), + migrations.AlterField( + model_name='followupattachment', + name='mime_type', + field=models.CharField(blank=True, max_length=255, verbose_name='MIME Type'), + ), + migrations.AlterField( + model_name='followupattachment', + name='size', + field=models.IntegerField(blank=True, help_text='Size of this file in bytes', verbose_name='Size'), + ), + migrations.AlterField( + model_name='kbiattachment', + name='filename', + field=models.CharField(blank=True, max_length=1000, verbose_name='Filename'), + ), + migrations.AlterField( + model_name='kbiattachment', + name='mime_type', + field=models.CharField(blank=True, max_length=255, verbose_name='MIME Type'), + ), + migrations.AlterField( + model_name='kbiattachment', + name='size', + field=models.IntegerField(blank=True, help_text='Size of this file in bytes', verbose_name='Size'), + ), + migrations.AlterField( + model_name='kbitem', + name='voted_by', + field=models.ManyToManyField(related_name='votes', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='queue', + name='enable_notifications_on_email_events', + field=models.BooleanField(blank=True, default=False, help_text='When an email arrives to either create a ticket or to interact with an existing discussion. Should email notifications be sent ? Note: the new_ticket_cc and updated_ticket_cc work independently of this feature', verbose_name='Notify contacts when email updates arrive'), + ), + ] diff --git a/helpdesk/models.py b/helpdesk/models.py index ec17b3ba..8a200e27 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -1243,7 +1243,14 @@ class KBItem(models.Model): An item within the knowledgebase. Very straightforward question/answer style system. """ - voted_by = models.ManyToManyField(settings.AUTH_USER_MODEL) + voted_by = models.ManyToManyField( + settings.AUTH_USER_MODEL, + related_name='votes', + ) + downvoted_by = models.ManyToManyField( + settings.AUTH_USER_MODEL, + related_name='downvotes', + ) category = models.ForeignKey( KBCategory, on_delete=models.CASCADE, diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index 65283c6e..b095ae3f 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -46,12 +46,21 @@ def item(request, item): def vote(request, item): item = get_object_or_404(KBItem, pk=item) vote = request.GET.get('vote', None) - if vote in ('up', 'down'): - if request.user not in item.voted_by: - + if vote == 'up': + if not item.voted_by.filter(pk=request.user.pk): item.votes += 1 - if vote == 'up': - item.recommendations += 1 - item.save() - + item.voted_by.add(request.user.pk) + item.recommendations += 1 + if item.downvoted_by.filter(pk=request.user.pk): + item.votes -= 1 + item.downvoted_by.remove(request.user.pk) + if vote == 'down': + if not item.downvoted_by.filter(pk=request.user.pk): + item.votes += 1 + item.downvoted_by.add(request.user.pk) + item.recommendations -= 1 + if item.voted_by.filter(pk=request.user.pk): + item.votes -= 1 + item.voted_by.remove(request.user.pk) + item.save() return HttpResponseRedirect(item.get_absolute_url()) From 7fe6444f8fc6c0e48f6ca777b35f048d8a32d789 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Tue, 7 Jan 2020 13:33:06 +0100 Subject: [PATCH 26/67] Redo KB categories as accordion view --- helpdesk/forms.py | 19 ++++++-- helpdesk/models.py | 10 +++- helpdesk/templates/helpdesk/kb_category.html | 44 ++++++++++------- helpdesk/templates/helpdesk/kb_item.html | 51 -------------------- helpdesk/urls.py | 4 -- helpdesk/views/kb.py | 15 +++--- helpdesk/views/public.py | 10 +++- 7 files changed, 65 insertions(+), 88 deletions(-) delete mode 100644 helpdesk/templates/helpdesk/kb_item.html diff --git a/helpdesk/forms.py b/helpdesk/forms.py index a635a75b..ff76d435 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -18,7 +18,7 @@ from django.utils import timezone from helpdesk.lib import safe_template_context, process_attachments from helpdesk.models import (Ticket, Queue, FollowUp, IgnoreEmail, TicketCC, - CustomField, TicketCustomFieldValue, TicketDependency, UserSettings) + CustomField, TicketCustomFieldValue, TicketDependency, UserSettings, KBItem) from helpdesk import settings as helpdesk_settings User = get_user_model() @@ -197,7 +197,10 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): return Queue.objects.get(id=int(self.cleaned_data['queue'])) def _create_ticket(self): - queue = self._get_queue() + queue = Queue.objects.get(id=int(self.cleaned_data['queue'])) + kbitem = None + if 'kbitem' in self.cleaned_data: + kbitem = KBItem.objects.get(id=int(self.cleaned_data['kbitem'])) ticket = Ticket(title=self.cleaned_data['title'], submitter_email=self.cleaned_data['submitter_email'], @@ -207,6 +210,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): description=self.cleaned_data['body'], priority=self.cleaned_data['priority'], due_date=self.cleaned_data['due_date'], + kbitem=kbitem, ) return ticket, queue @@ -337,13 +341,22 @@ class PublicTicketForm(AbstractTicketForm): help_text=_('We will e-mail you when your ticket is updated.'), ) - def __init__(self, hidden_fields=(), readonly_fields=(), *args, **kwargs): + def __init__(self, hidden_fields=(), readonly_fields=(), kbcategory=None, *args, **kwargs): """ Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) self._add_form_custom_fields(False) + if kbcategory: + self.fields['kbitem'] = forms.ChoiceField( + widget=forms.Select(attrs={'class': 'form-control'}), + required=False, + label=_('Knowedge Base Item'), + choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], + ) + + field_hide_table = { 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', diff --git a/helpdesk/models.py b/helpdesk/models.py index 8a200e27..09d2436b 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -559,6 +559,14 @@ class Ticket(models.Model): default=mk_secret, ) + kbitem = models.ForeignKey( + "KBItem", + blank=True, + null=True, + on_delete=models.CASCADE, + verbose_name=_('Knowledge base item the user was viewing when they created this ticket.'), + ) + @property def time_spent(self): """Return back total time spent on the ticket. This is calculated value @@ -1310,7 +1318,7 @@ class KBItem(models.Model): def get_absolute_url(self): from django.urls import reverse - return reverse('helpdesk:kb_item', args=(self.id,)) + return str(reverse('helpdesk:kb_category', args=(self.category.slug,)))+"?kbitem="+str(self.pk) def get_markdown(self): return get_markdown(self.answer) diff --git a/helpdesk/templates/helpdesk/kb_category.html b/helpdesk/templates/helpdesk/kb_category.html index f7a6a3e1..953fe183 100644 --- a/helpdesk/templates/helpdesk/kb_category.html +++ b/helpdesk/templates/helpdesk/kb_category.html @@ -19,29 +19,37 @@

{{ category.description }}

- +
{% for item in items %} -{% cycle 'one' 'two' 'three' as itemnumperrow silent %} -{% ifequal itemnumperrow 'one' %}
{% endifequal %} -
-
-
{{ item.title }}
-
-
-

{{ item.question }}

-

- {% blocktrans with item.get_absolute_url as url %} Go to answer {% endblocktrans %} -

-
-

{% trans 'Rating' %}: {{ item.score }}

-

{% trans 'Last Update' %}: {{ item.last_updated|naturaltime }}

+
+ +
+
+

{{ item.question }}

+

{{ item.get_markdown }}

+
+ {% if request.user.pk %} +
+ +
+
+ +
+ {% blocktrans with recommendations=item.recommendations votes=item.votes %}{{ recommendations }} people found this answer useful of {{votes}}. {% endblocktrans %} + {% endif %}
-

+ +
-{% ifequal itemnumperrow 'three' %}
{% endifequal %} {% endfor %} -{% ifnotequal itemnumperrow 'three' %}
{% endifnotequal %} +
{% if category.queue %} {% endif %} diff --git a/helpdesk/templates/helpdesk/kb_item.html b/helpdesk/templates/helpdesk/kb_item.html deleted file mode 100644 index c75c0777..00000000 --- a/helpdesk/templates/helpdesk/kb_item.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "helpdesk/public_base.html" %}{% load i18n %} - -{% block helpdesk_breadcrumb %} - - - -{% endblock %} - -{% block helpdesk_body %} -

{% trans 'Knowledgebase' %}: {% blocktrans with item.title as item %}{{ item }}{% endblocktrans %}

- -
-
- - {{ item.question }} -
-
-

{{ item.get_markdown }}

-
- -
- -

{% blocktrans with item.category.title as category_title and item.category.get_absolute_url as category_url %}View other {{ category_title }} articles, or continue viewing other knowledgebase articles.{% endblocktrans %}

- -{% endblock %} diff --git a/helpdesk/urls.py b/helpdesk/urls.py index c0d3223f..e183efe1 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -232,10 +232,6 @@ if helpdesk_settings.HELPDESK_KB_ENABLED: kb.index, name='kb_index'), - url(r'^kb/(?P[0-9]+)/$', - kb.item, - name='kb_item'), - url(r'^kb/(?P[0-9]+)/vote/$', kb.vote, name='kb_vote'), diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index b095ae3f..78666767 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -27,18 +27,15 @@ def index(request): def category(request, slug): category = get_object_or_404(KBCategory, slug__iexact=slug) items = category.kbitem_set.all() + selected_item = request.GET.get('kbitem', None) + try: + selected_item = int(selected_item) + except ValueError: + pass return render(request, 'helpdesk/kb_category.html', { 'category': category, 'items': items, - 'helpdesk_settings': helpdesk_settings, - }) - - -def item(request, item): - item = get_object_or_404(KBItem, pk=item) - return render(request, 'helpdesk/kb_item.html', { - 'category': item.category, - 'item': item, + 'selected_item': selected_item, 'helpdesk_settings': helpdesk_settings, }) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 94820d13..2ffcdc38 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -21,7 +21,7 @@ from helpdesk.decorators import protect_view, is_helpdesk_staff import helpdesk.views.staff as staff from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam -from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory +from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem def create_ticket(request, *args, **kwargs): @@ -82,7 +82,7 @@ class BaseCreateTicketView(FormView): if request.user.is_authenticated and request.user.email: initial_data['submitter_email'] = request.user.email - query_param_fields = ['submitter_email', 'title', 'body', 'queue'] + query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem'] custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] query_param_fields += custom_fields for qpf in query_param_fields: @@ -93,6 +93,12 @@ class BaseCreateTicketView(FormView): kwargs = super().get_form_kwargs(*args, **kwargs) kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',') + kbitem = self.request.GET.get('kbitem', None) + if kbitem: + try: + kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem)) + except (ValueError, KBItem.DoesNotExist): + pass return kwargs def form_valid(self, form): From 6579ac0e6fad994c1d264fa56a6a0660925c5276 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Wed, 8 Jan 2020 18:39:41 +0100 Subject: [PATCH 27/67] Associate tickets with KB items --- helpdesk/forms.py | 29 +++++++------- helpdesk/models.py | 5 +++ .../templates/helpdesk/filters/kbitems.html | 15 +++++++ helpdesk/templates/helpdesk/kb_category.html | 3 +- .../templates/helpdesk/ticket_desc_table.html | 6 +++ helpdesk/templates/helpdesk/ticket_list.html | 13 +++++-- helpdesk/views/abstract_views.py | 37 ++++++++++++++++++ helpdesk/views/kb.py | 8 +++- helpdesk/views/public.py | 39 +++++-------------- helpdesk/views/staff.py | 24 ++++++------ 10 files changed, 118 insertions(+), 61 deletions(-) create mode 100644 helpdesk/templates/helpdesk/filters/kbitems.html create mode 100644 helpdesk/views/abstract_views.py diff --git a/helpdesk/forms.py b/helpdesk/forms.py index ff76d435..55b004e9 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -177,6 +177,17 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): help_text=_('You can attach a file such as a document or screenshot to this ticket.'), ) + def __init__(self, kbcategory=None, *args, **kwargs): + super().__init__(*args, **kwargs) + if kbcategory: + self.fields['kbitem'] = forms.ChoiceField( + widget=forms.Select(attrs={'class': 'form-control'}), + required=False, + label=_('Knowedge Base Item'), + choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], + ) + + def _add_form_custom_fields(self, staff_only_filter=None): if staff_only_filter is None: queryset = CustomField.objects.all() @@ -184,11 +195,8 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): queryset = CustomField.objects.filter(staff_only=staff_only_filter) for field in queryset: - instanceargs = { - 'label': field.label, - 'help_text': field.help_text, - 'required': field.required, - } + instanceargs = { 'label': field.label, 'help_text': + field.help_text, 'required': field.required, } self.customfield_to_field(field, instanceargs) @@ -341,22 +349,13 @@ class PublicTicketForm(AbstractTicketForm): help_text=_('We will e-mail you when your ticket is updated.'), ) - def __init__(self, hidden_fields=(), readonly_fields=(), kbcategory=None, *args, **kwargs): + def __init__(self, hidden_fields=(), readonly_fields=(), *args, **kwargs): """ Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) self._add_form_custom_fields(False) - if kbcategory: - self.fields['kbitem'] = forms.ChoiceField( - widget=forms.Select(attrs={'class': 'form-control'}), - required=False, - label=_('Knowedge Base Item'), - choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], - ) - - field_hide_table = { 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', diff --git a/helpdesk/models.py b/helpdesk/models.py index 09d2436b..1ab47c74 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -1320,6 +1320,11 @@ class KBItem(models.Model): from django.urls import reverse return str(reverse('helpdesk:kb_category', args=(self.category.slug,)))+"?kbitem="+str(self.pk) + def query_url(self): + from django.urls import reverse + return str(reverse('helpdesk:list')) +"?kbitem="+str(self.pk) + + def get_markdown(self): return get_markdown(self.answer) diff --git a/helpdesk/templates/helpdesk/filters/kbitems.html b/helpdesk/templates/helpdesk/filters/kbitems.html new file mode 100644 index 00000000..fa79dd33 --- /dev/null +++ b/helpdesk/templates/helpdesk/filters/kbitems.html @@ -0,0 +1,15 @@ +{% load i18n humanize %} +{% load static from staticfiles %} +{% load in_list %} +
+
+ +
+
+ +
+
+ +
+
{% trans "Ctrl-click to select multiple options" %}
+
diff --git a/helpdesk/templates/helpdesk/kb_category.html b/helpdesk/templates/helpdesk/kb_category.html index 953fe183..38ddd413 100644 --- a/helpdesk/templates/helpdesk/kb_category.html +++ b/helpdesk/templates/helpdesk/kb_category.html @@ -43,6 +43,7 @@
{% blocktrans with recommendations=item.recommendations votes=item.votes %}{{ recommendations }} people found this answer useful of {{votes}}. {% endblocktrans %} {% endif %} +
@@ -51,7 +52,7 @@ {% endfor %} {% if category.queue %} - + {% endif %} {% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 52b3000e..4a3dbfe5 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -68,6 +68,12 @@ {% trans "Total time spent" %} {{ ticket.time_spent_formated }} + {% if ticket.kbitem %} + + {% trans "Knowlegebase item" %} + {{ticket.kbitem.title}} + + {% endif %} {% trans "Attachments" %} diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 3e56eb7c..03be65e2 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -171,6 +171,7 @@ + {% csrf_token %} @@ -196,6 +197,9 @@
  • {% include './filters/keywords.html' %}
  • +
  • + {% include './filters/kbitems.html' %} +
  • @@ -318,8 +322,8 @@ var name = data.split(" ")[1]; if (type === 'display') { - data = '
    ' + - row.id + '. ' + + data = ''; } return data @@ -348,7 +352,7 @@ "render": function(data, type, row, meta) { if (data != "None") { return data; - } + } else { return ""; } @@ -396,6 +400,9 @@ {% if query_params.search_string %} $("#filterBuilderSelect-Keywords")[0].disabled = "disabled"; {% endif %} + {% if query_params.filtering.kbitem__in %} + $("#filterBuilderSelect-KBItems")[0].disabled = "disabled"; + {% endif %} }); {% for f in query_params.filtering %} diff --git a/helpdesk/views/abstract_views.py b/helpdesk/views/abstract_views.py new file mode 100644 index 00000000..648996b3 --- /dev/null +++ b/helpdesk/views/abstract_views.py @@ -0,0 +1,37 @@ +from django.views.generic.edit import FormView + +from helpdesk.models import CustomField, KBItem, Queue + + +class AbstractCreateTicketMixin(): + def get_initial(self): + initial_data = {} + request = self.request + try: + initial_data['queue'] = Queue.objects.get(slug=request.GET.get('queue', None)).id + except Queue.DoesNotExist: + pass + if request.user.is_authenticated and request.user.usersettings_helpdesk.use_email_as_submitter and request.user.email: + initial_data['submitter_email'] = request.user.email + + + query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem'] + custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] + query_param_fields += custom_fields + for qpf in query_param_fields: + initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) + + return initial_data + + def get_form_kwargs(self, *args, **kwargs): + kwargs = super().get_form_kwargs(*args, **kwargs) + kbitem = self.request.GET.get( + 'kbitem', + self.request.POST.get('kbitem', None), + ) + if kbitem: + try: + kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem)).category + except (ValueError, KBItem.DoesNotExist): + pass + return kwargs diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index 78666767..1b2626d0 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -30,12 +30,18 @@ def category(request, slug): selected_item = request.GET.get('kbitem', None) try: selected_item = int(selected_item) - except ValueError: + except TypeError: + pass + qparams = request.GET.copy() + try: + del qparams['kbitem'] + except KeyError: pass return render(request, 'helpdesk/kb_category.html', { 'category': category, 'items': items, 'selected_item': selected_item, + 'query_param_string': qparams.urlencode(), 'helpdesk_settings': helpdesk_settings, }) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 2ffcdc38..b9f5279b 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -19,6 +19,7 @@ from django.views.generic.edit import FormView from helpdesk import settings as helpdesk_settings from helpdesk.decorators import protect_view, is_helpdesk_staff import helpdesk.views.staff as staff +import helpdesk.views.abstract_views as abstract_views from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem @@ -31,7 +32,7 @@ def create_ticket(request, *args, **kwargs): return CreateTicketView.as_view()(request, *args, **kwargs) -class BaseCreateTicketView(FormView): +class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView): form_class = PublicTicketForm def dispatch(self, *args, **kwargs): @@ -51,54 +52,27 @@ class BaseCreateTicketView(FormView): return HttpResponseRedirect(reverse('helpdesk:dashboard')) return super().dispatch(*args, **kwargs) - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['kb_categories'] = KBCategory.objects.all() - return context - def get_initial(self): request = self.request - initial_data = {} - try: - queue = Queue.objects.get(slug=request.GET.get('queue', None)) - except Queue.DoesNotExist: - queue = None + initial_data = super().get_initial() # add pre-defined data for public ticket if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE'): # get the requested queue; return an error if queue not found try: - queue = Queue.objects.get(slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE) + initial_data['queue'] = Queue.objects.get(slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE).id except Queue.DoesNotExist: return HttpResponse(status=500) if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_PRIORITY'): initial_data['priority'] = settings.HELPDESK_PUBLIC_TICKET_PRIORITY if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_DUE_DATE'): initial_data['due_date'] = settings.HELPDESK_PUBLIC_TICKET_DUE_DATE - - if queue: - initial_data['queue'] = queue.id - - if request.user.is_authenticated and request.user.email: - initial_data['submitter_email'] = request.user.email - - query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem'] - custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] - query_param_fields += custom_fields - for qpf in query_param_fields: - initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) return initial_data def get_form_kwargs(self, *args, **kwargs): kwargs = super().get_form_kwargs(*args, **kwargs) kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',') - kbitem = self.request.GET.get('kbitem', None) - if kbitem: - try: - kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem)) - except (ValueError, KBItem.DoesNotExist): - pass return kwargs def form_valid(self, form): @@ -134,6 +108,11 @@ class CreateTicketView(BaseCreateTicketView): class Homepage(CreateTicketView): template_name = 'helpdesk/public_homepage.html' + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['kb_categories'] = KBCategory.objects.all() + return context + def search_for_ticket(request, error_message=None): if hasattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC') and settings.HELPDESK_VIEW_A_TICKET_PUBLIC: diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index c374b51b..0ea2f481 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -52,9 +52,10 @@ from helpdesk.lib import ( ) from helpdesk.models import ( Ticket, Queue, FollowUp, TicketChange, PreSetReply, FollowUpAttachment, SavedSearch, - IgnoreEmail, TicketCC, TicketDependency, UserSettings, + IgnoreEmail, TicketCC, TicketDependency, UserSettings, KBItem, ) from helpdesk import settings as helpdesk_settings +import helpdesk.views.abstract_views as abstract_views from helpdesk.views.permissions import MustBeStaffMixin from ..lib import format_time_spent @@ -802,7 +803,9 @@ def ticket_list(request): 'search_string': '', } default_query_params = { - 'filtering': {'status__in': [1, 2, 3]}, + 'filtering': { + 'status__in': [1, 2, 3], + }, 'sorting': 'created', 'search_string': '', 'sortreverse': False, @@ -849,7 +852,7 @@ def ticket_list(request): if saved_query: pass - elif not {'queue', 'assigned_to', 'status', 'q', 'sort', 'sortreverse'}.intersection(request.GET): + elif not {'queue', 'assigned_to', 'status', 'q', 'sort', 'sortreverse', 'kbitem'}.intersection(request.GET): # Fall-back if no querying is being done all_queues = Queue.objects.all() query_params = deepcopy(default_query_params) @@ -858,6 +861,7 @@ def ticket_list(request): ('queue', 'queue__id__in'), ('assigned_to', 'assigned_to__id__in'), ('status', 'status__in'), + ('kbitem', 'kbitem__in'), ] for param, filter_command in filter_in_params: @@ -884,7 +888,7 @@ def ticket_list(request): # SORTING sort = request.GET.get('sort', None) - if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'): + if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority', 'kbitem'): sort = 'created' query_params['sorting'] = sort @@ -907,12 +911,15 @@ def ticket_list(request): '' 'Django Documentation on string matching in SQLite.') + kbitem_choices = [(item.pk, item.title) for item in KBItem.objects.all()] + return render(request, 'helpdesk/ticket_list.html', dict( context, default_tickets_per_page=request.user.usersettings_helpdesk.tickets_per_page, user_choices=User.objects.filter(is_active=True, is_staff=True), queue_choices=huser.get_queues(), status_choices=Ticket.STATUS_CHOICES, + kbitem_choices=kbitem_choices, urlsafe_query=urlsafe_query, user_saved_queries=user_saved_queries, query_params=query_params, @@ -992,17 +999,12 @@ def edit_ticket(request, ticket_id): edit_ticket = staff_member_required(edit_ticket) -class CreateTicketView(MustBeStaffMixin, FormView): +class CreateTicketView(MustBeStaffMixin, abstract_views.AbstractCreateTicketMixin, FormView): template_name = 'helpdesk/create_ticket.html' form_class = TicketForm def get_initial(self): - initial_data = {} - request = self.request - if request.user.usersettings_helpdesk.use_email_as_submitter and request.user.email: - initial_data['submitter_email'] = request.user.email - if 'queue' in request.GET: - initial_data['queue'] = request.GET['queue'] + initial_data = super().get_initial() return initial_data def get_form_kwargs(self): From 6a8ebd56a015bdf70ec6956449186b89bcf7799c Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Wed, 8 Jan 2020 19:36:48 +0100 Subject: [PATCH 28/67] Add iframe view of KB categories --- helpdesk/templates/helpdesk/base.html | 32 ++---------- helpdesk/templates/helpdesk/base_js.html | 27 ++++++++++ helpdesk/templates/helpdesk/kb_category.html | 51 ++----------------- .../templates/helpdesk/kb_category_base.html | 38 ++++++++++++++ .../helpdesk/kb_category_iframe.html | 13 +++++ helpdesk/templates/helpdesk/public_base.html | 43 +++------------- helpdesk/urls.py | 4 ++ helpdesk/views/kb.py | 12 ++++- 8 files changed, 108 insertions(+), 112 deletions(-) create mode 100644 helpdesk/templates/helpdesk/base_js.html create mode 100644 helpdesk/templates/helpdesk/kb_category_base.html create mode 100644 helpdesk/templates/helpdesk/kb_category_iframe.html diff --git a/helpdesk/templates/helpdesk/base.html b/helpdesk/templates/helpdesk/base.html index 751ca263..2e7ed15d 100644 --- a/helpdesk/templates/helpdesk/base.html +++ b/helpdesk/templates/helpdesk/base.html @@ -17,10 +17,10 @@ {% include "helpdesk/navigation-header.html" %} - +
    {% include "helpdesk/navigation-sidebar.html" %} - +
    @@ -43,35 +43,9 @@ {% include "helpdesk/debug.html" %} - - - - {% if helpdesk_settings.HELPDESK_USE_CDN %} - - - {% else %} - - - {% endif %} - - - - - - - - - - - - - - - - - + {% include 'helpdesk/base_js.html' %} {% block helpdesk_js %}{% endblock %} diff --git a/helpdesk/templates/helpdesk/base_js.html b/helpdesk/templates/helpdesk/base_js.html new file mode 100644 index 00000000..4ae7f07c --- /dev/null +++ b/helpdesk/templates/helpdesk/base_js.html @@ -0,0 +1,27 @@ +{% load static from staticfiles %} + +{% if helpdesk_settings.HELPDESK_USE_CDN %} + + +{% else %} + + +{% endif %} + + + + + + + + + + + + + + + + + + diff --git a/helpdesk/templates/helpdesk/kb_category.html b/helpdesk/templates/helpdesk/kb_category.html index 38ddd413..f71f108d 100644 --- a/helpdesk/templates/helpdesk/kb_category.html +++ b/helpdesk/templates/helpdesk/kb_category.html @@ -1,4 +1,7 @@ -{% extends "helpdesk/public_base.html" %}{% load i18n humanize %} +{% extends "helpdesk/public_base.html" %} +{% load i18n %} + +{% block helpdesk_title %}{% blocktrans with category.title as kbcat %}{{ kbcat }}{% endblocktrans %}{% endblock %} {% block helpdesk_breadcrumb %}
    {% if category.queue %} - + + + {% endif %} diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index 78943850..ee164ea9 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -40,6 +40,7 @@ def category(request, slug, iframe=False): template = 'helpdesk/kb_category.html' if iframe: template = 'helpdesk/kb_category_iframe.html' + staff = request.user.is_authenticated and request.user.is_staff return render(request, template, { 'category': category, 'items': items, @@ -47,6 +48,7 @@ def category(request, slug, iframe=False): 'query_param_string': qparams.urlencode(), 'helpdesk_settings': helpdesk_settings, 'iframe': iframe, + 'staff': staff, }) From 0b50b14449e07d827b46b8c801de6d80d2355c26 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 9 Jan 2020 17:35:05 +0100 Subject: [PATCH 32/67] Add kb tests --- helpdesk/tests/test_kb.py | 83 ++++++++++++++++++++++++ helpdesk/tests/test_ticket_submission.py | 24 ++++++- helpdesk/urls.py | 8 +-- 3 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 helpdesk/tests/test_kb.py diff --git a/helpdesk/tests/test_kb.py b/helpdesk/tests/test_kb.py new file mode 100644 index 00000000..6eba76bd --- /dev/null +++ b/helpdesk/tests/test_kb.py @@ -0,0 +1,83 @@ +#queue_publii -*- coding: utf-8 -*- +from django.urls import reverse +from django.test import TestCase + +from helpdesk.models import KBCategory, KBItem, Queue, Ticket + +from helpdesk.tests.helpers import (get_staff_user, reload_urlconf, User, create_ticket, print_response) + + +class KBTests(TestCase): + def setUp(self): + self.queue = Queue.objects.create( + title="Test queue", + slug="test_queue", + allow_public_submission=True, + ) + self.queue.save() + cat = KBCategory.objects.create( + title="Test Cat", + slug="test_cat", + description="This is a test category", + queue=self.queue, + ) + cat.save() + self.kbitem1 = KBItem.objects.create( + category=cat, + title="KBItem 1", + question="What?", + answer="A KB Item", + ) + self.kbitem1.save() + self.kbitem2 = KBItem.objects.create( + category=cat, + title="KBItem 2", + question="When?", + answer="Now", + ) + self.kbitem2.save() + self.user = get_staff_user() + + def test_kb_index(self): + response = self.client.get(reverse('helpdesk:kb_index')) + self.assertContains(response, 'This is a test category') + + def test_kb_category(self): + response = self.client.get(reverse('helpdesk:kb_category', args=("test_cat", ))) + self.assertContains(response, 'This is a test category') + self.assertContains(response, 'KBItem 1') + self.assertContains(response, 'KBItem 2') + self.assertContains(response, 'Contact a human') + self.client.login(username=self.user.get_username(), password='password') + response = self.client.get(reverse('helpdesk:kb_category', args=("test_cat", ))) + self.assertContains(response, '') + self.assertContains(response, '0 open tickets') + ticket = Ticket.objects.create( + title="Test ticket", + queue=self.queue, + kbitem=self.kbitem1, + ) + ticket.save() + response = self.client.get(reverse('helpdesk:kb_category', args=("test_cat",))) + self.assertContains(response, '1 open tickets') + + + def test_kb_vote(self): + self.client.login(username=self.user.get_username(), password='password') + response = self.client.get(reverse('helpdesk:kb_vote', args=(self.kbitem1.pk,)) + "?vote=up") + cat_url = reverse('helpdesk:kb_category', args=("test_cat",)) + "?kbitem=1" + self.assertRedirects(response, cat_url) + response = self.client.get(cat_url) + self.assertContains(response, '1 people found this answer useful of 1') + response = self.client.get(reverse('helpdesk:kb_vote', args=(self.kbitem1.pk,)) + "?vote=down") + self.assertRedirects(response, cat_url) + response = self.client.get(cat_url) + self.assertContains(response, '0 people found this answer useful of 1') + + + def test_kb_category_iframe(self): + cat_url = reverse('helpdesk:kb_category', args=("test_cat",)) + "?kbitem=1;submitter_email=foo@bar.cz;title=lol;" + response = self.client.get(cat_url) + # Assert that query params are passed on to ticket submit form + self.assertContains(response, "'/helpdesk/tickets/submit/?queue=1;_readonly_fields_=queue;kbitem=1;submitter_email=foo%40bar.cz&title=lol") + diff --git a/helpdesk/tests/test_ticket_submission.py b/helpdesk/tests/test_ticket_submission.py index 547cb6cc..31fb0b68 100644 --- a/helpdesk/tests/test_ticket_submission.py +++ b/helpdesk/tests/test_ticket_submission.py @@ -2,7 +2,7 @@ import email import uuid -from helpdesk.models import Queue, CustomField, FollowUp, Ticket, TicketCC +from helpdesk.models import Queue, CustomField, FollowUp, Ticket, TicketCC, KBCategory, KBItem from django.test import TestCase from django.core import mail from django.core.exceptions import ObjectDoesNotExist @@ -11,6 +11,7 @@ from django.test.client import Client from django.urls import reverse from helpdesk.email import object_from_message, create_ticket_cc +from helpdesk.tests.helpers import print_response from urllib.parse import urlparse @@ -976,3 +977,24 @@ class EmailInteractionsTestCase(TestCase): # public_update_queue: +1 expected_email_count += 1 + 2 + 1 self.assertEqual(expected_email_count, len(mail.outbox)) + + def test_ticket_field_autofill(self): + cat = KBCategory.objects.create( + title="Test Cat", + slug="test_cat", + description="This is a test category", + queue=self.queue_public, + ) + cat.save() + self.kbitem1 = KBItem.objects.create( + category=cat, + title="KBItem 1", + question="What?", + answer="A KB Item", + ) + self.kbitem1.save() + cat_url = reverse('helpdesk:submit') + "?kbitem=1;submitter_email=foo@bar.cz;title=lol;" + response = self.client.get(cat_url) + self.assertContains(response, '') + self.assertContains(response, '') + self.assertContains(response, '') diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 94bd28c8..59f27bcc 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -232,14 +232,14 @@ if helpdesk_settings.HELPDESK_KB_ENABLED: kb.index, name='kb_index'), - url(r'^kb/(?P[0-9]+)/vote/$', - kb.vote, - name='kb_vote'), - url(r'^kb/(?P[A-Za-z0-9_-]+)/$', kb.category, name='kb_category'), + url(r'^kb/(?P[0-9]+)/vote/$', + kb.vote, + name='kb_vote'), + url(r'^kb_iframe/(?P[A-Za-z0-9_-]+)/$', kb.category_iframe, name='kb_category_iframe'), From a1b9d18557805dd7e721a23328b4340190560cd3 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 10 Jan 2020 15:01:52 +0100 Subject: [PATCH 33/67] Update Czech translation --- helpdesk/locale/cs/LC_MESSAGES/django.mo | Bin 55247 -> 52426 bytes helpdesk/locale/cs/LC_MESSAGES/django.po | 2340 +++++++++++++--------- 2 files changed, 1341 insertions(+), 999 deletions(-) diff --git a/helpdesk/locale/cs/LC_MESSAGES/django.mo b/helpdesk/locale/cs/LC_MESSAGES/django.mo index b939c14e003f3ebe916fb3e4e996551e28ea51eb..328fda4e5c78eca23786bb6b082cafb8bddb05f9 100644 GIT binary patch delta 11182 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 KGc~eauJeB~YC=!| delta 13851 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 diff --git a/helpdesk/locale/cs/LC_MESSAGES/django.po b/helpdesk/locale/cs/LC_MESSAGES/django.po index 267da0d1..177582a3 100644 --- a/helpdesk/locale/cs/LC_MESSAGES/django.po +++ b/helpdesk/locale/cs/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django-helpdesk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-02-04 15:00+0000\n" -"PO-Revision-Date: 2019-02-02 12:47+0000\n" +"POT-Creation-Date: 2020-01-10 14:47+0100\n" +"PO-Revision-Date: 2020-01-10 15:00+0100\n" "Last-Translator: jachym \n" "Language-Team: Czech (http://www.transifex.com/django-helpdesk/django-" "helpdesk/language/cs/)\n" @@ -20,85 +20,126 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" +"X-Generator: Poedit 1.8.11\n" -#: helpdesk/admin.py:29 helpdesk/models.py:415 -#: helpdesk/templates/helpdesk/public_view_ticket.html:19 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:57 +#: third_party/django-helpdesk/helpdesk/admin.py:38 +#: third_party/django-helpdesk/helpdesk/models.py:491 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:19 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:39 msgid "Submitter E-Mail" msgstr "E-mail zadavatele" -#: helpdesk/admin.py:48 helpdesk/models.py:40 helpdesk/models.py:965 -#: helpdesk/models.py:1374 +#: third_party/django-helpdesk/helpdesk/admin.py:68 +#: third_party/django-helpdesk/helpdesk/models.py:84 +#: third_party/django-helpdesk/helpdesk/models.py:1220 +#: third_party/django-helpdesk/helpdesk/models.py:1677 msgid "Slug" msgstr "Slug" -#: helpdesk/forms.py:139 helpdesk/models.py:272 helpdesk/models.py:399 -#: helpdesk/templates/helpdesk/include/tickets.html:18 -#: helpdesk/templates/helpdesk/include/unassigned.html:18 -#: helpdesk/templates/helpdesk/report_index.html:39 -#: helpdesk/templates/helpdesk/rss_list.html:34 -#: helpdesk/templates/helpdesk/ticket_list.html:63 -#: helpdesk/templates/helpdesk/ticket_list.html:82 -#: helpdesk/templates/helpdesk/ticket_list.html:223 -#: helpdesk/views/staff.py:1253 helpdesk/views/staff.py:1259 -#: helpdesk/views/staff.py:1265 helpdesk/views/staff.py:1271 +#: third_party/django-helpdesk/helpdesk/email.py:357 +#, python-format +msgid "E-Mail Received from %(sender_email)s" +msgstr "E-mail obdržen od %(sender_email)s" + +#: third_party/django-helpdesk/helpdesk/email.py:366 +#, python-format +msgid "Ticket Re-Opened by E-Mail Received from %(sender_email)s" +msgstr "Ticket znovu otevřen e-mailem od%(sender_email)s" + +#: third_party/django-helpdesk/helpdesk/email.py:423 +msgid "Comment from e-mail" +msgstr "Komentář z e-mailu" + +#: third_party/django-helpdesk/helpdesk/email.py:429 +msgid "Unknown Sender" +msgstr "Neznámý odesílatel" + +#: third_party/django-helpdesk/helpdesk/email.py:498 +msgid "email_html_body.html" +msgstr "email_html_body.html" + +#: third_party/django-helpdesk/helpdesk/forms.py:138 +#: third_party/django-helpdesk/helpdesk/models.py:330 +#: third_party/django-helpdesk/helpdesk/models.py:475 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:44 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:45 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:64 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:170 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1211 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1217 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1223 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1229 msgid "Queue" msgstr "Fronta požadavků" -#: helpdesk/forms.py:148 +#: third_party/django-helpdesk/helpdesk/forms.py:147 msgid "Summary of the problem" msgstr "Shrnutí problému" -#: helpdesk/forms.py:153 +#: third_party/django-helpdesk/helpdesk/forms.py:152 msgid "Description of your issue" msgstr "Popis problému" -#: helpdesk/forms.py:155 +#: third_party/django-helpdesk/helpdesk/forms.py:154 msgid "Please be as descriptive as possible and include all details" msgstr "" "Prosíme, popište problém co nejdetailněji, abychom jej byli schopni vyřešit." -#: helpdesk/forms.py:163 helpdesk/management/commands/escalate_tickets.py:156 -#: helpdesk/models.py:459 -#: helpdesk/templates/helpdesk/public_view_ticket.html:24 -#: helpdesk/templates/helpdesk/ticket.html:215 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:62 -#: helpdesk/templates/helpdesk/ticket_list.html:88 helpdesk/views/staff.py:548 +#: third_party/django-helpdesk/helpdesk/forms.py:162 +#: third_party/django-helpdesk/helpdesk/management/commands/escalate_tickets.py:131 +#: third_party/django-helpdesk/helpdesk/models.py:535 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:22 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:24 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:178 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:49 +#: third_party/django-helpdesk/helpdesk/views/staff.py:593 msgid "Priority" msgstr "Priorita" -#: helpdesk/forms.py:164 +#: third_party/django-helpdesk/helpdesk/forms.py:163 msgid "Please select a priority carefully. If unsure, leave it as '3'." msgstr "" "Prosím volte prioritu uvážlivě. Pokud si nejste jisti, ponechte číslo '3'." -#: helpdesk/forms.py:170 helpdesk/models.py:467 -#: helpdesk/templates/helpdesk/ticket.html:218 helpdesk/views/staff.py:558 +#: third_party/django-helpdesk/helpdesk/forms.py:170 +#: third_party/django-helpdesk/helpdesk/models.py:543 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:181 +#: third_party/django-helpdesk/helpdesk/views/staff.py:603 msgid "Due on" msgstr "Vyřešit do" -#: helpdesk/forms.py:175 +#: third_party/django-helpdesk/helpdesk/forms.py:176 msgid "Attach File" msgstr "Přiložit soubor" -#: helpdesk/forms.py:176 +#: third_party/django-helpdesk/helpdesk/forms.py:177 msgid "You can attach a file such as a document or screenshot to this ticket." msgstr "K ticketu můžete přiložit soubor, dokument nebo snímek obrazovky." -#: helpdesk/forms.py:299 +#: third_party/django-helpdesk/helpdesk/forms.py:186 +#, fuzzy +#| msgid "Knowledge base item" +msgid "Knowedge Base Item" +msgstr "Položka znalostní báze" + +#: third_party/django-helpdesk/helpdesk/forms.py:274 msgid "Submitter E-Mail Address" msgstr "E-mailová adresa zadavatele" -#: helpdesk/forms.py:301 +#: third_party/django-helpdesk/helpdesk/forms.py:276 msgid "" "This e-mail address will receive copies of all public updates to this ticket." msgstr "Na tuto adresu budou zasílány všechny veřejné aktualizace k ticketu." -#: helpdesk/forms.py:309 +#: third_party/django-helpdesk/helpdesk/forms.py:282 msgid "Case owner" msgstr "Vlastník případu" -#: helpdesk/forms.py:310 +#: third_party/django-helpdesk/helpdesk/forms.py:283 msgid "" "If you select an owner other than yourself, they'll be e-mailed details of " "this ticket immediately." @@ -106,93 +147,38 @@ msgstr "" "Pokud vyberete jiného vlastníka než sami sebe, budou mu okamžitě e-mailem " "odeslány detaily ticketu." -#: helpdesk/forms.py:338 +#: third_party/django-helpdesk/helpdesk/forms.py:322 #, python-format msgid "Ticket Opened & Assigned to %(name)s" msgstr "Ticket byl otevřen a přiřazen 1%(name)s" -#: helpdesk/forms.py:339 +#: third_party/django-helpdesk/helpdesk/forms.py:323 msgid "" msgstr "" -#: helpdesk/forms.py:342 +#: third_party/django-helpdesk/helpdesk/forms.py:326 msgid "Ticket Opened" msgstr "Ticket byl otevřen" -#: helpdesk/forms.py:362 +#: third_party/django-helpdesk/helpdesk/forms.py:346 msgid "Your E-Mail Address" msgstr "Váš e-mail" -#: helpdesk/forms.py:363 +#: third_party/django-helpdesk/helpdesk/forms.py:347 msgid "We will e-mail you when your ticket is updated." msgstr "Při aktualizaci ticketu budete informováni e-mailem." -#: helpdesk/forms.py:392 +#: third_party/django-helpdesk/helpdesk/forms.py:384 msgid "Ticket Opened Via Web" msgstr "Ticket založen prostřednictvím webového formuláře." -#: helpdesk/forms.py:405 -msgid "Show Ticket List on Login?" -msgstr "Zobrazit seznam ticketů po přihlášení?" - -#: helpdesk/forms.py:406 -msgid "Display the ticket list upon login? Otherwise, the dashboard is shown." -msgstr "" -"Zobrazit seznam ticketů po přihlášení? V opačném případě bude zobrazena " -"nástěnka." - -#: helpdesk/forms.py:411 -msgid "E-mail me on ticket change?" -msgstr "Poslat mi e-mail při změně ticketu?" - -#: helpdesk/forms.py:412 -msgid "" -"If you're the ticket owner and the ticket is changed via the web by somebody " -"else, do you want to receive an e-mail?" -msgstr "" -"Přejete si dostat e-mail v případě, pokud jste vlastník ticketu a ticket je " -"změněn prostřednictvím webového formuláře někým dalším." - -#: helpdesk/forms.py:417 -msgid "E-mail me when assigned a ticket?" -msgstr "Poslat e-mail, pokud mi byl přiřazen ticket?" - -#: helpdesk/forms.py:418 -msgid "" -"If you are assigned a ticket via the web, do you want to receive an e-mail?" -msgstr "" -"Přejete si dostat e-mail, pokud Vám byl přiřazen ticket prostřednictvím " -"webového formuláře?" - -#: helpdesk/forms.py:423 -msgid "Number of tickets to show per page" -msgstr "Počet ticketů zobrazených na stránku" - -#: helpdesk/forms.py:424 -msgid "How many tickets do you want to see on the Ticket List page?" -msgstr "Kolik ticketů chcete vidět na stránce Seznam ticketů?" - -#: helpdesk/forms.py:430 -msgid "Use my e-mail address when submitting tickets?" -msgstr "Použít můj e-mail, pokud zadávám nový ticket?" - -#: helpdesk/forms.py:431 -msgid "" -"When you submit a ticket, do you want to automatically use your e-mail " -"address as the submitter address? You can type a different e-mail address " -"when entering the ticket if needed, this option only changes the default." -msgstr "" -"V případě, že zadáte nový ticket, chcete, aby Váš e-mail byl automaticky " -"přiřazen jako e-mail zadavatele? Můžete také zadat jiný e-mail, pokud je to " -"potřeba, tato volba pouze nastaví výchozí hodnotu." - -#: helpdesk/management/commands/create_queue_permissions.py:69 -#: helpdesk/migrations/0009_migrate_queuemembership.py:30 -#: helpdesk/models.py:331 +#: third_party/django-helpdesk/helpdesk/management/commands/create_queue_permissions.py:69 +#: third_party/django-helpdesk/helpdesk/migrations/0009_migrate_queuemembership.py:28 +#: third_party/django-helpdesk/helpdesk/models.py:404 msgid "Permission for queue: " msgstr "" -#: helpdesk/management/commands/create_usersettings.py:24 +#: third_party/django-helpdesk/helpdesk/management/commands/create_usersettings.py:24 msgid "" "Check for user without django-helpdesk UserSettings and create settings if " "required. Uses settings.DEFAULT_USER_SETTINGS which can be overridden to " @@ -202,44 +188,23 @@ msgstr "" "požadováno, vytvořit nastavení. Používá settings.DEFAULT_USER_SETTINGS, " "které může být přepsáno pro váš případ." -#: helpdesk/management/commands/escalate_tickets.py:150 +#: third_party/django-helpdesk/helpdesk/management/commands/escalate_tickets.py:125 #, python-format msgid "Ticket escalated after %s days" msgstr "Priorita ticketu zvýšena po %s dnech" -#: helpdesk/management/commands/get_email.py:306 -msgid "Comment from e-mail" -msgstr "Komentář z e-mailu" - -#: helpdesk/management/commands/get_email.py:312 -msgid "Unknown Sender" -msgstr "Neznámý odesílatel" - -#: helpdesk/management/commands/get_email.py:369 -msgid "email_html_body.html" -msgstr "email_html_body.html" - -#: helpdesk/management/commands/get_email.py:481 -#, python-format -msgid "E-Mail Received from %(sender_email)s" -msgstr "E-mail obdržen od %(sender_email)s" - -#: helpdesk/management/commands/get_email.py:489 -#, python-format -msgid "Ticket Re-Opened by E-Mail Received from %(sender_email)s" -msgstr "Ticket znovu otevřen e-mailem od%(sender_email)s" - -#: helpdesk/models.py:35 helpdesk/models.py:392 helpdesk/models.py:651 -#: helpdesk/models.py:960 helpdesk/models.py:998 -#: helpdesk/templates/helpdesk/include/tickets.html:17 -#: helpdesk/templates/helpdesk/include/unassigned.html:17 -#: helpdesk/templates/helpdesk/ticket.html:209 -#: helpdesk/templates/helpdesk/ticket_list.html:79 -#: helpdesk/templates/helpdesk/ticket_list.html:222 helpdesk/views/staff.py:520 +#: third_party/django-helpdesk/helpdesk/models.py:79 +#: third_party/django-helpdesk/helpdesk/models.py:468 +#: third_party/django-helpdesk/helpdesk/models.py:829 +#: third_party/django-helpdesk/helpdesk/models.py:1215 +#: third_party/django-helpdesk/helpdesk/models.py:1268 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:172 +#: third_party/django-helpdesk/helpdesk/views/staff.py:565 msgid "Title" msgstr "Nadpis" -#: helpdesk/models.py:43 +#: third_party/django-helpdesk/helpdesk/models.py:87 msgid "" "This slug is used when building ticket ID's. Once set, try not to change it " "or e-mailing may get messy." @@ -248,13 +213,15 @@ msgstr "" "tvorbě ID ticketu. Pokud je jednou nastaven, již jej neměňte, protože " "nastane zmatek v e-mailech." -#: helpdesk/models.py:48 helpdesk/models.py:1208 helpdesk/models.py:1291 -#: helpdesk/models.py:1371 -#: helpdesk/templates/helpdesk/email_ignore_list.html:25 +#: third_party/django-helpdesk/helpdesk/models.py:92 +#: third_party/django-helpdesk/helpdesk/models.py:1513 +#: third_party/django-helpdesk/helpdesk/models.py:1595 +#: third_party/django-helpdesk/helpdesk/models.py:1674 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:25 msgid "E-Mail Address" msgstr "Adresa e-mailu" -#: helpdesk/models.py:51 +#: third_party/django-helpdesk/helpdesk/models.py:95 msgid "" "All outgoing e-mails for this queue will use this e-mail address. If you use " "IMAP or POP3, this should be the e-mail address for that mailbox." @@ -262,11 +229,12 @@ msgstr "" "Všechny odchozí e-maily této fronty budou odeslány z této e-mailové adresy. " "Pokud používáte IMAP nebo POP3, tato adresa je adresa Vašeho mailboxu." -#: helpdesk/models.py:57 helpdesk/models.py:936 +#: third_party/django-helpdesk/helpdesk/models.py:101 +#: third_party/django-helpdesk/helpdesk/models.py:1192 msgid "Locale" msgstr "Locale" -#: helpdesk/models.py:61 +#: third_party/django-helpdesk/helpdesk/models.py:105 msgid "" "Locale of this queue. All correspondence in this queue will be in this " "language." @@ -274,28 +242,28 @@ msgstr "" "Jazykové nastavení fronty. Veškerá korespondence této fronty bude používat " "tento jazyk." -#: helpdesk/models.py:66 +#: third_party/django-helpdesk/helpdesk/models.py:110 msgid "Allow Public Submission?" msgstr "Povolit veřejné podání?" -#: helpdesk/models.py:69 +#: third_party/django-helpdesk/helpdesk/models.py:113 msgid "Should this queue be listed on the public submission form?" msgstr "Má tato fronta být uvedena v seznamu veřejného formuláře pro podání?" -#: helpdesk/models.py:73 +#: third_party/django-helpdesk/helpdesk/models.py:117 msgid "Allow E-Mail Submission?" msgstr "Povolit e-mailové podání?" -#: helpdesk/models.py:76 +#: third_party/django-helpdesk/helpdesk/models.py:120 msgid "Do you want to poll the e-mail box below for new tickets?" msgstr "" "Má si tato fronta stahovat nové tickety z e-mailové schránky automaticky?" -#: helpdesk/models.py:81 +#: third_party/django-helpdesk/helpdesk/models.py:125 msgid "Escalation Days" msgstr "Počet dní do zvýšení priority" -#: helpdesk/models.py:84 +#: third_party/django-helpdesk/helpdesk/models.py:128 msgid "" "For tickets which are not held, how often do you wish to increase their " "priority? Set to 0 for no escalation." @@ -303,11 +271,11 @@ msgstr "" "Jak často chcete zvýšit prioritu ticketů, které nejsou pozdržené? Nastavte " "'0' pro ponechání původní priority." -#: helpdesk/models.py:89 +#: third_party/django-helpdesk/helpdesk/models.py:133 msgid "New Ticket CC Address" msgstr "CC adresa pro nové tickety" -#: helpdesk/models.py:93 +#: third_party/django-helpdesk/helpdesk/models.py:137 msgid "" "If an e-mail address is entered here, then it will receive notification of " "all new tickets created for this queue. Enter a comma between multiple e-" @@ -316,11 +284,11 @@ msgstr "" "Pokud zadáte e-mailovou adresu, tato adresa bude dostávat upozornění na nové " "tickety této fronty. Více adres může být odděleno čárkou." -#: helpdesk/models.py:99 +#: third_party/django-helpdesk/helpdesk/models.py:143 msgid "Updated Ticket CC Address" msgstr "CC adresa pro aktualizaci ticketů" -#: helpdesk/models.py:103 +#: third_party/django-helpdesk/helpdesk/models.py:147 msgid "" "If an e-mail address is entered here, then it will receive notification of " "all activity (new tickets, closed tickets, updates, reassignments, etc) for " @@ -330,23 +298,34 @@ msgstr "" "všech aktualizacích ticketů této fronty (nové tickety, uzavřené tickety, " "aktualizace, přiřazení, atd.). Více adres může být odděleno čárkou." -#: helpdesk/models.py:110 +#: third_party/django-helpdesk/helpdesk/models.py:154 +msgid "Notify contacts when email updates arrive" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:157 +msgid "" +"When an email arrives to either create a ticket or to interact with an " +"existing discussion. Should email notifications be sent ? Note: the " +"new_ticket_cc and updated_ticket_cc work independently of this feature" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:163 msgid "E-Mail Box Type" msgstr "Typ mailové schránky" -#: helpdesk/models.py:112 +#: third_party/django-helpdesk/helpdesk/models.py:165 msgid "POP 3" msgstr "POP 3" -#: helpdesk/models.py:112 +#: third_party/django-helpdesk/helpdesk/models.py:165 msgid "IMAP" msgstr "IMAP" -#: helpdesk/models.py:112 +#: third_party/django-helpdesk/helpdesk/models.py:165 msgid "Local Directory" msgstr "" -#: helpdesk/models.py:115 +#: third_party/django-helpdesk/helpdesk/models.py:168 msgid "" "E-Mail server type for creating tickets automatically from a mailbox - both " "POP3 and IMAP are supported, as well as reading from a local directory." @@ -354,21 +333,21 @@ msgstr "" "Typ e-mailového serveru pro automatické vytváření ticketů - jsou podporovány " "protokoly IMAP a POP3, stejně jako čtená z lokální složky." -#: helpdesk/models.py:121 +#: third_party/django-helpdesk/helpdesk/models.py:174 msgid "E-Mail Hostname" msgstr "E-mail Hostname" -#: helpdesk/models.py:125 +#: third_party/django-helpdesk/helpdesk/models.py:178 msgid "" "Your e-mail server address - either the domain name or IP address. May be " "\"localhost\"." msgstr "Doména mail serveru nebo IP adresa. Může být i \"localhost\"." -#: helpdesk/models.py:130 +#: third_party/django-helpdesk/helpdesk/models.py:183 msgid "E-Mail Port" msgstr "Port" -#: helpdesk/models.py:133 +#: third_party/django-helpdesk/helpdesk/models.py:186 msgid "" "Port number to use for accessing e-mail. Default for POP3 is \"110\", and " "for IMAP is \"143\". This may differ on some servers. Leave it blank to use " @@ -378,37 +357,37 @@ msgstr "" "Některé servery mají vlastní nastavení. Pro použití výchozích hodnot " "ponechte prázdné." -#: helpdesk/models.py:139 +#: third_party/django-helpdesk/helpdesk/models.py:192 msgid "Use SSL for E-Mail?" msgstr "Použít SSL pro e-mail?" -#: helpdesk/models.py:142 +#: third_party/django-helpdesk/helpdesk/models.py:195 msgid "" "Whether to use SSL for IMAP or POP3 - the default ports when using SSL are " "993 for IMAP and 995 for POP3." msgstr "Pokud je použito SSL, výchozí port pro POP3 je 995 a pro IMAP je 993." -#: helpdesk/models.py:147 +#: third_party/django-helpdesk/helpdesk/models.py:200 msgid "E-Mail Username" msgstr "uživatelské jméno e-mailu" -#: helpdesk/models.py:151 +#: third_party/django-helpdesk/helpdesk/models.py:204 msgid "Username for accessing this mailbox." msgstr "Uživatel s právy k poštovní schránce." -#: helpdesk/models.py:155 +#: third_party/django-helpdesk/helpdesk/models.py:208 msgid "E-Mail Password" msgstr "Heslo k e-mailu" -#: helpdesk/models.py:159 +#: third_party/django-helpdesk/helpdesk/models.py:212 msgid "Password for the above username" msgstr "Heslo pro výše nastaveného uživatele." -#: helpdesk/models.py:163 +#: third_party/django-helpdesk/helpdesk/models.py:216 msgid "IMAP Folder" msgstr "Adresář pro IMAP" -#: helpdesk/models.py:167 +#: third_party/django-helpdesk/helpdesk/models.py:220 msgid "" "If using IMAP, what folder do you wish to fetch messages from? This allows " "you to use one IMAP account for multiple queues, by filtering messages on " @@ -418,94 +397,95 @@ msgstr "" "umožní použít jeden účet IMAP pro více front prostřednictvím filtrování " "zpráv na serveru IMAP do několika složek. Výchozí: INBOX" -#: helpdesk/models.py:174 +#: third_party/django-helpdesk/helpdesk/models.py:227 msgid "E-Mail Local Directory" msgstr "Lokální složka pro e-mail" -#: helpdesk/models.py:178 +#: third_party/django-helpdesk/helpdesk/models.py:231 msgid "" "If using a local directory, what directory path do you wish to poll for new " "email? Example: /var/lib/mail/helpdesk/" msgstr "" -#: helpdesk/models.py:184 +#: third_party/django-helpdesk/helpdesk/models.py:237 msgid "Django auth permission name" msgstr "" -#: helpdesk/models.py:189 +#: third_party/django-helpdesk/helpdesk/models.py:242 msgid "Name used in the django.contrib.auth permission system" msgstr "" -#: helpdesk/models.py:193 +#: third_party/django-helpdesk/helpdesk/models.py:246 msgid "E-Mail Check Interval" msgstr "Interval kontroly e-mailu" -#: helpdesk/models.py:194 +#: third_party/django-helpdesk/helpdesk/models.py:247 msgid "How often do you wish to check this mailbox? (in Minutes)" msgstr "Jak často si přejete kontrolovat mailbox? (minuty)" -#: helpdesk/models.py:208 +#: third_party/django-helpdesk/helpdesk/models.py:261 msgid "Socks Proxy Type" msgstr "" -#: helpdesk/models.py:210 +#: third_party/django-helpdesk/helpdesk/models.py:263 msgid "SOCKS4" msgstr "" -#: helpdesk/models.py:210 +#: third_party/django-helpdesk/helpdesk/models.py:263 msgid "SOCKS5" msgstr "" -#: helpdesk/models.py:213 +#: third_party/django-helpdesk/helpdesk/models.py:266 msgid "" "SOCKS4 or SOCKS5 allows you to proxy your connections through a SOCKS server." msgstr "" -#: helpdesk/models.py:217 +#: third_party/django-helpdesk/helpdesk/models.py:270 msgid "Socks Proxy Host" msgstr "" -#: helpdesk/models.py:220 +#: third_party/django-helpdesk/helpdesk/models.py:273 msgid "Socks proxy IP address. Default: 127.0.0.1" msgstr "" -#: helpdesk/models.py:224 +#: third_party/django-helpdesk/helpdesk/models.py:277 msgid "Socks Proxy Port" msgstr "" -#: helpdesk/models.py:227 +#: third_party/django-helpdesk/helpdesk/models.py:280 msgid "Socks proxy port number. Default: 9150 (default TOR port)" msgstr "" -#: helpdesk/models.py:231 +#: third_party/django-helpdesk/helpdesk/models.py:284 msgid "Logging Type" msgstr "" -#: helpdesk/models.py:234 helpdesk/templates/helpdesk/ticket_list.html:250 +#: third_party/django-helpdesk/helpdesk/models.py:287 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:80 msgid "None" msgstr "Žádný" -#: helpdesk/models.py:235 +#: third_party/django-helpdesk/helpdesk/models.py:288 msgid "Debug" msgstr "Debug" -#: helpdesk/models.py:236 +#: third_party/django-helpdesk/helpdesk/models.py:289 msgid "Information" msgstr "Informace" -#: helpdesk/models.py:237 +#: third_party/django-helpdesk/helpdesk/models.py:290 msgid "Warning" msgstr "Varování" -#: helpdesk/models.py:238 +#: third_party/django-helpdesk/helpdesk/models.py:291 msgid "Error" msgstr "Chyba" -#: helpdesk/models.py:239 +#: third_party/django-helpdesk/helpdesk/models.py:292 msgid "Critical" msgstr "Kritická" -#: helpdesk/models.py:243 +#: third_party/django-helpdesk/helpdesk/models.py:296 msgid "" "Set the default logging level. All messages at that level or above will be " "logged to the directory set below. If no level is set, logging will be " @@ -515,147 +495,171 @@ msgstr "" "nebo vyšší do složky nastavené níže. Pokud úroveň nenastavíte, bude logování " "vypnuto." -#: helpdesk/models.py:249 +#: third_party/django-helpdesk/helpdesk/models.py:302 msgid "Logging Directory" msgstr "Složka pro log soubory" -#: helpdesk/models.py:253 +#: third_party/django-helpdesk/helpdesk/models.py:306 msgid "" "If logging is enabled, what directory should we use to store log files for " "this queue? If no directory is set, default to /var/log/helpdesk/" msgstr "" -#: helpdesk/models.py:264 +#: third_party/django-helpdesk/helpdesk/models.py:317 msgid "Default owner" msgstr "Výchozí vlastník" -#: helpdesk/models.py:273 helpdesk/templates/helpdesk/email_ignore_list.html:27 +#: third_party/django-helpdesk/helpdesk/models.py:321 +msgid "Time to be spent on this Queue in total" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:331 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:27 msgid "Queues" msgstr "Fronty" -#: helpdesk/models.py:376 helpdesk/templates/helpdesk/report_index.html:40 -#: helpdesk/templates/helpdesk/ticket.html:160 +#: third_party/django-helpdesk/helpdesk/models.py:452 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:45 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:116 msgid "Open" msgstr "Otevřené" -#: helpdesk/models.py:377 helpdesk/templates/helpdesk/ticket.html:168 -#: helpdesk/templates/helpdesk/ticket.html:176 -#: helpdesk/templates/helpdesk/ticket.html:182 -#: helpdesk/templates/helpdesk/ticket.html:187 +#: third_party/django-helpdesk/helpdesk/models.py:453 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:99 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:107 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:113 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:118 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:124 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:132 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:138 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:143 msgid "Reopened" msgstr "Znovu otevřené" -#: helpdesk/models.py:378 helpdesk/templates/helpdesk/report_index.html:41 -#: helpdesk/templates/helpdesk/ticket.html:161 -#: helpdesk/templates/helpdesk/ticket.html:169 -#: helpdesk/templates/helpdesk/ticket.html:177 +#: third_party/django-helpdesk/helpdesk/models.py:454 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:100 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:108 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:46 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:117 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:125 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:133 msgid "Resolved" msgstr "Vyřešené" -#: helpdesk/models.py:379 helpdesk/templates/helpdesk/report_index.html:42 -#: helpdesk/templates/helpdesk/ticket.html:162 -#: helpdesk/templates/helpdesk/ticket.html:170 -#: helpdesk/templates/helpdesk/ticket.html:178 -#: helpdesk/templates/helpdesk/ticket.html:183 +#: third_party/django-helpdesk/helpdesk/models.py:455 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:101 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:109 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:114 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:47 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:118 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:126 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:134 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:139 msgid "Closed" msgstr "Uzavřené" -#: helpdesk/models.py:380 helpdesk/templates/helpdesk/ticket.html:163 -#: helpdesk/templates/helpdesk/ticket.html:171 -#: helpdesk/templates/helpdesk/ticket.html:188 +#: third_party/django-helpdesk/helpdesk/models.py:456 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:102 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:119 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:119 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:127 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:144 msgid "Duplicate" msgstr "Duplicitní" -#: helpdesk/models.py:384 +#: third_party/django-helpdesk/helpdesk/models.py:460 msgid "1. Critical" msgstr "1. kritická" -#: helpdesk/models.py:385 +#: third_party/django-helpdesk/helpdesk/models.py:461 msgid "2. High" msgstr "2. vysoká" -#: helpdesk/models.py:386 +#: third_party/django-helpdesk/helpdesk/models.py:462 msgid "3. Normal" msgstr "3. normální" -#: helpdesk/models.py:387 +#: third_party/django-helpdesk/helpdesk/models.py:463 msgid "4. Low" msgstr "4. nízká" -#: helpdesk/models.py:388 +#: third_party/django-helpdesk/helpdesk/models.py:464 msgid "5. Very Low" msgstr "5. velmi nízká" -#: helpdesk/models.py:403 -#: helpdesk/templates/helpdesk/include/unassigned.html:19 -#: helpdesk/templates/helpdesk/ticket_list.html:76 -#: helpdesk/templates/helpdesk/ticket_list.html:225 +#: third_party/django-helpdesk/helpdesk/models.py:479 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:17 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:66 msgid "Created" msgstr "Vytvořeno" -#: helpdesk/models.py:405 +#: third_party/django-helpdesk/helpdesk/models.py:481 msgid "Date this ticket was first created" msgstr "Datum prvního založení ticketu" -#: helpdesk/models.py:409 +#: third_party/django-helpdesk/helpdesk/models.py:485 msgid "Modified" msgstr "Změněno" -#: helpdesk/models.py:411 +#: third_party/django-helpdesk/helpdesk/models.py:487 msgid "Date this ticket was most recently changed." msgstr "Datum poslední změny ticketu" -#: helpdesk/models.py:418 +#: third_party/django-helpdesk/helpdesk/models.py:494 msgid "" "The submitter will receive an email for all public follow-ups left for this " "task." msgstr "" "Na zadavatelův e-mail budou zasílány všechny veřejné aktualizace ticketu." -#: helpdesk/models.py:428 +#: third_party/django-helpdesk/helpdesk/models.py:504 msgid "Assigned to" msgstr "Přiřazeno" -#: helpdesk/models.py:432 helpdesk/templates/helpdesk/include/tickets.html:19 -#: helpdesk/templates/helpdesk/ticket_list.html:64 -#: helpdesk/templates/helpdesk/ticket_list.html:85 -#: helpdesk/templates/helpdesk/ticket_list.html:224 helpdesk/views/staff.py:530 +#: third_party/django-helpdesk/helpdesk/models.py:508 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:19 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:17 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:65 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:171 +#: third_party/django-helpdesk/helpdesk/views/staff.py:575 msgid "Status" msgstr "Stav" -#: helpdesk/models.py:438 +#: third_party/django-helpdesk/helpdesk/models.py:514 msgid "On Hold" msgstr "Pozdrženo" -#: helpdesk/models.py:441 +#: third_party/django-helpdesk/helpdesk/models.py:517 msgid "If a ticket is on hold, it will not automatically be escalated." msgstr "Ticketu se stavem \"pozdrženo\" nebude automaticky zvýšena priorita." -#: helpdesk/models.py:445 helpdesk/models.py:969 -#: helpdesk/templates/helpdesk/public_view_ticket.html:42 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:29 +#: third_party/django-helpdesk/helpdesk/models.py:521 +#: third_party/django-helpdesk/helpdesk/models.py:1224 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:42 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:95 msgid "Description" msgstr "Popis" -#: helpdesk/models.py:448 +#: third_party/django-helpdesk/helpdesk/models.py:524 msgid "The content of the customers query." msgstr "Obsah zákaznického požadavku" -#: helpdesk/models.py:452 -#: helpdesk/templates/helpdesk/public_view_ticket.html:49 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:36 +#: third_party/django-helpdesk/helpdesk/models.py:528 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:49 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:100 msgid "Resolution" msgstr "Rozřešení" -#: helpdesk/models.py:455 +#: third_party/django-helpdesk/helpdesk/models.py:531 msgid "The resolution provided to the customer by our staff." msgstr "Rozřešení nastavené námi směrem k zákazníkům." -#: helpdesk/models.py:463 +#: third_party/django-helpdesk/helpdesk/models.py:539 msgid "1 = Highest Priority, 5 = Low Priority" msgstr "1 = nejvyšší priorita, 5 = nejnižší priorita" -#: helpdesk/models.py:476 +#: third_party/django-helpdesk/helpdesk/models.py:552 msgid "" "The date this ticket was last escalated - updated automatically by " "management/commands/escalate_tickets.py." @@ -663,46 +667,79 @@ msgstr "" "Datum, kdy byla priorita ticketu zvýšena - ticket byl automaticky " "aktualizován pomocí skriptu management/commands/escalte_tickets.py." -#: helpdesk/models.py:485 helpdesk/templates/helpdesk/ticket_desc_table.html:53 -#: helpdesk/views/feeds.py:93 helpdesk/views/feeds.py:118 -#: helpdesk/views/feeds.py:170 helpdesk/views/staff.py:492 +#: third_party/django-helpdesk/helpdesk/models.py:557 +msgid "Secret key needed for viewing/editing ticket by non-logged in users" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:567 +msgid "Knowledge base item the user was viewing when they created this ticket." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:639 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:35 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:93 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:118 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:170 +#: third_party/django-helpdesk/helpdesk/views/staff.py:535 msgid "Unassigned" msgstr "Nepřiřazeno" -#: helpdesk/models.py:525 +#: third_party/django-helpdesk/helpdesk/models.py:679 msgid " - On Hold" msgstr "- Pozdrženo" -#: helpdesk/models.py:528 +#: third_party/django-helpdesk/helpdesk/models.py:682 msgid " - Open dependencies" msgstr "- Otevřít závislosti" -#: helpdesk/models.py:585 helpdesk/models.py:642 helpdesk/models.py:1278 -#: helpdesk/models.py:1454 helpdesk/models.py:1489 -#: helpdesk/templates/helpdesk/public_homepage.html:79 -#: helpdesk/templates/helpdesk/public_view_form.html:12 +#: third_party/django-helpdesk/helpdesk/models.py:757 +#: third_party/django-helpdesk/helpdesk/models.py:820 +#: third_party/django-helpdesk/helpdesk/models.py:1582 +#: third_party/django-helpdesk/helpdesk/models.py:1755 +#: third_party/django-helpdesk/helpdesk/models.py:1789 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:89 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_form.html:12 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:62 msgid "Ticket" msgstr "Ticket" -#: helpdesk/models.py:586 helpdesk/templates/helpdesk/navigation.html:23 -#: helpdesk/templates/helpdesk/ticket_list.html:4 +#: third_party/django-helpdesk/helpdesk/models.py:758 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/create_ticket.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/delete_ticket.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_add.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:19 msgid "Tickets" msgstr "Tickety" -#: helpdesk/models.py:646 helpdesk/models.py:880 helpdesk/models.py:1201 -#: helpdesk/models.py:1368 +#: third_party/django-helpdesk/helpdesk/models.py:824 +#: third_party/django-helpdesk/helpdesk/models.py:1137 +#: third_party/django-helpdesk/helpdesk/models.py:1506 +#: third_party/django-helpdesk/helpdesk/models.py:1671 msgid "Date" msgstr "Datum" -#: helpdesk/models.py:658 helpdesk/views/staff.py:509 +#: third_party/django-helpdesk/helpdesk/models.py:836 +#: third_party/django-helpdesk/helpdesk/views/staff.py:552 msgid "Comment" msgstr "Komentář" -#: helpdesk/models.py:664 +#: third_party/django-helpdesk/helpdesk/models.py:842 msgid "Public" msgstr "Veřejný" -#: helpdesk/models.py:667 +#: third_party/django-helpdesk/helpdesk/models.py:846 msgid "" "Public tickets are viewable by the submitter and all staff, but non-public " "tickets can only be seen by staff." @@ -710,100 +747,129 @@ msgstr "" "Veřejné tickety jsou viditelné zadavatelem a naším personálem, ale neveřejné " "tickety jsou viděny pouze naším personálem. " -#: helpdesk/models.py:676 helpdesk/models.py:1068 helpdesk/models.py:1287 -#: helpdesk/templates/helpdesk/ticket_cc_add.html:20 -#: helpdesk/views/staff.py:1228 helpdesk/views/staff.py:1234 -#: helpdesk/views/staff.py:1241 helpdesk/views/staff.py:1247 +#: third_party/django-helpdesk/helpdesk/models.py:856 +#: third_party/django-helpdesk/helpdesk/models.py:1347 +#: third_party/django-helpdesk/helpdesk/models.py:1591 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:31 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1186 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1192 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1199 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1205 msgid "User" msgstr "Uživatel" -#: helpdesk/models.py:680 helpdesk/templates/helpdesk/ticket.html:156 +#: third_party/django-helpdesk/helpdesk/models.py:860 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:112 msgid "New Status" msgstr "Nový stav" -#: helpdesk/models.py:684 +#: third_party/django-helpdesk/helpdesk/models.py:864 msgid "If the status was changed, what was it changed to?" msgstr "Pokud byl stav změněn, na co?" -#: helpdesk/models.py:691 helpdesk/models.py:717 helpdesk/models.py:780 +#: third_party/django-helpdesk/helpdesk/models.py:868 +#, fuzzy +#| msgid "E-Mail Port" +msgid "E-Mail ID" +msgstr "Port" + +#: third_party/django-helpdesk/helpdesk/models.py:872 +msgid "The Message ID of the submitter's email." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:879 +msgid "Time spent on this follow up" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/models.py:885 +#: third_party/django-helpdesk/helpdesk/models.py:917 +#: third_party/django-helpdesk/helpdesk/models.py:1034 msgid "Follow-up" msgstr "Aktualizace" -#: helpdesk/models.py:692 +#: third_party/django-helpdesk/helpdesk/models.py:886 msgid "Follow-ups" msgstr "Aktualizace" -#: helpdesk/models.py:721 helpdesk/models.py:1460 +#: third_party/django-helpdesk/helpdesk/models.py:921 +#: third_party/django-helpdesk/helpdesk/models.py:1761 msgid "Field" msgstr "Pole" -#: helpdesk/models.py:726 +#: third_party/django-helpdesk/helpdesk/models.py:926 msgid "Old Value" msgstr "Stará hodnota" -#: helpdesk/models.py:732 +#: third_party/django-helpdesk/helpdesk/models.py:932 msgid "New Value" msgstr "Nová hodnota" -#: helpdesk/models.py:740 +#: third_party/django-helpdesk/helpdesk/models.py:940 msgid "removed" msgstr "Smazáno" -#: helpdesk/models.py:742 +#: third_party/django-helpdesk/helpdesk/models.py:942 #, python-format msgid "set to %s" msgstr "nastaveno na %s" -#: helpdesk/models.py:744 +#: third_party/django-helpdesk/helpdesk/models.py:944 #, python-format msgid "changed from \"%(old_value)s\" to \"%(new_value)s\"" msgstr "změněno z \"%(old_value)s\" na \"%(new_value)s\"" -#: helpdesk/models.py:751 +#: third_party/django-helpdesk/helpdesk/models.py:951 msgid "Ticket change" msgstr "Změna ticketu" -#: helpdesk/models.py:752 +#: third_party/django-helpdesk/helpdesk/models.py:952 msgid "Ticket changes" msgstr "Změny ticketu" -#: helpdesk/models.py:784 +#: third_party/django-helpdesk/helpdesk/models.py:967 msgid "File" msgstr "Soubor" -#: helpdesk/models.py:790 +#: third_party/django-helpdesk/helpdesk/models.py:973 msgid "Filename" msgstr "Jméno souboru" -#: helpdesk/models.py:795 +#: third_party/django-helpdesk/helpdesk/models.py:979 msgid "MIME Type" msgstr "MIME Type" -#: helpdesk/models.py:800 +#: third_party/django-helpdesk/helpdesk/models.py:985 msgid "Size" msgstr "Velikost" -#: helpdesk/models.py:801 +#: third_party/django-helpdesk/helpdesk/models.py:987 msgid "Size of this file in bytes" msgstr "Velikost souboru [bytes]" -#: helpdesk/models.py:809 +#: third_party/django-helpdesk/helpdesk/models.py:1024 msgid "Attachment" msgstr "Příloha" -#: helpdesk/models.py:810 +#: third_party/django-helpdesk/helpdesk/models.py:1025 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:56 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:78 msgid "Attachments" msgstr "Přílohy" -#: helpdesk/models.py:827 +#: third_party/django-helpdesk/helpdesk/models.py:1056 +#: third_party/django-helpdesk/helpdesk/models.py:1315 +msgid "Knowledge base item" +msgstr "Položka znalostní báze" + +#: third_party/django-helpdesk/helpdesk/models.py:1085 msgid "Pre-set reply" msgstr "Přednastavená odpověď" -#: helpdesk/models.py:828 +#: third_party/django-helpdesk/helpdesk/models.py:1086 msgid "Pre-set replies" msgstr "Přednastavené odpovědi" -#: helpdesk/models.py:833 +#: third_party/django-helpdesk/helpdesk/models.py:1091 msgid "" "Leave blank to allow this reply to be used for all queues, or select those " "queues you wish to limit this reply to." @@ -811,23 +877,25 @@ msgstr "" "Ponechte prázdné aby bylo možno tuto odpověď použít pro všechny fronty nebo " "vyberte jednu z front, na kterou chcete tuto odpověď aplikovat." -#: helpdesk/models.py:838 helpdesk/models.py:875 helpdesk/models.py:1196 -#: helpdesk/templates/helpdesk/email_ignore_list.html:24 +#: third_party/django-helpdesk/helpdesk/models.py:1096 +#: third_party/django-helpdesk/helpdesk/models.py:1132 +#: third_party/django-helpdesk/helpdesk/models.py:1501 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:24 msgid "Name" msgstr "Název" -#: helpdesk/models.py:840 +#: third_party/django-helpdesk/helpdesk/models.py:1098 msgid "" "Only used to assist users with selecting a reply - not shown to the user." msgstr "" "Je použit pouze pro uživatele, aby vybrali správnou odpověď - jinak není " "zobrazen." -#: helpdesk/models.py:845 +#: third_party/django-helpdesk/helpdesk/models.py:1103 msgid "Body" msgstr "Obsah" -#: helpdesk/models.py:846 +#: third_party/django-helpdesk/helpdesk/models.py:1104 msgid "" "Context available: {{ ticket }} - ticket object (eg {{ ticket.title }}); " "{{ queue }} - The queue; and {{ user }} - the current user." @@ -835,7 +903,7 @@ msgstr "" "Dostupný kontext: {{ ticket }} - Ticket (např. {{ ticket.title }}); " "{{ queue }} - Fronta; and {{ user }} - současný uživatel." -#: helpdesk/models.py:870 +#: third_party/django-helpdesk/helpdesk/models.py:1127 msgid "" "Leave blank for this exclusion to be applied to all queues, or select those " "queues you wish to exclude with this entry." @@ -843,27 +911,27 @@ msgstr "" "Ponechte prázdné, pokud si přejete aplikovat na všechny fronty nebo vyberte " "frontu, na kterou se tento záznam vztáhne." -#: helpdesk/models.py:881 +#: third_party/django-helpdesk/helpdesk/models.py:1138 msgid "Date on which escalation should not happen" msgstr "Datum, kdy se priorita ticketu nemá zvyšovat." -#: helpdesk/models.py:888 +#: third_party/django-helpdesk/helpdesk/models.py:1145 msgid "Escalation exclusion" msgstr "Vynechání zvýšení priority" -#: helpdesk/models.py:889 +#: third_party/django-helpdesk/helpdesk/models.py:1146 msgid "Escalation exclusions" msgstr "Vynechání zvýšeních priority" -#: helpdesk/models.py:903 +#: third_party/django-helpdesk/helpdesk/models.py:1159 msgid "Template Name" msgstr "Název šablony" -#: helpdesk/models.py:908 +#: third_party/django-helpdesk/helpdesk/models.py:1164 msgid "Subject" msgstr "Předmět" -#: helpdesk/models.py:910 +#: third_party/django-helpdesk/helpdesk/models.py:1166 msgid "" "This will be prefixed with \"[ticket.ticket] ticket.title\". We recommend " "something simple such as \"(Updated\") or \"(Closed)\" - the same context is " @@ -873,11 +941,11 @@ msgstr "" "použít něco jednoduchého, jako např. \"(Aktualizováno\") nebo " "\"(Uzavřeno)\" - stejný obsah je dostupný jako prostý text níže." -#: helpdesk/models.py:916 +#: third_party/django-helpdesk/helpdesk/models.py:1172 msgid "Heading" msgstr "Nadpis" -#: helpdesk/models.py:918 +#: third_party/django-helpdesk/helpdesk/models.py:1174 msgid "" "In HTML e-mails, this will be the heading at the top of the email - the same " "context is available as in plain_text, below." @@ -885,11 +953,11 @@ msgstr "" "V HTML e-mailech bude toto použito zcela na začátku e-mailu - stejný obsah " "je použit jako prostý text níže." -#: helpdesk/models.py:924 +#: third_party/django-helpdesk/helpdesk/models.py:1180 msgid "Plain Text" msgstr "Prostý text" -#: helpdesk/models.py:925 +#: third_party/django-helpdesk/helpdesk/models.py:1181 msgid "" "The context available to you includes {{ ticket }}, {{ queue }}, and " "depending on the time of the call: {{ resolution }} or {{ comment }}." @@ -897,144 +965,208 @@ msgstr "" "Dostupný obsah obsahuje {{ ticket }}, {{ queue }}, a v závislosti na čase " "výzvy: {{ resolution }} nebo {{ comment }}." -#: helpdesk/models.py:931 +#: third_party/django-helpdesk/helpdesk/models.py:1187 msgid "HTML" msgstr "HTML" -#: helpdesk/models.py:932 +#: third_party/django-helpdesk/helpdesk/models.py:1188 msgid "The same context is available here as in plain_text, above." msgstr "Stejný obsah zde jako prostý text jako výše." -#: helpdesk/models.py:940 +#: third_party/django-helpdesk/helpdesk/models.py:1196 msgid "Locale of this template." msgstr "Nastavení Locale této šablony" -#: helpdesk/models.py:948 +#: third_party/django-helpdesk/helpdesk/models.py:1204 msgid "e-mail template" msgstr "šablona pro e-mail" -#: helpdesk/models.py:949 +#: third_party/django-helpdesk/helpdesk/models.py:1205 msgid "e-mail templates" msgstr "šablony pro e-mail" -#: helpdesk/models.py:977 -msgid "Knowledge base category" -msgstr "Katorie znalostní báze" +#: third_party/django-helpdesk/helpdesk/models.py:1232 +msgid "Default queue when creating a ticket after viewing this category." +msgstr "" -#: helpdesk/models.py:978 +#: third_party/django-helpdesk/helpdesk/models.py:1240 +msgid "Knowledge base category" +msgstr "Kategorie znalostní báze" + +#: third_party/django-helpdesk/helpdesk/models.py:1241 msgid "Knowledge base categories" msgstr "Kategorie znalostní báze" -#: helpdesk/models.py:994 helpdesk/templates/helpdesk/public_homepage.html:12 +#: third_party/django-helpdesk/helpdesk/models.py:1264 msgid "Category" msgstr "Kategorie" -#: helpdesk/models.py:1003 +#: third_party/django-helpdesk/helpdesk/models.py:1273 msgid "Question" msgstr "Otázka" -#: helpdesk/models.py:1007 +#: third_party/django-helpdesk/helpdesk/models.py:1277 msgid "Answer" msgstr "Odpověď" -#: helpdesk/models.py:1011 +#: third_party/django-helpdesk/helpdesk/models.py:1281 msgid "Votes" msgstr "Hlasy" -#: helpdesk/models.py:1012 +#: third_party/django-helpdesk/helpdesk/models.py:1282 msgid "Total number of votes cast for this item" msgstr "Celkový počet hlasů pro tuto položku" -#: helpdesk/models.py:1017 +#: third_party/django-helpdesk/helpdesk/models.py:1287 msgid "Positive Votes" msgstr "Pozitivní hlasy" -#: helpdesk/models.py:1018 +#: third_party/django-helpdesk/helpdesk/models.py:1288 msgid "Number of votes for this item which were POSITIVE." msgstr "Počet hlasů pro tuto položku, které byly POZITIVNÍ" -#: helpdesk/models.py:1023 +#: third_party/django-helpdesk/helpdesk/models.py:1293 msgid "Last Updated" msgstr "Naposledy aktualizováno" -#: helpdesk/models.py:1024 +#: third_party/django-helpdesk/helpdesk/models.py:1294 msgid "The date on which this question was most recently changed." msgstr "Datum poslední aktualizace této otázky" -#: helpdesk/models.py:1037 +#: third_party/django-helpdesk/helpdesk/models.py:1307 msgid "Unrated" msgstr "Nehodnoceno" -#: helpdesk/models.py:1045 -msgid "Knowledge base item" -msgstr "Položka znalostní báze" - -#: helpdesk/models.py:1046 +#: third_party/django-helpdesk/helpdesk/models.py:1316 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:174 msgid "Knowledge base items" msgstr "Položky znalostní báze" -#: helpdesk/models.py:1072 helpdesk/templates/helpdesk/ticket_list.html:160 +#: third_party/django-helpdesk/helpdesk/models.py:1351 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:232 msgid "Query Name" msgstr "Název dotazu" -#: helpdesk/models.py:1074 +#: third_party/django-helpdesk/helpdesk/models.py:1353 msgid "User-provided name for this query" msgstr "Název dotazu zadaný uživatelem" -#: helpdesk/models.py:1078 +#: third_party/django-helpdesk/helpdesk/models.py:1357 msgid "Shared With Other Users?" msgstr "Sdílet s dalšími uživateli?" -#: helpdesk/models.py:1081 +#: third_party/django-helpdesk/helpdesk/models.py:1360 msgid "Should other users see this query?" msgstr "Mohou i ostatní uživatelé vidět tento dataz?" -#: helpdesk/models.py:1085 +#: third_party/django-helpdesk/helpdesk/models.py:1364 msgid "Search Query" msgstr "Prohledat dotaz" -#: helpdesk/models.py:1086 +#: third_party/django-helpdesk/helpdesk/models.py:1365 msgid "Pickled query object. Be wary changing this." msgstr "Zabalený objekt dotazu. Toto měňte pouze s nevyšší opatrností." -#: helpdesk/models.py:1096 +#: third_party/django-helpdesk/helpdesk/models.py:1375 msgid "Saved search" msgstr "Uložené hledání" -#: helpdesk/models.py:1097 +#: third_party/django-helpdesk/helpdesk/models.py:1376 msgid "Saved searches" msgstr "Uložená hledání" -#: helpdesk/models.py:1116 -msgid "Settings Dictionary" +#: third_party/django-helpdesk/helpdesk/models.py:1418 +#, fuzzy +#| msgid "Settings Dictionary" +msgid "DEPRECATED! Settings Dictionary DEPRECATED!" msgstr "Slovník nastavení" -#: helpdesk/models.py:1117 +#: third_party/django-helpdesk/helpdesk/models.py:1419 +#, fuzzy +#| msgid "" +#| "This is a base64-encoded representation of a pickled Python dictionary. " +#| "Do not change this field via the admin." msgid "" -"This is a base64-encoded representation of a pickled Python dictionary. Do " -"not change this field via the admin." +"DEPRECATED! This is a base64-encoded representation of a pickled Python " +"dictionary. Do not change this field via the admin." msgstr "" "Toto je reprezentace zabaleného slovníku jazyka Python do base64. Toto pole " "rozhodně neměňte v administrativním rozhraní. " -#: helpdesk/models.py:1156 +#: third_party/django-helpdesk/helpdesk/models.py:1426 +msgid "Show Ticket List on Login?" +msgstr "Zobrazit seznam ticketů po přihlášení?" + +#: third_party/django-helpdesk/helpdesk/models.py:1427 +msgid "Display the ticket list upon login? Otherwise, the dashboard is shown." +msgstr "" +"Zobrazit seznam ticketů po přihlášení? V opačném případě bude zobrazena " +"nástěnka." + +#: third_party/django-helpdesk/helpdesk/models.py:1432 +msgid "E-mail me on ticket change?" +msgstr "Poslat mi e-mail při změně ticketu?" + +#: third_party/django-helpdesk/helpdesk/models.py:1433 +msgid "" +"If you're the ticket owner and the ticket is changed via the web by somebody " +"else, do you want to receive an e-mail?" +msgstr "" +"Přejete si dostat e-mail v případě, pokud jste vlastník ticketu a ticket je " +"změněn prostřednictvím webového formuláře někým dalším." + +#: third_party/django-helpdesk/helpdesk/models.py:1438 +msgid "E-mail me when assigned a ticket?" +msgstr "Poslat e-mail, pokud mi byl přiřazen ticket?" + +#: third_party/django-helpdesk/helpdesk/models.py:1439 +msgid "" +"If you are assigned a ticket via the web, do you want to receive an e-mail?" +msgstr "" +"Přejete si dostat e-mail, pokud Vám byl přiřazen ticket prostřednictvím " +"webového formuláře?" + +#: third_party/django-helpdesk/helpdesk/models.py:1444 +msgid "Number of tickets to show per page" +msgstr "Počet ticketů zobrazených na stránku" + +#: third_party/django-helpdesk/helpdesk/models.py:1445 +msgid "How many tickets do you want to see on the Ticket List page?" +msgstr "Kolik ticketů chcete vidět na stránce Seznam ticketů?" + +#: third_party/django-helpdesk/helpdesk/models.py:1451 +msgid "Use my e-mail address when submitting tickets?" +msgstr "Použít můj e-mail, pokud zadávám nový ticket?" + +#: third_party/django-helpdesk/helpdesk/models.py:1452 +msgid "" +"When you submit a ticket, do you want to automatically use your e-mail " +"address as the submitter address? You can type a different e-mail address " +"when entering the ticket if needed, this option only changes the default." +msgstr "" +"V případě, že zadáte nový ticket, chcete, aby Váš e-mail byl automaticky " +"přiřazen jako e-mail zadavatele? Můžete také zadat jiný e-mail, pokud je to " +"potřeba, tato volba pouze nastaví výchozí hodnotu." + +#: third_party/django-helpdesk/helpdesk/models.py:1463 msgid "User Setting" msgstr "Uživatelskáé nastavení." -#: helpdesk/models.py:1157 helpdesk/templates/helpdesk/navigation.html:71 -#: helpdesk/templates/helpdesk/user_settings.html:6 +#: third_party/django-helpdesk/helpdesk/models.py:1464 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:49 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/user_settings.html:15 msgid "User Settings" msgstr "Uživatelská nastavení" -#: helpdesk/models.py:1185 +#: third_party/django-helpdesk/helpdesk/models.py:1490 msgid "Ignored e-mail address" msgstr "Ignorovaná e-mailová adresa" -#: helpdesk/models.py:1186 +#: third_party/django-helpdesk/helpdesk/models.py:1491 msgid "Ignored e-mail addresses" msgstr "Ignorované e-mailové adresy" -#: helpdesk/models.py:1191 +#: third_party/django-helpdesk/helpdesk/models.py:1496 msgid "" "Leave blank for this e-mail to be ignored on all queues, or select those " "queues you wish to ignore this e-mail for." @@ -1042,11 +1174,11 @@ msgstr "" "Ponechte prázdné pokud chcete, aby byl e-mail ignorován ve všech frontách a " "nebo vyberte fronty, pro které si přejete ignorovat e-mail." -#: helpdesk/models.py:1202 +#: third_party/django-helpdesk/helpdesk/models.py:1507 msgid "Date on which this e-mail address was added" msgstr "Datum přidání e-mailové adresy." -#: helpdesk/models.py:1210 +#: third_party/django-helpdesk/helpdesk/models.py:1515 msgid "" "Enter a full e-mail address, or portions with wildcards, eg *@domain.com or " "postmaster@*." @@ -1054,11 +1186,11 @@ msgstr "" "Přidejte plnou e-mailovou adresu nebo její část pomocí divokých znaků, např. " "*@doména.cz nebo postmaster@*." -#: helpdesk/models.py:1215 +#: third_party/django-helpdesk/helpdesk/models.py:1520 msgid "Save Emails in Mailbox?" msgstr "Ukládat e-maily do Mailboxu?" -#: helpdesk/models.py:1218 +#: third_party/django-helpdesk/helpdesk/models.py:1523 msgid "" "Do you want to save emails from this address in the mailbox? If this is " "unticked, emails from this address will be deleted." @@ -1066,35 +1198,35 @@ msgstr "" "Přejete si ukládat e-maily z této adresy do mailboxu? Pokud nezatrhnete, e-" "maily z této adresy budou mazány." -#: helpdesk/models.py:1286 +#: third_party/django-helpdesk/helpdesk/models.py:1590 msgid "User who wishes to receive updates for this ticket." msgstr "Uživatel, který si přeje být informován o tomto ticketu." -#: helpdesk/models.py:1294 +#: third_party/django-helpdesk/helpdesk/models.py:1598 msgid "For non-user followers, enter their e-mail address" msgstr "Pro neregistrované uživatele zadejte jejich e-mail" -#: helpdesk/models.py:1298 +#: third_party/django-helpdesk/helpdesk/models.py:1602 msgid "Can View Ticket?" msgstr "Může vidět ticket?" -#: helpdesk/models.py:1301 +#: third_party/django-helpdesk/helpdesk/models.py:1605 msgid "Can this CC login to view the ticket details?" msgstr "Může tento uživatel v kopii vidět detaily ticketu?" -#: helpdesk/models.py:1305 +#: third_party/django-helpdesk/helpdesk/models.py:1609 msgid "Can Update Ticket?" msgstr "Může aktualizovat?" -#: helpdesk/models.py:1308 +#: third_party/django-helpdesk/helpdesk/models.py:1612 msgid "Can this CC login and update the ticket?" msgstr "Může tento uživatel v kopii aktualizovat ticket?" -#: helpdesk/models.py:1342 +#: third_party/django-helpdesk/helpdesk/models.py:1645 msgid "Field Name" msgstr "Název pole" -#: helpdesk/models.py:1343 +#: third_party/django-helpdesk/helpdesk/models.py:1646 msgid "" "As used in the database and behind the scenes. Must be unique and consist of " "only lowercase letters with no punctuation." @@ -1102,87 +1234,87 @@ msgstr "" "Bude použito v databázi na pozadí. Musí být unikátní a obsahovat pouze malá " "písmena a žádnou interpunkci." -#: helpdesk/models.py:1349 +#: third_party/django-helpdesk/helpdesk/models.py:1652 msgid "Label" msgstr "Štítek" -#: helpdesk/models.py:1351 +#: third_party/django-helpdesk/helpdesk/models.py:1654 msgid "The display label for this field" msgstr "Zobrazený štítek pro toto pole" -#: helpdesk/models.py:1355 +#: third_party/django-helpdesk/helpdesk/models.py:1658 msgid "Help Text" msgstr "Nápověda" -#: helpdesk/models.py:1356 +#: third_party/django-helpdesk/helpdesk/models.py:1659 msgid "Shown to the user when editing the ticket" msgstr "Zobrazí se uživateli při editaci ticketu" -#: helpdesk/models.py:1362 +#: third_party/django-helpdesk/helpdesk/models.py:1665 msgid "Character (single line)" msgstr "Řetězec znaků (jeden řádek)" -#: helpdesk/models.py:1363 +#: third_party/django-helpdesk/helpdesk/models.py:1666 msgid "Text (multi-line)" msgstr "Text (více řádků)" -#: helpdesk/models.py:1364 +#: third_party/django-helpdesk/helpdesk/models.py:1667 msgid "Integer" msgstr "Celé číslo" -#: helpdesk/models.py:1365 +#: third_party/django-helpdesk/helpdesk/models.py:1668 msgid "Decimal" msgstr "Číslo s plovoucí desetinnou čárkou" -#: helpdesk/models.py:1366 +#: third_party/django-helpdesk/helpdesk/models.py:1669 msgid "List" msgstr "Seznam" -#: helpdesk/models.py:1367 +#: third_party/django-helpdesk/helpdesk/models.py:1670 msgid "Boolean (checkbox yes/no)" msgstr "Pravda/nepravda (zaškrtávátko ano/ne)" -#: helpdesk/models.py:1369 +#: third_party/django-helpdesk/helpdesk/models.py:1672 msgid "Time" msgstr "Čas" -#: helpdesk/models.py:1370 +#: third_party/django-helpdesk/helpdesk/models.py:1673 msgid "Date & Time" msgstr "Datum a čas" -#: helpdesk/models.py:1372 +#: third_party/django-helpdesk/helpdesk/models.py:1675 msgid "URL" msgstr "URL" -#: helpdesk/models.py:1373 +#: third_party/django-helpdesk/helpdesk/models.py:1676 msgid "IP Address" msgstr "IP Adresa" -#: helpdesk/models.py:1378 +#: third_party/django-helpdesk/helpdesk/models.py:1681 msgid "Data Type" msgstr "Datový typ" -#: helpdesk/models.py:1380 +#: third_party/django-helpdesk/helpdesk/models.py:1683 msgid "Allows you to restrict the data entered into this field" msgstr "Umožní omezit vstup tohoto pole" -#: helpdesk/models.py:1385 +#: third_party/django-helpdesk/helpdesk/models.py:1688 msgid "Maximum Length (characters)" msgstr "Maximální počet znaků" -#: helpdesk/models.py:1391 +#: third_party/django-helpdesk/helpdesk/models.py:1694 msgid "Decimal Places" msgstr "Počet desetinných míst" -#: helpdesk/models.py:1392 +#: third_party/django-helpdesk/helpdesk/models.py:1695 msgid "Only used for decimal fields" msgstr "Použito pouze pro pole typu Číslo s plovoucí desetinnou čárkou" -#: helpdesk/models.py:1398 +#: third_party/django-helpdesk/helpdesk/models.py:1701 msgid "Add empty first choice to List?" msgstr "Přiřadit první prázdnou volbu do seznamu?" -#: helpdesk/models.py:1400 +#: third_party/django-helpdesk/helpdesk/models.py:1703 msgid "" "Only for List: adds an empty first entry to the choices list, which enforces " "that the user makes an active choice." @@ -1190,70 +1322,84 @@ msgstr "" "Pouze pro seznamy: přidá prázdnou volbu jako první položku seznamu, což " "uživatele přiměje k aktivnímu výběru některé z možností." -#: helpdesk/models.py:1405 +#: third_party/django-helpdesk/helpdesk/models.py:1708 msgid "List Values" msgstr "Položky seznamu" -#: helpdesk/models.py:1406 +#: third_party/django-helpdesk/helpdesk/models.py:1709 msgid "For list fields only. Enter one option per line." msgstr "Pouze pro pole typu Seznam. Zadejte volby na zvláštní řádek." -#: helpdesk/models.py:1412 +#: third_party/django-helpdesk/helpdesk/models.py:1715 msgid "Ordering" msgstr "Řazení" -#: helpdesk/models.py:1413 +#: third_party/django-helpdesk/helpdesk/models.py:1716 msgid "Lower numbers are displayed first; higher numbers are listed later" msgstr "Nižší hodnoty se zobrazí první; vyšší hodnoty až nakonec" -#: helpdesk/models.py:1427 +#: third_party/django-helpdesk/helpdesk/models.py:1729 msgid "Required?" msgstr "Poviné pole?" -#: helpdesk/models.py:1428 +#: third_party/django-helpdesk/helpdesk/models.py:1730 msgid "Does the user have to enter a value for this field?" msgstr "Musí uživatel toto pole vyplnit?" -#: helpdesk/models.py:1433 +#: third_party/django-helpdesk/helpdesk/models.py:1735 msgid "Staff Only?" msgstr "Pouze pro naše pracovníky?" -#: helpdesk/models.py:1434 +#: third_party/django-helpdesk/helpdesk/models.py:1736 msgid "" "If this is ticked, then the public submission form will NOT show this field" msgstr "" "Pokud je zaškrtnuto, toto pole NEBUDE zobrazeno ve formuláři pro veřejné " "zadávání." -#: helpdesk/models.py:1445 +#: third_party/django-helpdesk/helpdesk/models.py:1747 msgid "Custom field" msgstr "Vlastní pole" -#: helpdesk/models.py:1446 +#: third_party/django-helpdesk/helpdesk/models.py:1748 msgid "Custom fields" msgstr "Vlastní pole" -#: helpdesk/models.py:1470 +#: third_party/django-helpdesk/helpdesk/models.py:1771 msgid "Ticket custom field value" msgstr "Hodnota vlastního pole u ticketu" -#: helpdesk/models.py:1471 +#: third_party/django-helpdesk/helpdesk/models.py:1772 msgid "Ticket custom field values" msgstr "Hodnoty vlastních polí u ticketu" -#: helpdesk/models.py:1483 +#: third_party/django-helpdesk/helpdesk/models.py:1783 msgid "Ticket dependency" msgstr "Vzájemná závislost ticketů" -#: helpdesk/models.py:1484 +#: third_party/django-helpdesk/helpdesk/models.py:1784 msgid "Ticket dependencies" msgstr "Vzájemné závislosti ticketů" -#: helpdesk/models.py:1496 +#: third_party/django-helpdesk/helpdesk/models.py:1796 msgid "Depends On Ticket" msgstr "Závisí na ticketu" -#: helpdesk/templates/helpdesk/attribution.html:2 +#: third_party/django-helpdesk/helpdesk/query.py:201 +msgid "No text" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/query.py:202 +#, fuzzy +#| msgid "View Ticket" +msgid "View ticket" +msgstr "Prohlédnout ticket" + +#: third_party/django-helpdesk/helpdesk/query.py:204 +msgid "Messages" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/attribution.html:6 msgid "" "django-" "helpdesk." @@ -1261,43 +1407,37 @@ msgstr "" "django-" "helpdesk." -#: helpdesk/templates/helpdesk/base.html:18 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/base-head.html:14 msgid "Powered by django-helpdesk" msgstr "Powered by django-helpdesk" -#: helpdesk/templates/helpdesk/base.html:75 -#: helpdesk/templates/helpdesk/rss_list.html:10 -#: helpdesk/templates/helpdesk/rss_list.html:36 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/base-head.html:46 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:21 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:47 msgid "My Open Tickets" msgstr "Mé otevřené tickety" -#: helpdesk/templates/helpdesk/base.html:76 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/base-head.html:47 msgid "All Recent Activity" msgstr "Veškerá poslední aktivita" -#: helpdesk/templates/helpdesk/base.html:77 -#: helpdesk/templates/helpdesk/include/unassigned.html:7 -#: helpdesk/templates/helpdesk/rss_list.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/base-head.html:48 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:27 msgid "Unassigned Tickets" msgstr "Nepřiřazené tickety" -#: helpdesk/templates/helpdesk/base.html:117 -#: helpdesk/templates/helpdesk/navigation.html:12 -#: helpdesk/templates/helpdesk/public_base.html:16 -#: helpdesk/templates/helpdesk/public_base.html:50 -msgid "Helpdesk" -msgstr "Helpdesk" - -#: helpdesk/templates/helpdesk/confirm_delete_saved_query.html:3 -#: helpdesk/templates/helpdesk/ticket_list.html:140 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:207 msgid "Delete Saved Query" msgstr "Smazat Uložený dotaz" -#: helpdesk/templates/helpdesk/confirm_delete_saved_query.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:13 msgid "Delete Query" msgstr "Smazat Dotaz" -#: helpdesk/templates/helpdesk/confirm_delete_saved_query.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:15 #, python-format msgid "" "Are you sure you want to delete this saved filter (%(query_title)s%(ticket_title)s)? All " @@ -1403,19 +1547,20 @@ msgstr "" "Jste si jisti smazáním ticketu (%(ticket_title)s)? Všechny stopy " "ticketu včetně návazností, příloh a aktualizací budou nenávratně odstraněny." -#: helpdesk/templates/helpdesk/edit_ticket.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:12 msgid "Edit Ticket" msgstr "Změnit ticket" -#: helpdesk/templates/helpdesk/edit_ticket.html:9 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:19 msgid "Edit a Ticket" msgstr "Změnit ticket" -#: helpdesk/templates/helpdesk/edit_ticket.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:23 msgid "Note" msgstr "Poznámka" -#: helpdesk/templates/helpdesk/edit_ticket.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:23 msgid "" "Editing a ticket does not send an e-mail to the ticket owner or " "submitter. No new details should be entered, this form should only be used " @@ -1425,17 +1570,18 @@ msgstr "" "zadavateli. Neměly by být vloženy žádné nové detaily. Tento formulář by měl " "být použít pouze pro opravu nesprávných detailů nebo vyčištění zadání.." -#: helpdesk/templates/helpdesk/edit_ticket.html:33 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/edit_ticket.html:43 msgid "Save Changes" msgstr "Uložit změny" -#: helpdesk/templates/helpdesk/email_ignore_add.html:3 -#: helpdesk/templates/helpdesk/email_ignore_add.html:6 -#: helpdesk/templates/helpdesk/email_ignore_add.html:23 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:9 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:30 msgid "Ignore E-Mail Address" msgstr "Ignorovat e-mailové adresy" -#: helpdesk/templates/helpdesk/email_ignore_add.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:15 msgid "" "To ignore an e-mail address and prevent any emails from that address " "creating tickets automatically, enter the e-mail address below." @@ -1443,7 +1589,7 @@ msgstr "" "Pro ignorování e-mailvoé adresy a zajištění, aby z této adresy nebyly " "automaticky vytvářeny žádné tickety, vložte níže tuto adresu." -#: helpdesk/templates/helpdesk/email_ignore_add.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_add.html:17 msgid "" "You can either enter a whole e-mail address such as email@domain.com or a portion of an e-mail address with a wildcard, such as *@domain." @@ -1452,15 +1598,15 @@ msgstr "" "Můžete vložit celou adresu jako např. email@domena.cz nebo použít " "divoké znaky, jako např. *@domena.cz nebo uzivatel@*." -#: helpdesk/templates/helpdesk/email_ignore_del.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_del.html:3 msgid "Delete Ignored E-Mail Address" msgstr "Smazat ignorovanou e-mailovou adresu" -#: helpdesk/templates/helpdesk/email_ignore_del.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_del.html:6 msgid "Un-Ignore E-Mail Address" msgstr "Přestat ignorovat e-mailovou adresu." -#: helpdesk/templates/helpdesk/email_ignore_del.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_del.html:8 #, python-format msgid "" "Are you sure you wish to stop removing this email address (" @@ -1471,20 +1617,20 @@ msgstr "" "automaticky vytvářet tickety ve vašem systému? Tuto adresu můžete samozřejmě " "přidat zpět kdykoliv v budoucnu." -#: helpdesk/templates/helpdesk/email_ignore_del.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_del.html:10 msgid "Keep Ignoring It" msgstr "Pokračovat v ignorování" -#: helpdesk/templates/helpdesk/email_ignore_del.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_del.html:13 msgid "Stop Ignoring It" msgstr "Přestat ignorovat" -#: helpdesk/templates/helpdesk/email_ignore_list.html:3 -#: helpdesk/templates/helpdesk/email_ignore_list.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:15 msgid "Ignored E-Mail Addresses" msgstr "Ignorované e-mailové adresy" -#: helpdesk/templates/helpdesk/email_ignore_list.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:5 msgid "" "\n" "

    Ignored E-Mail Addresses

    \n" @@ -1499,40 +1645,38 @@ msgstr "" "

    Tyto e-maillové adresy jsou procesorem ignorovány. Můžete přidat na " "seznam nové adresy nebo smazat některou z položek níže.

    " -#: helpdesk/templates/helpdesk/email_ignore_list.html:19 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:19 msgid "Add an Email" msgstr "Přidat e-mail" -#: helpdesk/templates/helpdesk/email_ignore_list.html:26 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:26 msgid "Date Added" msgstr "Přidáno datum" -#: helpdesk/templates/helpdesk/email_ignore_list.html:28 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:28 msgid "Keep in mailbox?" msgstr "Ponechat v mailboxu?" -#: helpdesk/templates/helpdesk/email_ignore_list.html:29 -#: helpdesk/templates/helpdesk/email_ignore_list.html:40 -#: helpdesk/templates/helpdesk/include/unassigned.html:33 -#: helpdesk/templates/helpdesk/ticket.html:103 -#: helpdesk/templates/helpdesk/ticket.html:119 -#: helpdesk/templates/helpdesk/ticket_cc_list.html:28 -#: helpdesk/templates/helpdesk/ticket_cc_list.html:37 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:17 -#: helpdesk/templates/helpdesk/ticket_list.html:254 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:29 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:40 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:30 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:38 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:47 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:89 msgid "Delete" msgstr "Smazat" -#: helpdesk/templates/helpdesk/email_ignore_list.html:38 -#: helpdesk/templates/helpdesk/ticket_list.html:249 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:38 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:77 msgid "All" msgstr "Všechny" -#: helpdesk/templates/helpdesk/email_ignore_list.html:39 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:39 msgid "Keep" msgstr "Ponechat" -#: helpdesk/templates/helpdesk/email_ignore_list.html:56 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:56 msgid "" "Note: If the 'Keep' option is not selected, emails sent " "from that address will be deleted permanently." @@ -1540,103 +1684,183 @@ msgstr "" "Poznámka: Pokud volba 'Ponechat' není zatržena, jsou e-" "maily automaticky mazány ze serveru." -#: helpdesk/templates/helpdesk/followup_edit.html:2 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/date.html:4 +msgid "Date (From)" +msgstr "Datum (Od)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/date.html:10 +msgid "Date (To)" +msgstr "Datum (Do)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/date.html:19 +#, fuzzy +#| msgid "Use YYYY-MM-DD date format, eg 2011-05-29" +msgid "Use YYYY-MM-DD date format, eg 2018-01-30" +msgstr "Použijte formát RRRR-MM-DD, např. 2011-05-29" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/kbitems.html:6 +#, fuzzy +#| msgid "Knowledge base items" +msgid "Knowledge base item(s)" +msgstr "Položky znalostní báze" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/kbitems.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/queue.html:18 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/status.html:14 +msgid "Ctrl-click to select multiple options" +msgstr "Ctrl-klik pro výběr více možností" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/keywords.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:172 +msgid "Keywords" +msgstr "Klíčová slova" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/keywords.html:12 +msgid "" +"Keywords are case-insensitive, and will be looked for pretty much everywhere " +"possible. Prepend with 'queue:' or 'priority:' to search by queue or " +"priority. You can also use the keyword OR to combine multiple searches." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/owner.html:6 +msgid "Owner(s)" +msgstr "Vlastník(ci)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/owner.html:12 +msgid "(ME)" +msgstr "(Já)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/owner.html:20 +msgid "Ctrl-Click to select multiple options" +msgstr "Ctrl-Klik pro výběr více voleb" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/queue.html:6 +msgid "Queue(s)" +msgstr "Fronta(y)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:168 +msgid "Sorting" +msgstr "Řazení" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:25 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:175 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:68 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:169 +#: third_party/django-helpdesk/helpdesk/views/staff.py:584 +msgid "Owner" +msgstr "Vlastník" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:30 +msgid "Reverse" +msgstr "Převrátit směr řazení" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:38 +msgid "Ordering applied to tickets" +msgstr "Řazení použité na tickety" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/status.html:6 +msgid "Status(es)" +msgstr "Stav(y)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:2 msgid "Edit followup" msgstr "Editovat návazné" -#: helpdesk/templates/helpdesk/followup_edit.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:20 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:30 msgid "Edit FollowUp" msgstr "Editovat návazné" -#: helpdesk/templates/helpdesk/followup_edit.html:21 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:37 msgid "Reassign ticket:" msgstr "Znovu přiřadit ticket" -#: helpdesk/templates/helpdesk/followup_edit.html:23 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:39 msgid "Title:" msgstr "Nadpis:" -#: helpdesk/templates/helpdesk/followup_edit.html:25 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/followup_edit.html:41 msgid "Comment:" msgstr "Komentář" -#: helpdesk/templates/helpdesk/include/stats.html:7 -msgid "Helpdesk Summary" -msgstr "Souhrn Helpdesku" - -#: helpdesk/templates/helpdesk/include/stats.html:27 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/stats.html:15 msgid "View Tickets" msgstr "Prohlédnout tickety" -#: helpdesk/templates/helpdesk/include/stats.html:27 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/stats.html:15 msgid "No tickets in this range" msgstr "V tomto rozsahu nejsou žádné tickety" -#: helpdesk/templates/helpdesk/include/tickets.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:7 msgid "Your Tickets" msgstr "Vaše Tickety" -#: helpdesk/templates/helpdesk/include/tickets.html:16 -#: helpdesk/templates/helpdesk/include/unassigned.html:16 -#: helpdesk/templates/helpdesk/ticket_list.html:221 -msgid "Pr" -msgstr "Pr" - -#: helpdesk/templates/helpdesk/include/tickets.html:20 -#: helpdesk/templates/helpdesk/kb_category.html:30 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:18 msgid "Last Update" msgstr "Poslední aktualizace" -#: helpdesk/templates/helpdesk/include/tickets.html:34 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:31 msgid "You do not have any pending tickets." msgstr "" -#: helpdesk/templates/helpdesk/include/unassigned.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:7 msgid "(pick up a ticket if you start to work on it)" msgstr "(převzít ticket, pokud na něm začnete pracovat)" -#: helpdesk/templates/helpdesk/include/unassigned.html:32 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:53 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:63 +msgid "Prority" +msgstr "Priorita" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:18 +msgid "Actions" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:29 msgid "Take" msgstr "Převzít" -#: helpdesk/templates/helpdesk/include/unassigned.html:37 -#: helpdesk/templates/helpdesk/report_index.html:54 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/unassigned.html:34 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:61 msgid "There are no unassigned tickets." msgstr "Nejsou žádné nepřiřazené tickety" -#: helpdesk/templates/helpdesk/kb_category.html:4 -msgid "Knowledgebase Category" -msgstr "Katorie znalostní báze" - -#: helpdesk/templates/helpdesk/kb_category.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:2 #, python-format msgid "%(kbcat)s" msgstr "%(kbcat)s" -#: helpdesk/templates/helpdesk/kb_category.html:8 -#, python-format -msgid "You are viewing all items in the %(kbcat)s category." -msgstr "Díváte se na všechny položky kategorie %(kbcat)s" - -#: helpdesk/templates/helpdesk/kb_category.html:26 -#, python-format -msgid "View Answer " -msgstr "" -"Prohlédnout Odpověď " - -#: helpdesk/templates/helpdesk/kb_category.html:29 -msgid "Rating" -msgstr "Hodnocení" - -#: helpdesk/templates/helpdesk/kb_index.html:4 -#: helpdesk/templates/helpdesk/kb_item.html:4 -#: helpdesk/templates/helpdesk/navigation.html:33 -#: helpdesk/templates/helpdesk/navigation.html:101 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:53 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:75 msgid "Knowledgebase" msgstr "Znalostní báze" -#: helpdesk/templates/helpdesk/kb_index.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:2 +msgid "Knowledgebase Category" +msgstr "Kategorie znalostní báze" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:27 +msgid "open tickets" +msgstr "otevřené tickety" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:32 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:46 +msgid "Contact a human" +msgstr "Napište nám" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_category_base.html:36 +#, python-format +msgid "%(recommendations)s people found this answer useful of %(votes)s" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:15 msgid "" "We have listed a number of Knowledgebase articles for your perusal in the " "following categories. Please check to see if any of these articles address " @@ -1646,152 +1870,137 @@ msgstr "" "podívejte se, jestli některé články neřeší Váš problém před tím, než " "otevřete nový ticket" -#: helpdesk/templates/helpdesk/kb_index.html:20 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:26 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:26 msgid "View articles" msgstr "Prohlédnout články" -#: helpdesk/templates/helpdesk/kb_item.html:4 -#, python-format -msgid "%(item)s" -msgstr "%(item)s" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:5 +msgid "Helpdesk" +msgstr "Helpdesk" -#: helpdesk/templates/helpdesk/kb_item.html:17 -msgid "Did you find this article useful?" -msgstr "Pomohl Vám tento článek?" - -#: helpdesk/templates/helpdesk/kb_item.html:28 -msgid "The results of voting by other readers of this article are below:" -msgstr "Výsledky hlasování dalších čtenářů jsou níže" - -#: helpdesk/templates/helpdesk/kb_item.html:30 -#, python-format -msgid "Recommendations: %(recommendations)s" -msgstr "Doporučení: %(recommendations)s" - -#: helpdesk/templates/helpdesk/kb_item.html:31 -#, python-format -msgid "Votes: %(votes)s" -msgstr "Hlasování: %(votes)s" - -#: helpdesk/templates/helpdesk/kb_item.html:32 -#, python-format -msgid "Overall Rating: %(score)s" -msgstr "Celkové hodnocení: %(score)s" - -#: helpdesk/templates/helpdesk/kb_item.html:40 -#, python-format -msgid "" -"View other %(category_title)s articles, or continue viewing other knowledgebase articles." -msgstr "" -"Prohlédnout další %(category_title)s " -"články, nebo pokračovat v prohlížení dalších článků " -"znalostní báze." - -#: helpdesk/templates/helpdesk/navigation.html:7 -msgid "Toggle navigation" -msgstr "" - -#: helpdesk/templates/helpdesk/navigation.html:20 -#: helpdesk/templates/helpdesk/navigation.html:94 -msgid "Dashboard" -msgstr "Nástěnka" - -#: helpdesk/templates/helpdesk/navigation.html:26 -msgid "New Ticket" -msgstr "Nový ticket" - -#: helpdesk/templates/helpdesk/navigation.html:29 -msgid "Stats" -msgstr "Statistiky" - -#: helpdesk/templates/helpdesk/navigation.html:38 -msgid "Saved Query" -msgstr "Uložené dotazy" - -#: helpdesk/templates/helpdesk/navigation.html:53 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:15 msgid "Search..." msgstr "Vyhledávat..." -#: helpdesk/templates/helpdesk/navigation.html:53 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:15 msgid "Enter a keyword, or a ticket number to jump straight to that ticket." msgstr "Vložte klíčové slovo, číslo ticketu nebo skočte rovnou na ticket." -#: helpdesk/templates/helpdesk/navigation.html:57 -#: helpdesk/templates/helpdesk/ticket_list.html:254 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:19 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:99 msgid "Go" msgstr "" -#: helpdesk/templates/helpdesk/navigation.html:73 -#: helpdesk/templates/helpdesk/rss_list.html:3 -#: helpdesk/templates/helpdesk/rss_list.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:50 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:16 msgid "RSS Feeds" msgstr "RSS kanály" -#: helpdesk/templates/helpdesk/navigation.html:75 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:52 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:2 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password_done.html:2 msgid "Change password" msgstr "Změnit heslo" -#: helpdesk/templates/helpdesk/navigation.html:79 -#: helpdesk/templates/helpdesk/system_settings.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:56 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:15 msgid "System Settings" msgstr "Systémová nastavení" -#: helpdesk/templates/helpdesk/navigation.html:82 -#: helpdesk/templates/helpdesk/navigation.html:103 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:59 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:78 msgid "Logout" msgstr "Odhlásit" -#: helpdesk/templates/helpdesk/navigation.html:103 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:66 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:9 +msgid "Dashboard" +msgstr "Nástěnka" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:80 msgid "Log In" msgstr "Přihlásit" -#: helpdesk/templates/helpdesk/public_change_language.html:2 -#: helpdesk/templates/helpdesk/public_homepage.html:74 -#: helpdesk/templates/helpdesk/public_view_form.html:4 -#: helpdesk/templates/helpdesk/public_view_ticket.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:15 +msgid "All Tickets" +msgstr "Všechny tickety" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:21 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:23 +msgid "Saved Queries" +msgstr "Uložené dotazy" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:33 +msgid "" +"No saved queries currently available. You can create one in the All Tickets " +"page." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:40 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:68 +msgid "New Ticket" +msgstr "Nový ticket" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:46 +#, fuzzy +#| msgid "Reports By User" +msgid "Reports" +msgstr "Zprávy na uživatele" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-sidebar.html:62 +msgid "Homepage" +msgstr "Domovská stránka" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_change_language.html:2 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:84 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_form.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:3 msgid "View a Ticket" msgstr "Prohlédnout ticket" -#: helpdesk/templates/helpdesk/public_change_language.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_change_language.html:5 msgid "Change the display language" msgstr "Změnit zobrazený jazyk" -#: helpdesk/templates/helpdesk/public_homepage.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_create_ticket_base.html:12 +msgid "" +"Public ticket submission is disabled. Please contact the administrator for " +"assistance." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:4 +msgid "Welcome to Helpdesk" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:20 msgid "Knowledgebase Articles" msgstr "Články znalostní báze" -#: helpdesk/templates/helpdesk/public_homepage.html:10 -msgid "Knowledgebase Categories" -msgstr "Kategorie znalostní báze" - -#: helpdesk/templates/helpdesk/public_homepage.html:29 -msgid "All fields are required." -msgstr "Všechny položky jsou vyžadovány." - -#: helpdesk/templates/helpdesk/public_homepage.html:67 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:77 msgid "Please use button at upper right to login first." msgstr "Prosím použijte tlačítko vpravo-nahoře pro přihlášení." -#: helpdesk/templates/helpdesk/public_homepage.html:83 -#: helpdesk/templates/helpdesk/public_view_form.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:93 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_form.html:15 msgid "Your E-mail Address" msgstr "Váš e-mail" -#: helpdesk/templates/helpdesk/public_homepage.html:87 -#: helpdesk/templates/helpdesk/public_view_form.html:19 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_homepage.html:97 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_form.html:19 msgid "View Ticket" msgstr "Prohlédnout ticket" -#: helpdesk/templates/helpdesk/public_spam.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_spam.html:4 msgid "Unable To Open Ticket" msgstr "Nelze otevřít ticket" -#: helpdesk/templates/helpdesk/public_spam.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_spam.html:5 msgid "Sorry, but there has been an error trying to submit your ticket." msgstr "Promiňte, při ukládání ticketu se vyskytla chyba." -#: helpdesk/templates/helpdesk/public_spam.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_spam.html:6 msgid "" "Our system has marked your submission as spam, so we are " "unable to save it. If this is not spam, please press back and re-type your " @@ -1803,7 +2012,7 @@ msgstr "" "Pokuste se neznít 'jako spam', např. pokud máte hodně linků, zkuste je " "odstranit pokud je to možné." -#: helpdesk/templates/helpdesk/public_spam.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_spam.html:7 msgid "" "We are sorry for any inconvenience, however this check is required to avoid " "our helpdesk resources being overloaded by spammers." @@ -1811,45 +2020,156 @@ msgstr "" "Omlouváme se za potíže, tato kontrola je nutná, aby nedošlo k zaspamování " "helpdesku." -#: helpdesk/templates/helpdesk/public_view_form.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_form.html:8 msgid "Error:" msgstr "Chyba:" -#: helpdesk/templates/helpdesk/public_view_ticket.html:10 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:10 #, python-format msgid "Queue: %(queue_name)s" msgstr "Fronta: %(queue_name)s" -#: helpdesk/templates/helpdesk/public_view_ticket.html:14 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:47 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:30 msgid "Submitted On" msgstr "Zadáno dne" -#: helpdesk/templates/helpdesk/public_view_ticket.html:36 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:36 msgid "Tags" msgstr "Tagy" -#: helpdesk/templates/helpdesk/public_view_ticket.html:49 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:36 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:49 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:100 msgid "Accept and Close" msgstr "Akceptovat a zavřít" -#: helpdesk/templates/helpdesk/public_view_ticket.html:58 -#: helpdesk/templates/helpdesk/ticket.html:80 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:58 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:38 msgid "Follow-Ups" msgstr "Následovníci" -#: helpdesk/templates/helpdesk/public_view_ticket.html:66 -#: helpdesk/templates/helpdesk/ticket.html:97 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:66 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:53 #, python-format msgid "Changed %(field)s from %(old_value)s to %(new_value)s." msgstr "Změněno %(field)s z %(old_value)s na %(new_value)s." -#: helpdesk/templates/helpdesk/registration/logged_out.html:2 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:84 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:103 +msgid "Use a Pre-set Reply" +msgstr "Použít přednastavenou odpověď" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:84 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:103 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:152 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:160 +msgid "(Optional)" +msgstr "(Volitelné)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:86 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:105 +msgid "" +"Selecting a pre-set reply will over-write your comment below. You can then " +"modify the pre-set reply to your liking before saving this update." +msgstr "" +"Pokud vyberete přednastavenou odpověď, přepíšete Vaše komentáře níže. " +"Přednastavený text můžete následně upravit, než jej uložíte jako aktualizaci." + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:89 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:108 +msgid "Comment / Resolution" +msgstr "Komentáře / usnesení" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:91 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:110 +msgid "" +"You can insert ticket and queue details in your message. For more " +"information, see the context help page." +msgstr "" +"Do zprávy můžete vložit detaily fronty a zprávy. Pro více informací se " +"podívejte na stránku kontextové nápovědy." + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:93 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:113 +msgid "" +"This ticket cannot be resolved or closed until the tickets it depends on are " +"resolved." +msgstr "" +"Tento ticket nelze rozřešit nebo uzavřít dokud nejsou rozřešeny tickety na " +"kterých závisí." + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:128 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:188 +msgid "Attach File(s) »" +msgstr "Přidat soubor(y) »" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:133 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:193 +msgid "Attach a File" +msgstr "Přidat soubor" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:138 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:199 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:261 +msgid "No files selected." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/public_view_ticket.html:147 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:208 +msgid "Update This Ticket" +msgstr "Aktualizovat ticket" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:6 +#, fuzzy +#| msgid "Change password" +msgid "Change Password" +msgstr "Změnit heslo" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:12 +msgid "Please correct the error below." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:12 +msgid "Please correct the errors below." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:17 +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password.html:45 +#, fuzzy +#| msgid "Change password" +msgid "Change my password" +msgstr "Změnit heslo" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password_done.html:6 +msgid "Success!" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/change_password_done.html:8 +msgid "Your password was changed." +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/logged_out.html:2 msgid "Logged Out" msgstr "Odhlášení" -#: helpdesk/templates/helpdesk/registration/logged_out.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/logged_out.html:4 +#, fuzzy +#| msgid "" +#| "\n" +#| "\n" +#| "
    \n" +#| "
    \n" +#| "

    Successfully Logged Out

    \n" +#| "

    Thanks for being here. Hopefully you've helped resolve a " +#| "few tickets and make the world a better place.

    \n" +#| "
    \n" +#| "
    \n" +#| "\n" msgid "" "\n" "\n" @@ -1857,7 +2177,7 @@ msgid "" "
    \n" "

    Successfully Logged Out

    \n" "

    Thanks for being here. Hopefully you've helped resolve a few " -"tickets and make the world a better place.

    \n" +"tickets and made the world a better place.

    \n" "
    \n" "
    \n" "\n" @@ -1873,46 +2193,48 @@ msgstr "" "
    \n" "\n" -#: helpdesk/templates/helpdesk/registration/login.html:2 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/login.html:2 msgid "Helpdesk Login" msgstr "Přihlášení do Helpdesku" -#: helpdesk/templates/helpdesk/registration/login.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/login.html:12 msgid "Please Sign In" msgstr "" -#: helpdesk/templates/helpdesk/registration/login.html:18 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/login.html:16 msgid "Your username and password didn't match. Please try again." msgstr "Uživatelské jméno a heslo nesedí. Zkuste to prosím ještě jednou" -#: helpdesk/templates/helpdesk/registration/login.html:29 -msgid "Remember Me" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/login.html:34 +msgid "Remember Password" msgstr "" -#: helpdesk/templates/helpdesk/registration/login.html:32 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/registration/login.html:38 msgid "Login" msgstr "Přihlášení" -#: helpdesk/templates/helpdesk/report_index.html:3 -#: helpdesk/templates/helpdesk/report_index.html:6 -#: helpdesk/templates/helpdesk/report_output.html:4 -#: helpdesk/templates/helpdesk/report_output.html:29 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:12 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:18 msgid "Reports & Statistics" msgstr "Zprávy a statistiky" -#: helpdesk/templates/helpdesk/report_index.html:9 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:16 msgid "You haven't created any tickets yet, so you cannot run any reports." msgstr "Zatím jste nevytvořili žádné tickety, takže nemůžete mít žádné zprávy." -#: helpdesk/templates/helpdesk/report_index.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:22 msgid "Current Ticket Stats" msgstr "Aktulání stav ticketu" -#: helpdesk/templates/helpdesk/report_index.html:24 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:29 msgid "Average number of days until ticket is closed (all tickets): " msgstr "Průměrný počet dní do zavření ticketu (všechny tickety):" -#: helpdesk/templates/helpdesk/report_index.html:28 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:33 msgid "" "Average number of days until ticket is closed (tickets opened in last 60 " "days): " @@ -1920,70 +2242,71 @@ msgstr "" "Průměrný počet dní do zavření ticketu (tickety otevřené v posledních 60 " "dnech):" -#: helpdesk/templates/helpdesk/report_index.html:29 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:34 msgid "Click" msgstr "Klik" -#: helpdesk/templates/helpdesk/report_index.html:29 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:34 msgid "for detailed average by month." msgstr "pro detailní průměr po měsících" -#: helpdesk/templates/helpdesk/report_index.html:71 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:48 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:160 +msgid "Time spent" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:75 msgid "Generate Report" msgstr "Generovat zprávu" -#: helpdesk/templates/helpdesk/report_index.html:76 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:79 msgid "Reports By User" msgstr "Zprávy na uživatele" -#: helpdesk/templates/helpdesk/report_index.html:78 -#: helpdesk/templates/helpdesk/report_index.html:86 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:81 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:89 msgid "by Priority" msgstr "podle Priority" -#: helpdesk/templates/helpdesk/report_index.html:79 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:82 msgid "by Queue" msgstr "podle Fronty" -#: helpdesk/templates/helpdesk/report_index.html:80 -#: helpdesk/templates/helpdesk/report_index.html:87 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:83 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:90 msgid "by Status" msgstr "podle Stavu" -#: helpdesk/templates/helpdesk/report_index.html:81 -#: helpdesk/templates/helpdesk/report_index.html:88 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:84 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:91 msgid "by Month" msgstr "podle Měsíce" -#: helpdesk/templates/helpdesk/report_index.html:84 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:87 msgid "Reports By Queue" msgstr "Zprávy podle Fronty" -#: helpdesk/templates/helpdesk/report_index.html:89 -#: helpdesk/views/staff.py:1270 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:92 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1228 msgid "Days until ticket closed by Month" msgstr "Počet dní do zavření ticketu podle Měsíce" -#: helpdesk/templates/helpdesk/report_output.html:35 -msgid "Saved Queries" -msgstr "Uložené dotazy" - -#: helpdesk/templates/helpdesk/report_output.html:40 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:27 msgid "" "You can run this query on filtered data by using one of your saved queries." msgstr "" "Tento dotaz můžete pustit na filtrovaná data tak, že použijete některý z " "uložených dotazů." -#: helpdesk/templates/helpdesk/report_output.html:42 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:29 msgid "Select Query:" msgstr "Vybrat dotaz:" -#: helpdesk/templates/helpdesk/report_output.html:47 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:34 msgid "Filter Report" msgstr "Zpráva z filtru" -#: helpdesk/templates/helpdesk/report_output.html:50 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_output.html:37 msgid "" "Want to filter this report to just show a subset of data? Go to the Ticket " "List, filter your query, and save your query." @@ -1991,7 +2314,7 @@ msgstr "" "Potřebujete filtrovat ještě menší podmnožinu dat? Jděte na Seznam ticketů, " "nastavte filtr a uložte jej." -#: helpdesk/templates/helpdesk/rss_list.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:18 msgid "" "The following RSS feeds are available for you to monitor using your " "preferred RSS software. With the exception of the 'Latest Activity' feed, " @@ -2003,7 +2326,7 @@ msgstr "" "otevřených a znovu otevřených případech. To má zajistit, aby Vaše čtečka " "nebyla přeplněna informacemi o zavřených a historických úlohách." -#: helpdesk/templates/helpdesk/rss_list.html:11 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:22 msgid "" "A summary of your open tickets - useful for getting alerted to new tickets " "opened for you" @@ -2011,11 +2334,11 @@ msgstr "" "Přehled Vašich otevřených ticketů - to je užitečné, pokud checete být " "informován o nově otevřených ticketech." -#: helpdesk/templates/helpdesk/rss_list.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:24 msgid "Latest Activity" msgstr "Nejnovější aktivita" -#: helpdesk/templates/helpdesk/rss_list.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:25 msgid "" "A summary of all helpdesk activity - including comments, emails, " "attachments, and more" @@ -2023,7 +2346,7 @@ msgstr "" "Přehled veškeré aktivity na helpdesku - včetně komentářů, e-mailů, příloh a " "dalšího" -#: helpdesk/templates/helpdesk/rss_list.html:17 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:28 msgid "" "All unassigned tickets - useful for being alerted to new tickets opened by " "the public via the web or via e-mail" @@ -2031,7 +2354,7 @@ msgstr "" "Všechny nepřiřazené tickety - užitečné propřehled o nových ticketech " "otevřených prostřednictvím veřejného formuláře nebo e-mailem." -#: helpdesk/templates/helpdesk/rss_list.html:20 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:31 msgid "" "These RSS feeds allow you to view a summary of either your own tickets, or " "all tickets, for each of the queues in your helpdesk. For example, if you " @@ -2043,155 +2366,105 @@ msgstr "" "řídíte Vaše kolegy využíající určitou frontu, tento kanál Vám dá přehled o " "nových přicházejících ticketech." -#: helpdesk/templates/helpdesk/rss_list.html:26 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:37 msgid "Per-Queue Feeds" msgstr "RSS kanály pro každou frontu" -#: helpdesk/templates/helpdesk/rss_list.html:35 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:46 msgid "All Open Tickets" msgstr "Všechny otevřené tickety" -#: helpdesk/templates/helpdesk/system_settings.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:3 msgid "Change System Settings" msgstr "Změnit nastavení systému" -#: helpdesk/templates/helpdesk/system_settings.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:7 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/user_settings.html:7 +#, fuzzy +#| msgid "User Settings" +msgid "Settings" +msgstr "Uživatelská nastavení" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:17 msgid "The following items can be maintained by you or other superusers:" msgstr "Následující položky lze spravovat Vámi nebo dalšími superuživateli:" -#: helpdesk/templates/helpdesk/system_settings.html:11 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:20 msgid "E-Mail Ignore list" msgstr "Seznam ignorovaných e-mailů" -#: helpdesk/templates/helpdesk/system_settings.html:12 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:21 msgid "Maintain Queues" msgstr "Spravovat fronty" -#: helpdesk/templates/helpdesk/system_settings.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:22 msgid "Maintain Pre-Set Replies" msgstr "Spravovat přednastavené odpovědi" -#: helpdesk/templates/helpdesk/system_settings.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:23 msgid "Maintain Knowledgebase Categories" msgstr "Spravovat kategorie znalostní báze" -#: helpdesk/templates/helpdesk/system_settings.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:24 msgid "Maintain Knowledgebase Items" msgstr "Spravovat položky znalostní báze" -#: helpdesk/templates/helpdesk/system_settings.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:25 msgid "Maintain E-Mail Templates" msgstr "Spravovat e-mailové šablony" -#: helpdesk/templates/helpdesk/system_settings.html:17 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:26 msgid "Maintain Users" msgstr "Spravovat uživatele" -#: helpdesk/templates/helpdesk/ticket.html:4 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:7 msgid "View Ticket Details" msgstr "Zobrazit detaily ticketu" -#: helpdesk/templates/helpdesk/ticket.html:44 -#: helpdesk/templates/helpdesk/ticket.html:236 -msgid "No files selected." +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:45 +msgid "time spent" msgstr "" -#: helpdesk/templates/helpdesk/ticket.html:91 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:45 msgid "Private" msgstr "Soukromý" -#: helpdesk/templates/helpdesk/ticket.html:115 -#: helpdesk/templates/helpdesk/ticket_desc_table.html:16 -msgid "Edit" -msgstr "" - -#: helpdesk/templates/helpdesk/ticket.html:140 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:95 msgid "Respond to this ticket" msgstr "Odpovědět na ticket" -#: helpdesk/templates/helpdesk/ticket.html:147 -msgid "Use a Pre-set Reply" -msgstr "Použít přednastavenou odpověď" - -#: helpdesk/templates/helpdesk/ticket.html:149 -msgid "" -"Selecting a pre-set reply will over-write your comment below. You can then " -"modify the pre-set reply to your liking before saving this update." -msgstr "" -"Pokud vyberete přednastavenou odpověď, přepíšete Vaše komentáře níže. " -"Přednastavený text můžete následně upravit, než jej uložíte jako aktualizaci." - -#: helpdesk/templates/helpdesk/ticket.html:152 -msgid "Comment / Resolution" -msgstr "Komentáře / usnesení" - -#: helpdesk/templates/helpdesk/ticket.html:154 -msgid "" -"You can insert ticket and queue details in your message. For more " -"information, see the context help page." -msgstr "" -"Do zprávy můžete vložit detaily fronty a zprávy. Pro více informací se " -"podívejte na stránku kontextové nápovědy." - -#: helpdesk/templates/helpdesk/ticket.html:157 -msgid "" -"This ticket cannot be resolved or closed until the tickets it depends on are " -"resolved." -msgstr "" -"Tento ticket nelze rozřešit nebo uzavřít dokud nejsou rozřešeny tickety na " -"kterých závisí." - -#: helpdesk/templates/helpdesk/ticket.html:196 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:152 msgid "Is this update public?" msgstr "Jedná se o veřejnou aktualizaci?" -#: helpdesk/templates/helpdesk/ticket.html:198 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:154 msgid "Yes, make this update public." msgstr "Ano, ať je tato aktualizace veřejná." -#: helpdesk/templates/helpdesk/ticket.html:199 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:155 msgid "" "If this is public, the submitter will be e-mailed your comment or resolution." msgstr "" "Pokud se jedná o veřejnou aktualizaci, zadavatel dostane e-mail s Vaším " "komentářem nebo rozřešením." -#: helpdesk/templates/helpdesk/ticket.html:203 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:166 msgid "Change Further Details »" msgstr "Změnit další detaily »" -#: helpdesk/templates/helpdesk/ticket.html:212 -#: helpdesk/templates/helpdesk/ticket_list.html:62 -#: helpdesk/templates/helpdesk/ticket_list.html:91 -#: helpdesk/templates/helpdesk/ticket_list.html:227 helpdesk/views/staff.py:539 -msgid "Owner" -msgstr "Vlastník" - -#: helpdesk/templates/helpdesk/ticket.html:213 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:176 msgid "Unassign" msgstr "Odstoupit" -#: helpdesk/templates/helpdesk/ticket.html:225 -msgid "Attach File(s) »" -msgstr "Přidat soubor(y) »" - -#: helpdesk/templates/helpdesk/ticket.html:230 -msgid "Attach a File" -msgstr "Přidat soubor" - -#: helpdesk/templates/helpdesk/ticket.html:233 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:196 msgid "Add Another File" msgstr "Přidat další soubor" -#: helpdesk/templates/helpdesk/ticket.html:245 -msgid "Update This Ticket" -msgstr "Aktualizovat ticket" - -#: helpdesk/templates/helpdesk/ticket_attachment_del.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_attachment_del.html:3 msgid "Delete Ticket Attachment" msgstr "Smazat přílohu ticketu" -#: helpdesk/templates/helpdesk/ticket_attachment_del.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_attachment_del.html:5 #, python-format msgid "" "\n" @@ -2202,23 +2475,37 @@ msgid "" "database, but the attachment itself will still exist on the server.

    \n" msgstr "" -#: helpdesk/templates/helpdesk/ticket_attachment_del.html:11 -#: helpdesk/templates/helpdesk/ticket_cc_del.html:12 -#: helpdesk/templates/helpdesk/ticket_dependency_del.html:11 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_attachment_del.html:11 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:25 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:21 msgid "Don't Delete" msgstr "Neodstraňovat" -#: helpdesk/templates/helpdesk/ticket_attachment_del.html:14 -#: helpdesk/templates/helpdesk/ticket_dependency_del.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_attachment_del.html:14 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:24 msgid "Yes, I Understand - Delete" msgstr "" -#: helpdesk/templates/helpdesk/ticket_cc_add.html:3 -#: helpdesk/templates/helpdesk/ticket_cc_add.html:6 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:19 msgid "Add Ticket CC" msgstr "Přidat CC k ticketu" -#: helpdesk/templates/helpdesk/ticket_cc_add.html:12 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:13 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:12 +#, fuzzy +#| msgid "Ticket CC Settings" +msgid "CC Settings" +msgstr "Nastavení CC k ticketu" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:15 +#, fuzzy +#| msgid "Add Ticket CC" +msgid "Add CC" +msgstr "Přidat CC k ticketu" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:24 msgid "" "To automatically send an email to a user or e-mail address when this ticket " "is updated, select the user or enter an e-mail address below." @@ -2226,28 +2513,34 @@ msgstr "" "Pro automatické zaslání zprávy uživateli nebo na e-mail pokaždé, když je " "ticket aktualizován, vyberte uživatele nebo zadejte jeho adresu níže." -#: helpdesk/templates/helpdesk/ticket_cc_add.html:18 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:29 msgid "Email" msgstr "" -#: helpdesk/templates/helpdesk/ticket_cc_add.html:27 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:38 msgid "Add Email" msgstr "" -#: helpdesk/templates/helpdesk/ticket_cc_add.html:37 -#: helpdesk/templates/helpdesk/ticket_cc_add.html:51 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:48 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:62 msgid "Save Ticket CC" msgstr "Uložit CC k ticketu" -#: helpdesk/templates/helpdesk/ticket_cc_add.html:41 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_add.html:52 msgid "Add User" msgstr "Přidat uživatele" -#: helpdesk/templates/helpdesk/ticket_cc_del.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:3 msgid "Delete Ticket CC" msgstr "Odhlásit odběr CC ticketu" -#: helpdesk/templates/helpdesk/ticket_cc_del.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:15 +#, fuzzy +#| msgid "Delete" +msgid "Delete CC" +msgstr "Smazat" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:18 #, python-format msgid "" "\n" @@ -2263,15 +2556,15 @@ msgstr "" "

    Určitě chcete odstranit CC (%(email_address)s) z ticketu? Na " "daný e-mail přestanou chodit oznámení o aktualizacích.

    \n" -#: helpdesk/templates/helpdesk/ticket_cc_del.html:15 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_del.html:28 msgid "Yes I Understand - Delete" msgstr "Ano rozumím - Odstranit" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:3 msgid "Ticket CC Settings" msgstr "Nastavení CC k ticketu" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:15 #, python-format msgid "" "\n" @@ -2294,36 +2587,37 @@ msgstr "" "

    Můžete přidat novou adresu na seznam nebo smazat některou z položek níže." "

    " -#: helpdesk/templates/helpdesk/ticket_cc_list.html:16 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:26 msgid "Ticket CC List" msgstr "Seznam CC pro ticket" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:20 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:30 msgid "Add an Email or Helpdesk User" msgstr "" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:25 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:35 msgid "E-Mail Address or Helpdesk User" msgstr "Adresa e-mailu nebo uživatel Helpdesku" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:26 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:36 msgid "View?" msgstr "Prohlédnout?" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:27 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:37 msgid "Update?" msgstr "Aktualizovat?" -#: helpdesk/templates/helpdesk/ticket_cc_list.html:53 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:63 #, python-format msgid "Return to %(ticket_title)s" msgstr "Návrat do %(ticket_title)s" -#: helpdesk/templates/helpdesk/ticket_dependency_add.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_add.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_add.html:12 msgid "Add Ticket Dependency" msgstr "Přidat závislost" -#: helpdesk/templates/helpdesk/ticket_dependency_add.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_add.html:15 msgid "" "\n" "

    Add Ticket Dependency

    \n" @@ -2337,15 +2631,16 @@ msgstr "" "

    Přidáním závislosti Vám znemožní ticket rozřešit, dokue nebudou všechny " "podřízené tickety rozřešeny nebo uzavřeny

    " -#: helpdesk/templates/helpdesk/ticket_dependency_add.html:21 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_add.html:31 msgid "Save Ticket Dependency" msgstr "Uložit závislost" -#: helpdesk/templates/helpdesk/ticket_dependency_del.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:12 msgid "Delete Ticket Dependency" msgstr "Smazat závislost" -#: helpdesk/templates/helpdesk/ticket_dependency_del.html:5 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_dependency_del.html:15 msgid "" "\n" "

    Delete Ticket Dependency

    \n" @@ -2357,41 +2652,37 @@ msgstr "" "\n" "

    Jste si jisti odstraněním závislosti na ticketu?

    \n" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:8 -msgid "Ticket Summary" -msgstr "Souhrn ticketu" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:18 -msgid "Unhold" -msgstr "Aktivovat" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:18 -msgid "Hold" -msgstr "Pozdržet" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:20 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:13 #, python-format msgid "Queue: %(queue)s" msgstr "Fronta: %(queue)s" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:43 -#: helpdesk/templates/helpdesk/ticket_list.html:226 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:15 +msgid "Edit" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:17 +msgid "Unhold" +msgstr "Aktivovat" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:17 +msgid "Hold" +msgstr "Pozdržet" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:27 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:67 msgid "Due Date" msgstr "Datum do kdy" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:52 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:34 msgid "Assigned To" msgstr "Přiřazeno" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:58 -msgid "Ignore" -msgstr "Ingorovat" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:67 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:52 msgid "Copies To" msgstr "Kopie na" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:68 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:53 msgid "" "Click here to add / remove people who should receive an e-mail whenever this " "ticket is updated." @@ -2399,11 +2690,7 @@ msgstr "" "Pro přidání a nebo odebrání lidí, kteří by měli dostávat e-mail kdykoliv je " "ticket aktualizován, klikněte zde." -#: helpdesk/templates/helpdesk/ticket_desc_table.html:68 -msgid "Manage" -msgstr "Spravovat" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:68 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:53 msgid "" "Click here to subscribe yourself to this ticket, if you want to receive an e-" "mail whenever this ticket is updated." @@ -2411,29 +2698,11 @@ msgstr "" "Klikněte zde pro vaše přihlášení k tomuto ticketu, pokud chcete dostávat " "kopie e-mailů kdykoliv je ticket aktualizován." -#: helpdesk/templates/helpdesk/ticket_desc_table.html:68 -msgid "Subscribe" -msgstr "Přihlásit" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:72 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:57 msgid "Dependencies" msgstr "Závislosti" -#: helpdesk/templates/helpdesk/ticket_desc_table.html:74 -msgid "" -"This ticket cannot be resolved until the following ticket(s) are resolved" -msgstr "" -"Tento ticket nelze rozřešit, dokud nejsou rozřešeny následující tickety" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:75 -msgid "Remove Dependency" -msgstr "Odstranit závislost" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:78 -msgid "This ticket has no dependencies." -msgstr "Tento ticket nemá žádnou závislost" - -#: helpdesk/templates/helpdesk/ticket_desc_table.html:80 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:59 msgid "" "Click on 'Add Dependency', if you want to make this ticket dependent on " "another ticket. A ticket may not be closed until all tickets it depends on " @@ -2443,100 +2712,123 @@ msgstr "" "'Přidat závislost'. Ticket pak nebude možné uzavřít, dokud nebudou uzavřeny " "všechny tickety na kterých závisí." -#: helpdesk/templates/helpdesk/ticket_desc_table.html:80 -msgid "Add Dependency" -msgstr "Přidat závislost" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:61 +msgid "" +"This ticket cannot be resolved until the following ticket(s) are resolved" +msgstr "" +"Tento ticket nelze rozřešit, dokud nejsou rozřešeny následující tickety" -#: helpdesk/templates/helpdesk/ticket_list.html:13 -msgid "No Tickets Match Your Selection" -msgstr "Vašemu výběru neodpovídá žádný ticket." +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:65 +msgid "This ticket has no dependencies." +msgstr "Tento ticket nemá žádnou závislost" -#: helpdesk/templates/helpdesk/ticket_list.html:46 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:68 +msgid "Total time spent" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:73 +#, fuzzy +#| msgid "Knowledge base item" +msgid "Knowlegebase item" +msgstr "Položka znalostní báze" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:107 +#, fuzzy +#| msgid "Edit Ticket" +msgid "Edit details" +msgstr "Změnit ticket" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:22 +msgid "Saved Query" +msgstr "Uložené dotazy" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:37 +msgid "Query Results" +msgstr "Výsledek dotazu" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:42 +msgid "Table" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:48 +#, fuzzy +#| msgid "Time" +msgid "Timeline" +msgstr "Čas" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:69 +msgid "Time Spent" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:74 +msgid "Select:" +msgstr "Vybrat" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:82 +msgid "Invert" +msgstr "Obrátit" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:86 +msgid "With Selected Tickets:" +msgstr "Provézt s vybranými tickety:" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:88 +msgid "Take (Assign to me)" +msgstr "Převízt (přiřadit mě)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:90 +msgid "Close" +msgstr "Uzavřít" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:91 +msgid "Close (Don't Send E-Mail)" +msgstr "Uzavřít (bez odeslání e-mailu)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:92 +msgid "Close (Send E-Mail)" +msgstr "Uzavřít (a odeslat e-mail)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:94 +msgid "Assign To" +msgstr "Přiřadit" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:95 +msgid "Nobody (Unassign)" +msgstr "Nikomu (odstranit přiřazení)" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:145 msgid "Query Selection" msgstr "Výběr" -#: helpdesk/templates/helpdesk/ticket_list.html:54 -msgid "Change Query" -msgstr "Změnit dotaz" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:155 +#, fuzzy +#| msgid "Apply Filter" +msgid "Filters" +msgstr "Použit filtr" -#: helpdesk/templates/helpdesk/ticket_list.html:61 -#: helpdesk/templates/helpdesk/ticket_list.html:73 -msgid "Sorting" -msgstr "Řazení" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:164 +#, fuzzy +#| msgid "Add User" +msgid "Add filter" +msgstr "Přidat uživatele" -#: helpdesk/templates/helpdesk/ticket_list.html:65 -#: helpdesk/templates/helpdesk/ticket_list.html:133 -msgid "Keywords" -msgstr "Klíčová slova" - -#: helpdesk/templates/helpdesk/ticket_list.html:66 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:173 msgid "Date Range" msgstr "Časový rozsah" -#: helpdesk/templates/helpdesk/ticket_list.html:94 -msgid "Reverse" -msgstr "Převrátit směr řazení" - -#: helpdesk/templates/helpdesk/ticket_list.html:96 -msgid "Ordering applied to tickets" -msgstr "Řazení použité na tickety" - -#: helpdesk/templates/helpdesk/ticket_list.html:101 -msgid "Owner(s)" -msgstr "Vlastník(ci)" - -#: helpdesk/templates/helpdesk/ticket_list.html:105 -msgid "(ME)" -msgstr "(Já)" - -#: helpdesk/templates/helpdesk/ticket_list.html:109 -msgid "Ctrl-Click to select multiple options" -msgstr "Ctrl-Klik pro výběr více voleb" - -#: helpdesk/templates/helpdesk/ticket_list.html:114 -msgid "Queue(s)" -msgstr "Fronta(y)" - -#: helpdesk/templates/helpdesk/ticket_list.html:115 -#: helpdesk/templates/helpdesk/ticket_list.html:121 -msgid "Ctrl-click to select multiple options" -msgstr "Ctrl-klik pro výběr více možností" - -#: helpdesk/templates/helpdesk/ticket_list.html:120 -msgid "Status(es)" -msgstr "Stav(y)" - -#: helpdesk/templates/helpdesk/ticket_list.html:126 -msgid "Date (From)" -msgstr "Datum (Od)" - -#: helpdesk/templates/helpdesk/ticket_list.html:127 -msgid "Date (To)" -msgstr "Datum (Do)" - -#: helpdesk/templates/helpdesk/ticket_list.html:128 -msgid "Use YYYY-MM-DD date format, eg 2011-05-29" -msgstr "Použijte formát RRRR-MM-DD, např. 2011-05-29" - -#: helpdesk/templates/helpdesk/ticket_list.html:134 -msgid "" -"Keywords are case-insensitive, and will be looked for in the title, body and " -"submitter fields." -msgstr "" -"Klíčová slova mohou být napsána velkým i malým písmem. Prohledává se nadpis, " -"tělo ticketu a zadavatel." - -#: helpdesk/templates/helpdesk/ticket_list.html:138 -msgid "Apply Filter" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:205 +#, fuzzy +#| msgid "Apply Filter" +msgid "Apply Filters" msgstr "Použit filtr" -#: helpdesk/templates/helpdesk/ticket_list.html:140 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:207 #, python-format msgid "" "You are currently viewing saved query \"%(query_name)s\"." msgstr "Prohlížíte uložený výběr \"%(query_name)s\"." -#: helpdesk/templates/helpdesk/ticket_list.html:143 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:210 #, python-format msgid "" "Run a report on this " @@ -2545,12 +2837,12 @@ msgstr "" "Spustit hlášení na tento " "výběr a uvidíte statistiku a grafy pro níže vypsaná data." -#: helpdesk/templates/helpdesk/ticket_list.html:152 -#: helpdesk/templates/helpdesk/ticket_list.html:170 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:223 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:242 msgid "Save Query" msgstr "Uložit výběr" -#: helpdesk/templates/helpdesk/ticket_list.html:162 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:234 msgid "" "This name appears in the drop-down list of saved queries. If you share your " "query, other users will see this name, so choose something clear and " @@ -2560,15 +2852,15 @@ msgstr "" "výběr sdílet, uvidí tento název i ostatní uživatelé, takže zvolte něco na " "první pohled jasného!" -#: helpdesk/templates/helpdesk/ticket_list.html:164 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:236 msgid "Shared?" msgstr "Sdíleno?" -#: helpdesk/templates/helpdesk/ticket_list.html:165 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:237 msgid "Yes, share this query with other users." msgstr "Ano, sdílet tento výběr s dalšími uživateli." -#: helpdesk/templates/helpdesk/ticket_list.html:166 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:238 msgid "" "If you share this query, it will be visible by all other logged-in " "users." @@ -2576,63 +2868,27 @@ msgstr "" "Pokud budete tento výběr sdílet, bude viditelný všemi dalšími " "přihlášenými uživateli." -#: helpdesk/templates/helpdesk/ticket_list.html:179 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:254 msgid "Use Saved Query" msgstr "Použít uložený výběr" -#: helpdesk/templates/helpdesk/ticket_list.html:185 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:261 msgid "Query" msgstr "Výběr" -#: helpdesk/templates/helpdesk/ticket_list.html:190 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:266 msgid "Run Query" msgstr "Spustit výběr" -#: helpdesk/templates/helpdesk/ticket_list.html:210 -msgid "Query Results" -msgstr "Výsledek dotazu" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:293 +msgid "No Tickets Match Your Selection" +msgstr "Vašemu výběru neodpovídá žádný ticket." -#: helpdesk/templates/helpdesk/ticket_list.html:248 -msgid "Select:" -msgstr "Vybrat" - -#: helpdesk/templates/helpdesk/ticket_list.html:251 -msgid "Invert" -msgstr "Obrátit" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "With Selected Tickets:" -msgstr "Provézt s vybranými tickety:" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Take (Assign to me)" -msgstr "Převízt (přiřadit mě)" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Close" -msgstr "Uzavřít" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Close (Don't Send E-Mail)" -msgstr "Uzavřít (bez odeslání e-mailu)" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Close (Send E-Mail)" -msgstr "Uzavřít (a odeslat e-mail)" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Assign To" -msgstr "Přiřadit" - -#: helpdesk/templates/helpdesk/ticket_list.html:254 -msgid "Nobody (Unassign)" -msgstr "Nikomu (odstranit přiřazení)" - -#: helpdesk/templates/helpdesk/user_settings.html:3 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/user_settings.html:3 msgid "Change User Settings" msgstr "Změnit uživatelská nastevení" -#: helpdesk/templates/helpdesk/user_settings.html:8 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/user_settings.html:17 msgid "" "Use the following options to change the way your helpdesk system works for " "you. These settings do not impact any other user." @@ -2640,43 +2896,39 @@ msgstr "" "Zvolte následující možnosti pro upravení chování helpdesku. Tato Vaše " "nastavení se neprojeví ostatním uživatelům." -#: helpdesk/templates/helpdesk/user_settings.html:13 -msgid "Save Options" -msgstr "Uložit změny" - -#: helpdesk/views/feeds.py:37 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:37 #, python-format msgid "Helpdesk: Open Tickets in queue %(queue)s for %(username)s" msgstr "Helpdesk: Otevřít tickety ve frontě %(queue)s pro %(username)s" -#: helpdesk/views/feeds.py:42 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:42 #, python-format msgid "Helpdesk: Open Tickets for %(username)s" msgstr "Helpdesk: Otevřít tickety pro %(username)s" -#: helpdesk/views/feeds.py:48 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:48 #, python-format msgid "Open and Reopened Tickets in queue %(queue)s for %(username)s" msgstr "Otevřít a znovu otevřít tickety ve frontě %(queue)s pro %(username)s" -#: helpdesk/views/feeds.py:53 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:53 #, python-format msgid "Open and Reopened Tickets for %(username)s" msgstr "Otevřít a znovu otevřít tickety pro %(username)s" -#: helpdesk/views/feeds.py:100 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:100 msgid "Helpdesk: Unassigned Tickets" msgstr "Helpdesk: Nepřiřazené tickety" -#: helpdesk/views/feeds.py:101 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:101 msgid "Unassigned Open and Reopened tickets" msgstr "Nepřiřazené Otevřené a Znovu otevřené tickety" -#: helpdesk/views/feeds.py:125 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:125 msgid "Helpdesk: Recent Followups" msgstr "Helpdesk: Nejčerstvější návazné" -#: helpdesk/views/feeds.py:126 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:126 msgid "" "Recent FollowUps, such as e-mail replies, comments, attachments and " "resolutions" @@ -2684,55 +2936,63 @@ msgstr "" "Nejčerstvější návazné akce, jako jsou odpovědi z e-mailů, komentáře, přílohy " "a rozřešení" -#: helpdesk/views/feeds.py:141 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:141 #, python-format msgid "Helpdesk: Open Tickets in queue %(queue)s" msgstr "Helpdesk: Otevřít tickety ve frontě %(queue)s" -#: helpdesk/views/feeds.py:146 +#: third_party/django-helpdesk/helpdesk/views/feeds.py:146 #, python-format msgid "Open and Reopened Tickets in queue %(queue)s" msgstr "Otevřít a znovu otevřít tickety ve frontě %(queue)s" -#: helpdesk/views/public.py:112 helpdesk/views/public.py:114 -msgid "Invalid ticket ID or e-mail address. Please try again." -msgstr "Špatné ID ticketu nebo e-mailová adresa. Zkuste to znovu prosím." - -#: helpdesk/views/public.py:130 -msgid "Submitter accepted resolution and closed ticket" -msgstr "Zadavatel akceptoval rozřešení a uzavřel ticket" - -#: helpdesk/views/public.py:151 +#: third_party/django-helpdesk/helpdesk/views/public.py:140 msgid "Missing ticket ID or e-mail address. Please try again." msgstr "Chybí ID ticketu nebo e-mailová adresa. Zkuste to znovu prosím." -#: helpdesk/views/staff.py:317 +#: third_party/django-helpdesk/helpdesk/views/public.py:149 +msgid "Invalid ticket ID or e-mail address. Please try again." +msgstr "Špatné ID ticketu nebo e-mailová adresa. Zkuste to znovu prosím." + +#: third_party/django-helpdesk/helpdesk/views/public.py:165 +msgid "Submitter accepted resolution and closed ticket" +msgstr "Zadavatel akceptoval rozřešení a uzavřel ticket" + +#: third_party/django-helpdesk/helpdesk/views/staff.py:306 msgid "Accepted resolution and closed ticket" msgstr "Rozřešení akceptováno a ticket uzavřen." -#: helpdesk/views/staff.py:485 +#: third_party/django-helpdesk/helpdesk/views/staff.py:394 +#, python-format +msgid "" +"When you add somebody on Cc, you must provide either a User or a valid " +"email. Email: %s" +msgstr "" + +#: third_party/django-helpdesk/helpdesk/views/staff.py:528 #, python-format msgid "Assigned to %(username)s" msgstr "Přiřazeno %(username)s" -#: helpdesk/views/staff.py:511 +#: third_party/django-helpdesk/helpdesk/views/staff.py:554 msgid "Updated" msgstr "Aktualizováno" -#: helpdesk/views/staff.py:711 +#: third_party/django-helpdesk/helpdesk/views/staff.py:723 #, python-format msgid "Assigned to %(username)s in bulk update" msgstr "Přiřazeno %(username)s v rámci hromadné aktualizace" -#: helpdesk/views/staff.py:722 +#: third_party/django-helpdesk/helpdesk/views/staff.py:734 msgid "Unassigned in bulk update" msgstr "Vlastník odstraněn v rámci hromadné aktualizace" -#: helpdesk/views/staff.py:731 helpdesk/views/staff.py:741 +#: third_party/django-helpdesk/helpdesk/views/staff.py:743 +#: third_party/django-helpdesk/helpdesk/views/staff.py:753 msgid "Closed in bulk update" msgstr "Uzavřeno v rámci hromadné aktualizace" -#: helpdesk/views/staff.py:963 +#: third_party/django-helpdesk/helpdesk/views/staff.py:907 msgid "" "

    Note: Your keyword search is case sensitive because of " "your database. This means the search will not be accurate. " @@ -2749,42 +3009,127 @@ msgstr "" "#sqlite-string-matching\">Dokumentaci k Djangu o vyhledávání textu v SQLite." -#: helpdesk/views/staff.py:1085 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1057 msgid "Ticket taken off hold" msgstr "Ticket znovu aktivní" -#: helpdesk/views/staff.py:1088 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1060 msgid "Ticket placed on hold" msgstr "Ticket pozdržen" -#: helpdesk/views/staff.py:1227 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1185 msgid "User by Priority" msgstr "Uživatelé podle priority" -#: helpdesk/views/staff.py:1233 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1191 msgid "User by Queue" msgstr "Uživatelé podle fronty" -#: helpdesk/views/staff.py:1240 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1198 msgid "User by Status" msgstr "Uživatelé podle stavu" -#: helpdesk/views/staff.py:1246 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1204 msgid "User by Month" msgstr "Uživatel podle měsíce" -#: helpdesk/views/staff.py:1252 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1210 msgid "Queue by Priority" msgstr "Fronta podle priority" -#: helpdesk/views/staff.py:1258 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1216 msgid "Queue by Status" msgstr "Fronta podle stavu" -#: helpdesk/views/staff.py:1264 +#: third_party/django-helpdesk/helpdesk/views/staff.py:1222 msgid "Queue by Month" msgstr "Fronta podle měsíce" +#~ msgid "Helpdesk Summary" +#~ msgstr "Souhrn Helpdesku" + +#~ msgid "Pr" +#~ msgstr "Pr" + +#~ msgid "You are viewing all items in the %(kbcat)s category." +#~ msgstr "Díváte se na všechny položky kategorie %(kbcat)s" + +#~ msgid "" +#~ "View Answer " +#~ msgstr "" +#~ "Prohlédnout Odpověď " + +#~ msgid "Rating" +#~ msgstr "Hodnocení" + +#~ msgid "%(item)s" +#~ msgstr "%(item)s" + +#~ msgid "Did you find this article useful?" +#~ msgstr "Pomohl Vám tento článek?" + +#~ msgid "The results of voting by other readers of this article are below:" +#~ msgstr "Výsledky hlasování dalších čtenářů jsou níže" + +#~ msgid "Recommendations: %(recommendations)s" +#~ msgstr "Doporučení: %(recommendations)s" + +#~ msgid "Votes: %(votes)s" +#~ msgstr "Hlasování: %(votes)s" + +#~ msgid "Overall Rating: %(score)s" +#~ msgstr "Celkové hodnocení: %(score)s" + +#~ msgid "" +#~ "View other %(category_title)s " +#~ "articles, or continue viewing other knowledgebase " +#~ "articles." +#~ msgstr "" +#~ "Prohlédnout další %(category_title)s " +#~ "články, nebo pokračovat v prohlížení dalších článků " +#~ "znalostní báze." + +#~ msgid "Stats" +#~ msgstr "Statistiky" + +#~ msgid "Knowledgebase Categories" +#~ msgstr "Kategorie znalostní báze" + +#~ msgid "All fields are required." +#~ msgstr "Všechny položky jsou vyžadovány." + +#~ msgid "Ticket Summary" +#~ msgstr "Souhrn ticketu" + +#~ msgid "Ignore" +#~ msgstr "Ingorovat" + +#~ msgid "Manage" +#~ msgstr "Spravovat" + +#~ msgid "Subscribe" +#~ msgstr "Přihlásit" + +#~ msgid "Remove Dependency" +#~ msgstr "Odstranit závislost" + +#~ msgid "Add Dependency" +#~ msgstr "Přidat závislost" + +#~ msgid "Change Query" +#~ msgstr "Změnit dotaz" + +#~ msgid "" +#~ "Keywords are case-insensitive, and will be looked for in the title, body " +#~ "and submitter fields." +#~ msgstr "" +#~ "Klíčová slova mohou být napsána velkým i malým písmem. Prohledává se " +#~ "nadpis, tělo ticketu a zadavatel." + +#~ msgid "Save Options" +#~ msgstr "Uložit změny" + #~ msgid "Description of Issue" #~ msgstr "Popis problému" @@ -2862,9 +3207,6 @@ msgstr "Fronta podle měsíce" #~ msgid "Accept" #~ msgstr "Akceptovat" -#~ msgid "Open Tickets" -#~ msgstr "Otevřené tickety" - #~ msgid "Attach another File" #~ msgstr "Přiřadit další soubor" From aea091b30cdc9ff9ee78079b84c12f6951118ea8 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 10 Jan 2020 18:04:33 +0100 Subject: [PATCH 34/67] Remove duplicate entries from django ticket query --- helpdesk/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/query.py b/helpdesk/query.py index a2fbe16e..0ee6373c 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -133,7 +133,7 @@ class __Query__: if sortreverse: sorting = "-%s" % sorting queryset = queryset.order_by(sorting) - return queryset + return queryset.distinct() # https://stackoverflow.com/questions/30487056/django-queryset-contains-duplicate-entries def get_cache_key(self): return str(self.huser.user.pk) + ":" + self.base64 From ebd876d0e27c8386b0e473960bd719f2c67f5d27 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 13 Jan 2020 15:43:33 +0100 Subject: [PATCH 35/67] Remove deprecated ticket_list_table.html file --- .../templates/helpdesk/ticket_list_table.html | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 helpdesk/templates/helpdesk/ticket_list_table.html diff --git a/helpdesk/templates/helpdesk/ticket_list_table.html b/helpdesk/templates/helpdesk/ticket_list_table.html deleted file mode 100644 index 6b4dfe74..00000000 --- a/helpdesk/templates/helpdesk/ticket_list_table.html +++ /dev/null @@ -1,18 +0,0 @@ -{% load i18n humanize %} - - - {% for ticket in tickets %} - - {{ ticket.ticket }} - - {{ ticket.priority }}||||| - {{ ticket.title }} - {{ ticket.queue }} - {{ ticket.get_status }} - {{ ticket.created|naturaltime }} - {{ ticket.due_date|naturaltime }} - {{ ticket.get_assigned_to }} - {{ ticket.time_spent_formated }} - - {% endfor %} - From 5da7069dbdd28f5e9c85521dbc278464741681a9 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 13 Jan 2020 15:43:47 +0100 Subject: [PATCH 36/67] Add submitter email to ticket list --- helpdesk/serializers.py | 6 +++++- helpdesk/templates/helpdesk/ticket_list.html | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/helpdesk/serializers.py b/helpdesk/serializers.py index ef5ed27d..469fc92e 100644 --- a/helpdesk/serializers.py +++ b/helpdesk/serializers.py @@ -15,6 +15,7 @@ datatables for ticket_list.html. Called from staff.datatables_ticket_list. class DatatablesTicketSerializer(serializers.ModelSerializer): ticket = serializers.SerializerMethodField() assigned_to = serializers.SerializerMethodField() + submitter = serializers.SerializerMethodField() created = serializers.SerializerMethodField() due_date = serializers.SerializerMethodField() status = serializers.SerializerMethodField() @@ -26,7 +27,7 @@ class DatatablesTicketSerializer(serializers.ModelSerializer): model = Ticket # fields = '__all__' fields = ('ticket', 'id', 'priority', 'title', 'queue', 'status', - 'created', 'due_date', 'assigned_to', 'row_class', + 'created', 'due_date', 'assigned_to', 'submitter', 'row_class', 'time_spent') def get_queue(self, obj): @@ -53,6 +54,9 @@ class DatatablesTicketSerializer(serializers.ModelSerializer): else: return ("None") + def get_submitter(self, obj): + return obj.submitter_email + def get_time_spent(self, obj): return format_time_spent(obj.time_spent) diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 03be65e2..8cdfd0b5 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -66,6 +66,7 @@ {% trans "Created" %} {% trans "Due Date" %} {% trans "Owner" %} + {% trans "Submitter" %} {% trans "Time Spent" %} @@ -358,6 +359,7 @@ } } }, + {"data": "submitter"}, {"data": "time_spent"}, ] }); From 4c1fda5991f0341c75e31a8550cb9f265969aa13 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 13 Jan 2020 21:15:22 +0100 Subject: [PATCH 37/67] Fix iframe views so they actually can be displayed --- helpdesk/views/kb.py | 3 ++- helpdesk/views/public.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index ee164ea9..bbeacdef 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -10,6 +10,7 @@ views/kb.py - Public-facing knowledgebase views. The knowledgebase is a from django.http import HttpResponseRedirect from django.shortcuts import render, get_object_or_404 +from django.views.decorators.clickjacking import xframe_options_exempt from helpdesk import settings as helpdesk_settings from helpdesk.models import KBCategory, KBItem @@ -51,7 +52,7 @@ def category(request, slug, iframe=False): 'staff': staff, }) - +@xframe_options_exempt def category_iframe(request, slug): return category(request, slug, iframe=True) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index b9f5279b..4c41a023 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -13,6 +13,7 @@ from django.shortcuts import render from django.utils.http import urlquote from django.utils.translation import ugettext as _ from django.conf import settings +from django.views.decorators.clickjacking import xframe_options_exempt from django.views.generic.base import TemplateView from django.views.generic.edit import FormView @@ -100,6 +101,11 @@ class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView): class CreateTicketIframeView(BaseCreateTicketView): template_name = 'helpdesk/public_create_ticket_iframe.html' + @xframe_options_exempt + def dispatch(self, *args, **kwargs): + return super().dispatch(*args, **kwargs) + + class CreateTicketView(BaseCreateTicketView): template_name = 'helpdesk/public_create_ticket.html' From 07a42e07f858a0d71c40888edc813439e05204ce Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Tue, 14 Jan 2020 16:02:00 +0100 Subject: [PATCH 38/67] Made iframe ticket submit view CSRF exempt. Hopefully this is secure! --- helpdesk/views/public.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 4c41a023..810f75e0 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -14,6 +14,7 @@ from django.utils.http import urlquote from django.utils.translation import ugettext as _ from django.conf import settings from django.views.decorators.clickjacking import xframe_options_exempt +from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import TemplateView from django.views.generic.edit import FormView @@ -101,6 +102,7 @@ class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView): class CreateTicketIframeView(BaseCreateTicketView): template_name = 'helpdesk/public_create_ticket_iframe.html' + @csrf_exempt @xframe_options_exempt def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) From cd019d11289af56aefaad9a871a5d07340bbdcd4 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Tue, 14 Jan 2020 17:41:55 +0100 Subject: [PATCH 39/67] Improvements to iframe workflow --- helpdesk/templates/helpdesk/public_create_ticket_base.html | 2 +- helpdesk/templates/helpdesk/success_iframe.html | 4 ++++ helpdesk/urls.py | 4 ++++ helpdesk/views/public.py | 7 +++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 helpdesk/templates/helpdesk/success_iframe.html diff --git a/helpdesk/templates/helpdesk/public_create_ticket_base.html b/helpdesk/templates/helpdesk/public_create_ticket_base.html index d82fd466..82cf375b 100644 --- a/helpdesk/templates/helpdesk/public_create_ticket_base.html +++ b/helpdesk/templates/helpdesk/public_create_ticket_base.html @@ -4,7 +4,7 @@ {% if helpdesk_settings.HELPDESK_SUBMIT_A_TICKET_PUBLIC %}

    {% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}

    -
    + {{ form|bootstrap4form }} {% csrf_token %}
    diff --git a/helpdesk/templates/helpdesk/success_iframe.html b/helpdesk/templates/helpdesk/success_iframe.html new file mode 100644 index 00000000..d2d28a2a --- /dev/null +++ b/helpdesk/templates/helpdesk/success_iframe.html @@ -0,0 +1,4 @@ +{% load i18n %} +

    +{% trans "Ticket submitted successfully! We will reply via email as soon as we get the chance." %} +

    diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 59f27bcc..3ad3216b 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -171,6 +171,10 @@ urlpatterns += [ public.CreateTicketIframeView.as_view(), name='submit_iframe'), + url(r'^tickets/success_iframe/$', # Ticket was submitted successfully + public.SuccessIframeView.as_view(), + name='success_iframe'), + url(r'^view/$', public.view_ticket, name='public_view'), diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 810f75e0..50b7038d 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -107,6 +107,13 @@ class CreateTicketIframeView(BaseCreateTicketView): def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) + def form_valid(self, form): + if super().form_valid(form).status_code == 302: + return HttpResponseRedirect(reverse('helpdesk:success_iframe')) + + +class SuccessIframeView(TemplateView): + template_name = 'helpdesk/success_iframe.html' class CreateTicketView(BaseCreateTicketView): From df94b56b079d8f7b2ca9af7a148660d41703305f Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 16 Jan 2020 18:23:41 +0100 Subject: [PATCH 40/67] Fix success view --- helpdesk/locale/cs/LC_MESSAGES/django.mo | Bin 52426 -> 52763 bytes helpdesk/locale/cs/LC_MESSAGES/django.po | 99 +++++++++++++---------- helpdesk/views/public.py | 4 + 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/helpdesk/locale/cs/LC_MESSAGES/django.mo b/helpdesk/locale/cs/LC_MESSAGES/django.mo index 328fda4e5c78eca23786bb6b082cafb8bddb05f9..2242c9b503c4ea89f521934b709ee06fcba06fd4 100644 GIT binary patch delta 10978 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

    <2Vh0&vz?1k*KKv>y{LxHpl);< z%i$jwg<%`)X0C-ik){nkkH?YaH(fW{CA_lHZyWy3Rs4a^alt?6!NGhXXfsVkJx24f z0M$L9D1YGkue zH(HJ_;#%y4^C~QZ0FTR6+p{DxHZFbM>L(TNr zfZYEMiF#Hd&vgVUyNVhCnoC{9Few%IrmUq#)p$lG?$ly}xa-Jm^&VP7nX$*6Y5y7N=82Ib|b zr{_KNmm)bs5{_3d+3ik@+j7#15 z?Wm>8$4K0VT7r}L%)feio(lOrYRY~`z4HsbYa1$yjVU)oHQ+@p*#uMvmY^DZ2{pBA zQ8Td@wKT_21HFP1@jhyEPxSAy|8cnqW2mURo5v}LU$xks@`62fWam&b6aJq4Cz*Cw zi}DimaQ$xVO8MMgW8TLY{>Y2`8ICKN?T}&bU{=vTmPBjQ=|j8L-BFuvD3-wq z7>^6F1@6Iycn39O)%IH(q6W|&tKl%zh-YIoZb99+088VK*oFI>dnB5o&L7z|8i<`$^mN8OsDZd`y>@Dj%2U2KHakJ&G? zzW5mB)u<)>8rS0FPniE?l1DzZYv;!)l(*p1SpB$tlZ|)ILXBi8w!?L(hOeNOq}B;* zQ!Gij6KV;2V?7*^(UBrP1(Ct)Wl<`=ldrthJ{Yr=QtFDD3{{Ti+B$8*y;F> zQ=3XhHsw%4PsxMhBN8UjTyfWK!Me(|{`&8B9o2}>0~sB0b$wAUjFGOa)0&|NhxW(& z#Cx0{N3c~)IV|$f^;*j6lm=>-akGmoFdufcmC{o~9e_T0IdyLN7Vp zKX82J{FIAskk3Uuqr5xJJbjRO86F&EC~u^Y<0|yx`h*ze%AyZ3miS1Y=NNTd#B$2BP={U|ItEjB0IT49d=u-Uj@9H|{EOI6uH$|DFHR*Y zkzZFOM{Dv^TK@;fDUu}jp_%*h-7!Z5N;<{xpFk;_K~kg9V1-*0(^(i zTeSqfL0k!3!Fz}N3qOB!^dfGNe@HYZ@+o_Wmc;)(QaGPT=;&%OysrYkf?YAdBV2RP zUDwy0*!u7V1n(U-(DF6>UsBOgk9Lw??UcqQcrM_1|^5$C9Qp2%<~dOA;IHgS_{ z1`*$p*Tzo>9sMn44dssd{a>Es1!58>`r%y6!=kv`UEGuMGvu`heNiP5eW*Kvm66xK z`5O12jx=%|Phw}{8ZqlZ9rZcnGXmv@*1rc8UvQFNNP+)rOBCV!g{Wr*q2FH)`UUxm0tMXV~^(c?jaQ#tnp z&LCQne}&cXIbs<3Y}7H1AY4A{9k-P>`n7F2s9HodZ#GHqp{~-6FjrbTR8{%Qq zv5Uy09HVo@2Fl9`A9*{XJ-Lo}@}KcbVko(emAHe5A}>VLBz_}wOnLD9tFV`gJ|^yx zFHi+XRmvxb|B!Dd?hqR&>v$apV>A{JWdpU`h&+UtPqZgqChl|YH1;NR#No4q{~Zb! zT%}IDNM1vUJ1#kIVH?g(Cc2Yr5q~T-=?)ZEM!f@(#~TPxoddCuOH(X87WKo-y9cF=<}Uh|HXf zWKZ^}6rU$4!#CcW<;n4RN93e=GDmo7)*hFc?e*mss(U`Mhc9PXdP;V-H`(LM89v$D5v%lIBVBd3>3f8FIYWGt!&QO}w7rqmnX) zd+X(&9#J^FXfsd47A+elF29%^WAjFh^MiAy73ux|^>@;P?$GzNl;lHs9$$(leWEYz zP+o%P@P4l+J1INUlbJjwbDXzei6_wM`(f|Me delta 10708 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 diff --git a/helpdesk/locale/cs/LC_MESSAGES/django.po b/helpdesk/locale/cs/LC_MESSAGES/django.po index 177582a3..657c55df 100644 --- a/helpdesk/locale/cs/LC_MESSAGES/django.po +++ b/helpdesk/locale/cs/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: django-helpdesk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-01-10 14:47+0100\n" -"PO-Revision-Date: 2020-01-10 15:00+0100\n" +"POT-Creation-Date: 2020-01-16 18:19+0100\n" +"PO-Revision-Date: 2020-01-16 18:23+0100\n" "Last-Translator: jachym \n" "Language-Team: Czech (http://www.transifex.com/django-helpdesk/django-" "helpdesk/language/cs/)\n" @@ -67,7 +67,7 @@ msgstr "email_html_body.html" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/report_index.html:44 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/rss_list.html:45 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:64 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:170 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:171 #: third_party/django-helpdesk/helpdesk/views/staff.py:1211 #: third_party/django-helpdesk/helpdesk/views/staff.py:1217 #: third_party/django-helpdesk/helpdesk/views/staff.py:1223 @@ -461,7 +461,7 @@ msgid "Logging Type" msgstr "" #: third_party/django-helpdesk/helpdesk/models.py:287 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:80 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:81 msgid "None" msgstr "Žádný" @@ -621,7 +621,7 @@ msgstr "Přiřazeno" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:19 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/include/tickets.html:17 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:65 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:171 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:172 #: third_party/django-helpdesk/helpdesk/views/staff.py:575 msgid "Status" msgstr "Stav" @@ -1038,12 +1038,12 @@ msgid "Unrated" msgstr "Nehodnoceno" #: third_party/django-helpdesk/helpdesk/models.py:1316 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:174 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:175 msgid "Knowledge base items" msgstr "Položky znalostní báze" #: third_party/django-helpdesk/helpdesk/models.py:1351 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:232 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:233 msgid "Query Name" msgstr "Název dotazu" @@ -1429,7 +1429,7 @@ msgstr "Nepřiřazené tickety" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:7 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/confirm_delete_saved_query.html:10 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:207 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:208 msgid "Delete Saved Query" msgstr "Smazat Uložený dotaz" @@ -1663,12 +1663,12 @@ msgstr "Ponechat v mailboxu?" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:38 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_cc_list.html:47 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_desc_table.html:16 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:89 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:90 msgid "Delete" msgstr "Smazat" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/email_ignore_list.html:38 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:77 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:78 msgid "All" msgstr "Všechny" @@ -1711,7 +1711,7 @@ msgid "Ctrl-click to select multiple options" msgstr "Ctrl-klik pro výběr více možností" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/keywords.html:4 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:172 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:173 msgid "Keywords" msgstr "Klíčová slova" @@ -1739,14 +1739,14 @@ msgid "Queue(s)" msgstr "Fronta(y)" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:5 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:168 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:169 msgid "Sorting" msgstr "Řazení" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/filters/sorting.html:25 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket.html:175 #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:68 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:169 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:170 #: third_party/django-helpdesk/helpdesk/views/staff.py:584 msgid "Owner" msgstr "Vlastník" @@ -1859,6 +1859,7 @@ msgstr "Napište nám" #, python-format msgid "%(recommendations)s people found this answer useful of %(votes)s" msgstr "" +"%(recommendations)s z %(votes)s lidí si myslí, že tato odpoveď je užitečná" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/kb_index.html:15 msgid "" @@ -1888,7 +1889,7 @@ msgid "Enter a keyword, or a ticket number to jump straight to that ticket." msgstr "Vložte klíčové slovo, číslo ticketu nebo skočte rovnou na ticket." #: third_party/django-helpdesk/helpdesk/templates/helpdesk/navigation-header.html:19 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:99 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:100 msgid "Go" msgstr "" @@ -2374,6 +2375,12 @@ msgstr "RSS kanály pro každou frontu" msgid "All Open Tickets" msgstr "Všechny otevřené tickety" +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/success_iframe.html:3 +msgid "" +"Ticket submitted successfully! We will reply via email as soon as we get the " +"chance." +msgstr "Ticket odeslán úspešně! Pokusíme se Vám odpovědět co nejdříve." + #: third_party/django-helpdesk/helpdesk/templates/helpdesk/system_settings.html:3 msgid "Change System Settings" msgstr "Změnit nastavení systému" @@ -2757,78 +2764,84 @@ msgid "Timeline" msgstr "Čas" #: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:69 +#, fuzzy +#| msgid "Submitted On" +msgid "Submitter" +msgstr "Zadáno dne" + +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:70 msgid "Time Spent" msgstr "" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:74 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:75 msgid "Select:" msgstr "Vybrat" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:82 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:83 msgid "Invert" msgstr "Obrátit" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:86 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:87 msgid "With Selected Tickets:" msgstr "Provézt s vybranými tickety:" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:88 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:89 msgid "Take (Assign to me)" msgstr "Převízt (přiřadit mě)" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:90 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:91 msgid "Close" msgstr "Uzavřít" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:91 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:92 msgid "Close (Don't Send E-Mail)" msgstr "Uzavřít (bez odeslání e-mailu)" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:92 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:93 msgid "Close (Send E-Mail)" msgstr "Uzavřít (a odeslat e-mail)" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:94 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:95 msgid "Assign To" msgstr "Přiřadit" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:95 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:96 msgid "Nobody (Unassign)" msgstr "Nikomu (odstranit přiřazení)" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:145 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:146 msgid "Query Selection" msgstr "Výběr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:155 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:156 #, fuzzy #| msgid "Apply Filter" msgid "Filters" msgstr "Použit filtr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:164 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:165 #, fuzzy #| msgid "Add User" msgid "Add filter" msgstr "Přidat uživatele" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:173 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:174 msgid "Date Range" msgstr "Časový rozsah" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:205 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:206 #, fuzzy #| msgid "Apply Filter" msgid "Apply Filters" msgstr "Použit filtr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:207 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:208 #, python-format msgid "" "You are currently viewing saved query \"%(query_name)s\"." msgstr "Prohlížíte uložený výběr \"%(query_name)s\"." -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:210 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:211 #, python-format msgid "" "Run a report on this " @@ -2837,12 +2850,12 @@ msgstr "" "Spustit hlášení na tento " "výběr a uvidíte statistiku a grafy pro níže vypsaná data." -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:223 -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:242 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:224 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:243 msgid "Save Query" msgstr "Uložit výběr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:234 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:235 msgid "" "This name appears in the drop-down list of saved queries. If you share your " "query, other users will see this name, so choose something clear and " @@ -2852,15 +2865,15 @@ msgstr "" "výběr sdílet, uvidí tento název i ostatní uživatelé, takže zvolte něco na " "první pohled jasného!" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:236 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:237 msgid "Shared?" msgstr "Sdíleno?" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:237 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:238 msgid "Yes, share this query with other users." msgstr "Ano, sdílet tento výběr s dalšími uživateli." -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:238 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:239 msgid "" "If you share this query, it will be visible by all other logged-in " "users." @@ -2868,19 +2881,19 @@ msgstr "" "Pokud budete tento výběr sdílet, bude viditelný všemi dalšími " "přihlášenými uživateli." -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:254 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:255 msgid "Use Saved Query" msgstr "Použít uložený výběr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:261 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:262 msgid "Query" msgstr "Výběr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:266 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:267 msgid "Run Query" msgstr "Spustit výběr" -#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:293 +#: third_party/django-helpdesk/helpdesk/templates/helpdesk/ticket_list.html:294 msgid "No Tickets Match Your Selection" msgstr "Vašemu výběru neodpovídá žádný ticket." @@ -2946,15 +2959,15 @@ msgstr "Helpdesk: Otevřít tickety ve frontě %(queue)s" msgid "Open and Reopened Tickets in queue %(queue)s" msgstr "Otevřít a znovu otevřít tickety ve frontě %(queue)s" -#: third_party/django-helpdesk/helpdesk/views/public.py:140 +#: third_party/django-helpdesk/helpdesk/views/public.py:159 msgid "Missing ticket ID or e-mail address. Please try again." msgstr "Chybí ID ticketu nebo e-mailová adresa. Zkuste to znovu prosím." -#: third_party/django-helpdesk/helpdesk/views/public.py:149 +#: third_party/django-helpdesk/helpdesk/views/public.py:168 msgid "Invalid ticket ID or e-mail address. Please try again." msgstr "Špatné ID ticketu nebo e-mailová adresa. Zkuste to znovu prosím." -#: third_party/django-helpdesk/helpdesk/views/public.py:165 +#: third_party/django-helpdesk/helpdesk/views/public.py:184 msgid "Submitter accepted resolution and closed ticket" msgstr "Zadavatel akceptoval rozřešení a uzavřel ticket" diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 50b7038d..8a2846fb 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -115,6 +115,10 @@ class CreateTicketIframeView(BaseCreateTicketView): class SuccessIframeView(TemplateView): template_name = 'helpdesk/success_iframe.html' + @xframe_options_exempt + def dispatch(self, *args, **kwargs): + return super().dispatch(*args, **kwargs) + class CreateTicketView(BaseCreateTicketView): template_name = 'helpdesk/public_create_ticket.html' From 90621f575dab1d8514a86f7646d96b18f14791e2 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 17 Jan 2020 18:11:52 +0100 Subject: [PATCH 41/67] Fix ironic ticket creation loop when submitter email == queue email --- helpdesk/models.py | 2 ++ helpdesk/tests/test_ticket_submission.py | 31 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/helpdesk/models.py b/helpdesk/models.py index 12d316c5..41bf756b 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -613,6 +613,8 @@ class Ticket(models.Model): if dont_send_to is not None: recipients.update(dont_send_to) + recipients.add(self.queue.email_address) + def should_receive(email): return email and email not in recipients diff --git a/helpdesk/tests/test_ticket_submission.py b/helpdesk/tests/test_ticket_submission.py index 31fb0b68..41efcca3 100644 --- a/helpdesk/tests/test_ticket_submission.py +++ b/helpdesk/tests/test_ticket_submission.py @@ -140,6 +140,37 @@ class TicketBasicsTestCase(TestCase): # Ensure only two e-mails were sent - submitter & updated. self.assertEqual(email_count + 2, len(mail.outbox)) + def test_create_ticket_public_no_loopback(self): + """ + Don't send emails to the queue's own inbox. It'll create a loop. + """ + email_count = len(mail.outbox) + + self.queue_public.email_address = "queue@example.com" + self.queue_public.save() + + post_data = { + 'title': 'Test ticket title', + 'queue': self.queue_public.id, + 'submitter_email': 'queue@example.com', + 'body': 'Test ticket body', + 'priority': 3, + } + + response = self.client.post(reverse('helpdesk:home'), post_data, follow=True) + last_redirect = response.redirect_chain[-1] + last_redirect_url = last_redirect[0] + # last_redirect_status = last_redirect[1] + + # Ensure we landed on the "View" page. + # Django 1.9 compatible way of testing this + # https://docs.djangoproject.com/en/1.9/releases/1.9/#http-redirects-no-longer-forced-to-absolute-uris + urlparts = urlparse(last_redirect_url) + self.assertEqual(urlparts.path, reverse('helpdesk:public_view')) + + # Ensure submitter, new-queue + update-queue were all emailed. + self.assertEqual(email_count + 2, len(mail.outbox)) + class EmailInteractionsTestCase(TestCase): fixtures = ['emailtemplate.json'] From 2f8b4acf64767ebd080302edb143ff8f012223a0 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 20 Jan 2020 14:17:23 +0100 Subject: [PATCH 42/67] Fix pycodestyle errors --- helpdesk/tests/test_kb.py | 5 +---- helpdesk/views/kb.py | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/helpdesk/tests/test_kb.py b/helpdesk/tests/test_kb.py index 6eba76bd..9c15e818 100644 --- a/helpdesk/tests/test_kb.py +++ b/helpdesk/tests/test_kb.py @@ -1,4 +1,4 @@ -#queue_publii -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from django.urls import reverse from django.test import TestCase @@ -61,7 +61,6 @@ class KBTests(TestCase): response = self.client.get(reverse('helpdesk:kb_category', args=("test_cat",))) self.assertContains(response, '1 open tickets') - def test_kb_vote(self): self.client.login(username=self.user.get_username(), password='password') response = self.client.get(reverse('helpdesk:kb_vote', args=(self.kbitem1.pk,)) + "?vote=up") @@ -74,10 +73,8 @@ class KBTests(TestCase): response = self.client.get(cat_url) self.assertContains(response, '0 people found this answer useful of 1') - def test_kb_category_iframe(self): cat_url = reverse('helpdesk:kb_category', args=("test_cat",)) + "?kbitem=1;submitter_email=foo@bar.cz;title=lol;" response = self.client.get(cat_url) # Assert that query params are passed on to ticket submit form self.assertContains(response, "'/helpdesk/tickets/submit/?queue=1;_readonly_fields_=queue;kbitem=1;submitter_email=foo%40bar.cz&title=lol") - diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index bbeacdef..a5d592a7 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -52,6 +52,7 @@ def category(request, slug, iframe=False): 'staff': staff, }) + @xframe_options_exempt def category_iframe(request, slug): return category(request, slug, iframe=True) From af2d0d59b7d09010f200420cd04ff7c873ac415a Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Mon, 20 Jan 2020 15:37:08 -0500 Subject: [PATCH 43/67] Update requirements.txt for django To address CVE-2019-19844, require at least Django 2.2.9 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 39c04244..119de8dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django>=2.2,<3 +Django>=2.2.9,<3 django-bootstrap4-form celery django-celery-beat From 9963a3fe5d51c20b8e8e4540bcd060f7fb1af95d Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Thu, 23 Jan 2020 15:30:08 +0100 Subject: [PATCH 44/67] Fix utf decoding bug in email parsing code For some reason mozilla thunderbird sometimes marks email parts as 8bit even though they are utf-8. I guess the best way to work around this is to add a try-catch block because this really cannot be predicted. --- helpdesk/email.py | 7 +- .../tests/test_files/utf-nondecodable.eml | 72 +++++++++++++++++++ helpdesk/tests/test_get_email.py | 15 ++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 helpdesk/tests/test_files/utf-nondecodable.eml diff --git a/helpdesk/email.py b/helpdesk/email.py index 5458e022..a2088be2 100644 --- a/helpdesk/email.py +++ b/helpdesk/email.py @@ -487,13 +487,18 @@ def object_from_message(message, queue, logger): body.encode('utf-8') logger.debug("Discovered plain text MIME part") else: + try: + email_body = encoding.smart_text(part.get_payload(decode=True)) + except UnicodeDecodeError: + email_body = encoding.smart_text(part.get_payload(decode=False)) + payload = """ %s -""" % encoding.smart_text(part.get_payload(decode=True)) +""" % email_body files.append( SimpleUploadedFile(_("email_html_body.html"), payload.encode("utf-8"), 'text/html') ) diff --git a/helpdesk/tests/test_files/utf-nondecodable.eml b/helpdesk/tests/test_files/utf-nondecodable.eml new file mode 100644 index 00000000..6d5a57d5 --- /dev/null +++ b/helpdesk/tests/test_files/utf-nondecodable.eml @@ -0,0 +1,72 @@ +Delivered-To: helpdesk@example.cz +Received: by 2002:a17:90a:f983:0:0:0:0 with SMTP id cq3csp4021504pjb; + Tue, 21 Jan 2020 04:28:48 -0800 (PST) +X-Received: by 2002:a05:6000:50:: with SMTP id k16mr4730387wrx.145.1579609728626; + Tue, 21 Jan 2020 04:28:48 -0800 (PST) +X-Received: by 2002:a5d:50d2:: with SMTP id f18mr4914314wrt.366.1579609727642; + Tue, 21 Jan 2020 04:28:47 -0800 (PST) +Return-Path: +Received: from [10.0.0.179] (ip-89-176-203-67.net.upcbroadband.cz. [89.176.203.67]) + by smtp.gmail.com with ESMTPSA id w83sm3724796wmb.42.2020.01.21.04.28.46 + for + (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); + Tue, 21 Jan 2020 04:28:47 -0800 (PST) +Subject: =?UTF-8?Q?Fwd=3a_Cyklozam=c4=9bstnavatel_-_zm=c4=9bna_vyhodnocen?= + =?UTF-8?B?w60=?= +References: +To: helpdesk@example.cz +From: John Smith +Openpgp: preference=signencrypt +X-Forwarded-Message-Id: +Message-ID: <00d73ce5-774a-5ea1-6742-af73ef58c01c@example.cz> +Date: Tue, 21 Jan 2020 13:28:46 +0100 +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 + Thunderbird/60.9.0 +MIME-Version: 1.0 +In-Reply-To: +Content-Type: multipart/alternative; + boundary="------------1E8B96489BB357387CBD04A6" +Content-Language: en-US + +This is a multi-part message in MIME format. +--------------1E8B96489BB357387CBD04A6 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + + + + +-------- Forwarded Message -------- +Subject: Cyklozaměstnavatel - změna vyhodnocení +Date: Thu, 9 Jan 2020 16:24:28 +0100 +From: Nikola +To: John Smith , Jiří Houdek + + + + +Ahoj Johne, +podle domluvy bych Tě poprosila o změnu vyhodnocení soutěže +Cyklozaměstnavatel.  +Poprosím, aby se ve výsledné tabulce pro každé město zobrazoval +jednotlivý zaměstnavatel *jen jednou s průměrným výsledkem, *který vyjde +po zprůměrování hodnocení všech zaměstnanců tohoto zaměstnavatele.  +Díky moc! +n.  + + +--------------1E8B96489BB357387CBD04A6 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: 8bit + + +

    + prosazuje lepší + prostředí pro + kvalitní život + ve městě. +

    + + + +--------------1E8B96489BB357387CBD04A6-- diff --git a/helpdesk/tests/test_get_email.py b/helpdesk/tests/test_get_email.py index 50a5f1e5..9c7b4c11 100644 --- a/helpdesk/tests/test_get_email.py +++ b/helpdesk/tests/test_get_email.py @@ -84,6 +84,21 @@ class GetEmailCommonTests(TestCase): self.assertEqual(ticket.title, "Testovácí email") self.assertEqual(ticket.description, "íářčšáíéřášč") + def test_email_with_utf_8_non_decodable_sequences(self): + """ + Tests that emails with utf-8 non-decodable sequences are parsed correctly + """ + with open(os.path.join(THIS_DIR, "test_files/utf-nondecodable.eml")) as fd: + test_email = fd.read() + ticket = helpdesk.email.object_from_message(test_email, self.queue_public, self.logger) + self.assertEqual(ticket.title, "Fwd: Cyklozaměstnavatel - změna vyhodnocení") + self.assertIn("prosazuje lepší", ticket.description) + followups = FollowUp.objects.filter(ticket=ticket) + followup = followups[0] + attachments = FollowUpAttachment.objects.filter(followup=followup) + attachment = attachments[0] + self.assertIn('prosazuje lepší', attachment.file.read().decode("utf-8")) + class GetEmailParametricTemplate(object): """TestCase that checks basic email functionality across methods and socks configs.""" From 0395f26d6cf394d5ffef69c7d130c29c474097e0 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 17:02:20 +0100 Subject: [PATCH 45/67] Don't show vote counts if there are no votes --- helpdesk/templates/helpdesk/kb_category_base.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpdesk/templates/helpdesk/kb_category_base.html b/helpdesk/templates/helpdesk/kb_category_base.html index b8bb973f..5b449b7f 100644 --- a/helpdesk/templates/helpdesk/kb_category_base.html +++ b/helpdesk/templates/helpdesk/kb_category_base.html @@ -33,7 +33,9 @@
    + {% if item.votes != 0 %} {% blocktrans with recommendations=item.recommendations votes=item.votes %}{{ recommendations }} people found this answer useful of {{votes}}{% endblocktrans %} + {% endif %}
    From ecbb91b86100bd90d931c7caad92fd810720a9b7 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 17:37:59 +0100 Subject: [PATCH 46/67] Private kb categories --- docs/index.rst | 1 + docs/install.rst | 4 +++ helpdesk/migrations/0028_kbitem_team.py | 20 +++++++++++++ helpdesk/migrations/0029_kbcategory_public.py | 18 ++++++++++++ helpdesk/models.py | 15 ++++++++++ helpdesk/user.py | 29 +++++++++++++++---- helpdesk/views/kb.py | 9 ++++-- helpdesk/views/public.py | 3 +- requirements.txt | 1 + 9 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 helpdesk/migrations/0028_kbitem_team.py create mode 100644 helpdesk/migrations/0029_kbcategory_public.py diff --git a/docs/index.rst b/docs/index.rst index 1757da4d..a3636cba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ Contents settings spam custom_fields + integration contributing license diff --git a/docs/install.rst b/docs/install.rst index e0b0d1f8..9eaba067 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -58,7 +58,11 @@ errors with trying to create User settings. 'django.contrib.admin', # Required for helpdesk admin/maintenance 'django.contrib.humanize', # Required for elapsed time formatting 'bootstrap4form', # Required for nicer formatting of forms with the default templates + 'account', # Required by pinax-teams + 'pinax.inviations', # required by pinax-teams + 'pinax.teams', # team support 'helpdesk', # This is us! + 'reversion', # required by pinax-teams ) Your ``settings.py`` file should also define a ``SITE_ID`` that allows multiple projects to share diff --git a/helpdesk/migrations/0028_kbitem_team.py b/helpdesk/migrations/0028_kbitem_team.py new file mode 100644 index 00000000..7801e585 --- /dev/null +++ b/helpdesk/migrations/0028_kbitem_team.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.9 on 2020-01-27 15:01 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('pinax_teams', '0004_auto_20170511_0856'), + ('helpdesk', '0027_auto_20200107_1221'), + ] + + operations = [ + migrations.AddField( + model_name='kbitem', + name='team', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pinax_teams.Team', verbose_name='Team'), + ), + ] diff --git a/helpdesk/migrations/0029_kbcategory_public.py b/helpdesk/migrations/0029_kbcategory_public.py new file mode 100644 index 00000000..1cca37be --- /dev/null +++ b/helpdesk/migrations/0029_kbcategory_public.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.9 on 2020-01-27 16:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helpdesk', '0028_kbitem_team'), + ] + + operations = [ + migrations.AddField( + model_name='kbcategory', + name='public', + field=models.BooleanField(default=True, verbose_name='Is KBCategory publicly visible?'), + ), + ] diff --git a/helpdesk/models.py b/helpdesk/models.py index 41bf756b..fd6979f3 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -25,6 +25,8 @@ from django.utils.safestring import mark_safe from markdown import markdown from markdown.extensions import Extension +import pinax.teams.models + import uuid @@ -1234,6 +1236,11 @@ class KBCategory(models.Model): verbose_name=_('Default queue when creating a ticket after viewing this category.'), ) + public = models.BooleanField( + default=True, + verbose_name=_("Is KBCategory publicly visible?") + ) + def __str__(self): return '%s' % self.title @@ -1297,6 +1304,14 @@ class KBItem(models.Model): blank=True, ) + team = models.ForeignKey( + pinax.teams.models.Team, + on_delete=models.CASCADE, + verbose_name=_('Team'), + blank=True, + null=True, + ) + def save(self, *args, **kwargs): if not self.last_updated: self.last_updated = timezone.now() diff --git a/helpdesk/user.py b/helpdesk/user.py index 32091d8e..8f3ec088 100644 --- a/helpdesk/user.py +++ b/helpdesk/user.py @@ -1,11 +1,16 @@ from helpdesk.models import ( Ticket, - Queue + Queue, + KBCategory, ) from helpdesk import settings as helpdesk_settings +def huser_from_request(req): + return HelpdeskUser(req.user) + + class HelpdeskUser: def __init__(self, user): self.user = user @@ -30,9 +35,19 @@ class HelpdeskUser: else: return all_queues + def get_kb_categories(self): + categories = [] + for cat in KBCategory.objects.all(): + if self.can_access_kbcategory(cat): + categories.append(cat) + return categories + def get_tickets_in_queues(self): return Ticket.objects.filter(queue__in=self.get_queues()) + def has_full_access(self): + return self.user.is_superuser or self.user.is_staff + def can_access_queue(self, queue): """Check if a certain user can access a certain queue. @@ -40,11 +55,10 @@ class HelpdeskUser: :param queue: The django-helpdesk Queue instance :return: True if the user has permission (either by default or explicitly), false otherwise """ - user = self.user - if user.is_superuser or not helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION: + if self.has_full_access(): return True else: - return user.has_perm(queue.permission_name) + return helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION and self.user.has_perm(queue.permission_name) def can_access_ticket(self, ticket): """Check to see if the user has permission to access @@ -52,8 +66,13 @@ class HelpdeskUser: user = self.user if self.can_access_queue(ticket.queue): return True - elif user.is_superuser or user.is_staff or \ + elif self.has_full_access() or \ (ticket.assigned_to and user.id == ticket.assigned_to.id): return True else: return False + + def can_access_kbcategory(self, category): + if category.public: + return True + return self.has_full_access() or (category.queue and self.can_access_queue(category.queue)) diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index a5d592a7..bb9d45ce 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -8,25 +8,28 @@ views/kb.py - Public-facing knowledgebase views. The knowledgebase is a resolutions to common problems. """ -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, Http404 from django.shortcuts import render, get_object_or_404 from django.views.decorators.clickjacking import xframe_options_exempt from helpdesk import settings as helpdesk_settings +from helpdesk import user from helpdesk.models import KBCategory, KBItem def index(request): - category_list = KBCategory.objects.all() + huser = user.huser_from_request(request) # TODO: It'd be great to have a list of most popular items here. return render(request, 'helpdesk/kb_index.html', { - 'kb_categories': category_list, + 'kb_categories': huser.get_kb_categories(), 'helpdesk_settings': helpdesk_settings, }) def category(request, slug, iframe=False): category = get_object_or_404(KBCategory, slug__iexact=slug) + if not user.huser_from_request(request).can_access_kbcategory(category): + raise Http404 items = category.kbitem_set.all() selected_item = request.GET.get('kbitem', None) try: diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 8a2846fb..3ef9b1e9 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -25,6 +25,7 @@ import helpdesk.views.abstract_views as abstract_views from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem +from helpdesk.user import huser_from_request def create_ticket(request, *args, **kwargs): @@ -129,7 +130,7 @@ class Homepage(CreateTicketView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['kb_categories'] = KBCategory.objects.all() + context['kb_categories'] = huser_from_request(self.request).get_kb_categories() return context diff --git a/requirements.txt b/requirements.txt index 119de8dc..0455b855 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ pytz six djangorestframework django-model-utils +pinax-teams From eea76a5eb77f1d92f48e9a2bb14e9bab8645dfbc Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 17:56:49 +0100 Subject: [PATCH 47/67] Do not include resolved tickets in default ticket query --- helpdesk/views/staff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 0ea2f481..0563976b 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -804,7 +804,7 @@ def ticket_list(request): } default_query_params = { 'filtering': { - 'status__in': [1, 2, 3], + 'status__in': [1, 2], }, 'sorting': 'created', 'search_string': '', From 5b0d44ec3a2c404fed61e6ccde4524e4fc38b9c9 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 19:45:15 +0100 Subject: [PATCH 48/67] Assign KBItems to teams This allows you to only show on the dashboard those tickets which belong to a given user's team. --- helpdesk/models.py | 3 ++ .../helpdesk/include/unassigned.html | 41 ++++++++++++++++++- helpdesk/templates/helpdesk/ticket_list.html | 4 ++ helpdesk/user.py | 10 ++++- helpdesk/views/kb.py | 2 +- helpdesk/views/public.py | 2 +- helpdesk/views/staff.py | 24 ++++++++++- 7 files changed, 81 insertions(+), 5 deletions(-) diff --git a/helpdesk/models.py b/helpdesk/models.py index fd6979f3..8e4e90d4 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -1343,6 +1343,9 @@ class KBItem(models.Model): def num_open_tickets(self): return Ticket.objects.filter(kbitem=self, status__in=(1, 2)).count() + def unassigned_tickets(self): + return Ticket.objects.filter(kbitem=self, status__in=(1, 2), assigned_to__isnull=True) + def get_markdown(self): return get_markdown(self.answer) diff --git a/helpdesk/templates/helpdesk/include/unassigned.html b/helpdesk/templates/helpdesk/include/unassigned.html index 9632ab36..743d0eb9 100644 --- a/helpdesk/templates/helpdesk/include/unassigned.html +++ b/helpdesk/templates/helpdesk/include/unassigned.html @@ -1,6 +1,5 @@ {% load i18n humanize %} -
    @@ -40,3 +39,43 @@
    +{% for kbitem in kbitems %} +
    +
    + + {% trans "KBItem:" %} {{kbitem.title}} {% trans "Team:" %} {{kbitem.team.name}} {% trans "(pick up a ticket if you start to work on it)" %} +
    +
    +
    + + + + + + + + + + + + {% for ticket in kbitem.unassigned_tickets %} + + + + + + + + {% empty %} + + {% endfor %} + +
    {% trans "Ticket" %}{% trans "Prority" %}{% trans "Queue" %}{% trans "Created" %}{% trans "Actions" %}
    {{ ticket.id }}. {{ ticket.title }} {{ ticket.priority }}{{ ticket.queue }}{{ ticket.created|naturaltime }} + + +
    {% trans "There are no unassigned tickets." %}
    +
    +
    + +
    +{% endfor %} diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 8cdfd0b5..6bb0dbd7 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -96,6 +96,10 @@ {% for u in user_choices %}{% endfor %} + + + {% for kbi in kb_items %}{% endfor %} +

    diff --git a/helpdesk/user.py b/helpdesk/user.py index 8f3ec088..03958e41 100644 --- a/helpdesk/user.py +++ b/helpdesk/user.py @@ -2,6 +2,7 @@ from helpdesk.models import ( Ticket, Queue, KBCategory, + KBItem, ) from helpdesk import settings as helpdesk_settings @@ -35,13 +36,20 @@ class HelpdeskUser: else: return all_queues - def get_kb_categories(self): + def get_allowed_kb_categories(self): categories = [] for cat in KBCategory.objects.all(): if self.can_access_kbcategory(cat): categories.append(cat) return categories + def get_assigned_kb_items(self): + kbitems = [] + for item in KBItem.objects.all(): + if item.team and item.team.is_member(self.user): + kbitems.append(item) + return kbitems + def get_tickets_in_queues(self): return Ticket.objects.filter(queue__in=self.get_queues()) diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index bb9d45ce..a1bead71 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -21,7 +21,7 @@ def index(request): huser = user.huser_from_request(request) # TODO: It'd be great to have a list of most popular items here. return render(request, 'helpdesk/kb_index.html', { - 'kb_categories': huser.get_kb_categories(), + 'kb_categories': huser.get_allowed_kb_categories(), 'helpdesk_settings': helpdesk_settings, }) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 3ef9b1e9..470ae8ab 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -130,7 +130,7 @@ class Homepage(CreateTicketView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['kb_categories'] = huser_from_request(self.request).get_kb_categories() + context['kb_categories'] = huser_from_request(self.request).get_allowed_kb_categories() return context diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 0563976b..1cd7e293 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -103,6 +103,7 @@ def dashboard(request): showing ticket counts by queue/status, and a list of unassigned tickets with options for them to 'Take' ownership of said tickets. """ + huser = HelpdeskUser(request.user) active_tickets = Ticket.objects.select_related('queue').exclude( status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS], ) @@ -117,13 +118,16 @@ def dashboard(request): assigned_to=request.user, status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS]) - user_queues = HelpdeskUser(request.user).get_queues() + user_queues = huser.get_queues() unassigned_tickets = active_tickets.filter( assigned_to__isnull=True, + kbitem__isnull=True, queue__in=user_queues ) + kbitems = huser.get_assigned_kb_items() + # all tickets, reported by current user all_tickets_reported_by_current_user = '' email_current_user = request.user.email @@ -157,6 +161,7 @@ def dashboard(request): 'user_tickets': tickets, 'user_tickets_closed_resolved': tickets_closed_resolved, 'unassigned_tickets': unassigned_tickets, + 'kbitems': kbitems, 'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user, 'basic_ticket_stats': basic_ticket_stats, }) @@ -706,6 +711,13 @@ def mass_update(request): parts = action.split('_') user = User.objects.get(id=parts[1]) action = 'assign' + if action == 'kbitem_none': + kbitem = None + action = 'set_kbitem' + if action.startswith('kbitem_'): + parts = action.split('_') + kbitem = KBItem.objects.get(id=parts[1]) + action = 'set_kbitem' elif action == 'take': user = request.user action = 'assign' @@ -735,6 +747,15 @@ def mass_update(request): public=True, user=request.user) f.save() + elif action == 'set_kbitem': + t.kbitem = kbitem + t.save() + f = FollowUp(ticket=t, + date=timezone.now(), + title=_('KBItem set in bulk update'), + public=False, + user=request.user) + f.save() elif action == 'close' and t.status != Ticket.CLOSED_STATUS: t.status = Ticket.CLOSED_STATUS t.save() @@ -917,6 +938,7 @@ def ticket_list(request): context, default_tickets_per_page=request.user.usersettings_helpdesk.tickets_per_page, user_choices=User.objects.filter(is_active=True, is_staff=True), + kb_items=KBItem.objects.all(), queue_choices=huser.get_queues(), status_choices=Ticket.STATUS_CHOICES, kbitem_choices=kbitem_choices, From cd0af7e0f5efa433b07f9fc80f8bb470ca770997 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 20:01:57 +0100 Subject: [PATCH 49/67] Document new teams code --- docs/index.rst | 1 + docs/teams.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 docs/teams.rst diff --git a/docs/index.rst b/docs/index.rst index a3636cba..e93cbde2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,7 @@ Contents spam custom_fields integration + teams contributing license diff --git a/docs/teams.rst b/docs/teams.rst new file mode 100644 index 00000000..cd27aaef --- /dev/null +++ b/docs/teams.rst @@ -0,0 +1,14 @@ +Working with teams and larger organizations +=========================================== + +If you only have one or two people working on tickets, basic Queue setup is enough to get you going. You can now assign tickets to teams for better ticket filtering, reducing noise and improving organization efficiency. + +Rather than assigning tickets to teams directly, django-helpdesk allows you assign tickets to knowledge-base items and then assign knowledge base items to teams. + +Knowledge-base items can be in either public or private knowledge-base categories, so this organizational structure need not have any influence on the external appearance of your public helpdesk web portal. + +You can visit the 'Pinax Teams' page in your django admin in order to create a team and add team members. + +You can assign a knowledge-base item to a team on the Helpdesk admin page. + +Once you have set up teams. Unassigned tickets which are associated with a knowledge-base item will only be shown on the dashboard to those users who are members of the team which is associated with that knowledge-base item. From c20dafe952c6e445d3f9ac2b768922b174d8f10d Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Fri, 31 Jan 2020 18:40:01 +0100 Subject: [PATCH 50/67] Use temporarilly fixed version of pinax-teams --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0455b855..b303f4b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,4 @@ pytz six djangorestframework django-model-utils -pinax-teams +pinax-teams @ git+https://github.com/auto-mat/pinax-teams.git@slugify#egg=pinax-teams diff --git a/setup.py b/setup.py index fc1151d9..c9f4c9db 100644 --- a/setup.py +++ b/setup.py @@ -113,7 +113,7 @@ def get_requirements(): def get_long_description(): with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f: long_desc = f.read() - + return long_desc From f8c652d5063f0788b1a33202f0e93985d22793a4 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 3 Feb 2020 14:06:45 +0100 Subject: [PATCH 51/67] Better filtering + optional columns in ticket list view --- helpdesk/query.py | 24 +- helpdesk/serializers.py | 7 +- .../datatables/css/buttons.dataTables.css | 380 ++++ .../vendor/datatables/js/buttons.colVis.js | 206 ++ .../datatables/js/dataTables.buttons.js | 2015 +++++++++++++++++ helpdesk/templates/helpdesk/base-head.html | 1 + helpdesk/templates/helpdesk/base_js.html | 2 + .../templates/helpdesk/filters/kbitems.html | 11 +- .../templates/helpdesk/filters/owner.html | 5 + helpdesk/templates/helpdesk/ticket_list.html | 16 +- helpdesk/views/staff.py | 14 +- 11 files changed, 2660 insertions(+), 21 deletions(-) create mode 100644 helpdesk/static/helpdesk/vendor/datatables/css/buttons.dataTables.css create mode 100644 helpdesk/static/helpdesk/vendor/datatables/js/buttons.colVis.js create mode 100644 helpdesk/static/helpdesk/vendor/datatables/js/dataTables.buttons.js diff --git a/helpdesk/query.py b/helpdesk/query.py index 0ee6373c..869e800a 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -77,13 +77,16 @@ def get_search_filter_args(search): DATATABLES_ORDER_COLUMN_CHOICES = Choices( ('0', 'id'), + ('1', 'title'), ('2', 'priority'), - ('3', 'title'), - ('4', 'queue'), - ('5', 'status'), - ('6', 'created'), - ('7', 'due_date'), - ('8', 'assigned_to') + ('3', 'queue'), + ('4', 'status'), + ('5', 'created'), + ('6', 'due_date'), + ('7', 'assigned_to'), + ('8', 'submitter_email'), + #('9', 'time_spent'), + ('10', 'kbitem'), ) @@ -123,10 +126,9 @@ class __Query__: sorting: The name of the column to sort by """ - for key in self.params.get('filtering', {}).keys(): - filter = {key: self.params['filtering'][key]} - queryset = queryset.filter(**filter) - queryset = queryset.filter(self.get_search_filter_args()) + filter = self.params.get('filtering', {}) + filter_or = self.params.get('filtering_or', {}) + queryset = queryset.filter((Q(**filter) | Q(**filter_or)) & self.get_search_filter_args()) sorting = self.params.get('sorting', None) if sorting: sortreverse = self.params.get('sortreverse', None) @@ -177,7 +179,7 @@ class __Query__: queryset = objects.all().order_by(order_by) total = queryset.count() - if search_value: + if search_value: # Dead code currently queryset = queryset.filter(get_search_filter_args(search_value)) count = queryset.count() diff --git a/helpdesk/serializers.py b/helpdesk/serializers.py index 469fc92e..7dd51346 100644 --- a/helpdesk/serializers.py +++ b/helpdesk/serializers.py @@ -22,13 +22,14 @@ class DatatablesTicketSerializer(serializers.ModelSerializer): row_class = serializers.SerializerMethodField() time_spent = serializers.SerializerMethodField() queue = serializers.SerializerMethodField() + kbitem = serializers.SerializerMethodField() class Meta: model = Ticket # fields = '__all__' fields = ('ticket', 'id', 'priority', 'title', 'queue', 'status', 'created', 'due_date', 'assigned_to', 'submitter', 'row_class', - 'time_spent') + 'time_spent', 'kbitem') def get_queue(self, obj): return ({"title": obj.queue.title, "id": obj.queue.id}) @@ -62,3 +63,7 @@ class DatatablesTicketSerializer(serializers.ModelSerializer): def get_row_class(self, obj): return (obj.get_priority_css_class) + + def get_kbitem(self, obj): + return obj.kbitem.title if obj.kbitem else "" + diff --git a/helpdesk/static/helpdesk/vendor/datatables/css/buttons.dataTables.css b/helpdesk/static/helpdesk/vendor/datatables/css/buttons.dataTables.css new file mode 100644 index 00000000..d8e63ed7 --- /dev/null +++ b/helpdesk/static/helpdesk/vendor/datatables/css/buttons.dataTables.css @@ -0,0 +1,380 @@ +@keyframes dtb-spinner { + 100% { + transform: rotate(360deg); + } +} +@-o-keyframes dtb-spinner { + 100% { + -o-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-ms-keyframes dtb-spinner { + 100% { + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-webkit-keyframes dtb-spinner { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-moz-keyframes dtb-spinner { + 100% { + -moz-transform: rotate(360deg); + transform: rotate(360deg); + } +} +div.dt-button-info { + position: fixed; + top: 50%; + left: 50%; + width: 400px; + margin-top: -100px; + margin-left: -200px; + background-color: white; + border: 2px solid #111; + box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3); + border-radius: 3px; + text-align: center; + z-index: 21; +} +div.dt-button-info h2 { + padding: 0.5em; + margin: 0; + font-weight: normal; + border-bottom: 1px solid #ddd; + background-color: #f3f3f3; +} +div.dt-button-info > div { + padding: 1em; +} + +div.dt-button-collection-title { + text-align: center; + padding: 0.3em 0 0.5em; + font-size: 0.9em; +} + +div.dt-button-collection-title:empty { + display: none; +} + +button.dt-button, +div.dt-button, +a.dt-button { + position: relative; + display: inline-block; + box-sizing: border-box; + margin-right: 0.333em; + margin-bottom: 0.333em; + padding: 0.5em 1em; + border: 1px solid #999; + border-radius: 2px; + cursor: pointer; + font-size: 0.88em; + line-height: 1.6em; + color: black; + white-space: nowrap; + overflow: hidden; + background-color: #e9e9e9; + /* Fallback */ + background-image: -webkit-linear-gradient(top, white 0%, #e9e9e9 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, white 0%, #e9e9e9 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, white 0%, #e9e9e9 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, white 0%, #e9e9e9 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, white 0%, #e9e9e9 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='white', EndColorStr='#e9e9e9'); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; + outline: none; + text-overflow: ellipsis; +} +button.dt-button.disabled, +div.dt-button.disabled, +a.dt-button.disabled { + color: #999; + border: 1px solid #d0d0d0; + cursor: default; + background-color: #f9f9f9; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #ffffff 0%, #f9f9f9 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #ffffff 0%, #f9f9f9 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #ffffff 0%, #f9f9f9 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #ffffff 0%, #f9f9f9 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #ffffff 0%, #f9f9f9 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff', EndColorStr='#f9f9f9'); +} +button.dt-button:active:not(.disabled), button.dt-button.active:not(.disabled), +div.dt-button:active:not(.disabled), +div.dt-button.active:not(.disabled), +a.dt-button:active:not(.disabled), +a.dt-button.active:not(.disabled) { + background-color: #e2e2e2; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #f3f3f3 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3', EndColorStr='#e2e2e2'); + box-shadow: inset 1px 1px 3px #999999; +} +button.dt-button:active:not(.disabled):hover:not(.disabled), button.dt-button.active:not(.disabled):hover:not(.disabled), +div.dt-button:active:not(.disabled):hover:not(.disabled), +div.dt-button.active:not(.disabled):hover:not(.disabled), +a.dt-button:active:not(.disabled):hover:not(.disabled), +a.dt-button.active:not(.disabled):hover:not(.disabled) { + box-shadow: inset 1px 1px 3px #999999; + background-color: #cccccc; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #eaeaea 0%, #cccccc 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #eaeaea 0%, #cccccc 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #eaeaea 0%, #cccccc 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #eaeaea 0%, #cccccc 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #eaeaea 0%, #cccccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#eaeaea', EndColorStr='#cccccc'); +} +button.dt-button:hover, +div.dt-button:hover, +a.dt-button:hover { + text-decoration: none; +} +button.dt-button:hover:not(.disabled), +div.dt-button:hover:not(.disabled), +a.dt-button:hover:not(.disabled) { + border: 1px solid #666; + background-color: #e0e0e0; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #f9f9f9 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f9f9f9', EndColorStr='#e0e0e0'); +} +button.dt-button:focus:not(.disabled), +div.dt-button:focus:not(.disabled), +a.dt-button:focus:not(.disabled) { + border: 1px solid #426c9e; + text-shadow: 0 1px 0 #c4def1; + outline: none; + background-color: #79ace9; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #bddef4 0%, #79ace9 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #bddef4 0%, #79ace9 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #bddef4 0%, #79ace9 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #bddef4 0%, #79ace9 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #bddef4 0%, #79ace9 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#bddef4', EndColorStr='#79ace9'); +} + +.dt-button embed { + outline: none; +} + +div.dt-buttons { + position: relative; + float: left; +} +div.dt-buttons.buttons-right { + float: right; +} + +div.dt-button-collection { + position: absolute; + top: 0; + left: 0; + width: 150px; + margin-top: 3px; + padding: 8px 8px 4px 8px; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.4); + background-color: white; + overflow: hidden; + z-index: 2002; + border-radius: 5px; + box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3); + box-sizing: border-box; +} +div.dt-button-collection button.dt-button, +div.dt-button-collection div.dt-button, +div.dt-button-collection a.dt-button { + position: relative; + left: 0; + right: 0; + width: 100%; + display: block; + float: none; + margin-bottom: 4px; + margin-right: 0; +} +div.dt-button-collection button.dt-button:active:not(.disabled), div.dt-button-collection button.dt-button.active:not(.disabled), +div.dt-button-collection div.dt-button:active:not(.disabled), +div.dt-button-collection div.dt-button.active:not(.disabled), +div.dt-button-collection a.dt-button:active:not(.disabled), +div.dt-button-collection a.dt-button.active:not(.disabled) { + background-color: #dadada; + /* Fallback */ + background-image: -webkit-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* FF3.6 */ + background-image: -ms-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* IE10 */ + background-image: -o-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f0f0f0', EndColorStr='#dadada'); + box-shadow: inset 1px 1px 3px #666; +} +div.dt-button-collection.fixed { + position: fixed; + top: 50%; + left: 50%; + margin-left: -75px; + border-radius: 0; +} +div.dt-button-collection.fixed.two-column { + margin-left: -200px; +} +div.dt-button-collection.fixed.three-column { + margin-left: -225px; +} +div.dt-button-collection.fixed.four-column { + margin-left: -300px; +} +div.dt-button-collection > :last-child { + display: block !important; + -webkit-column-gap: 8px; + -moz-column-gap: 8px; + -ms-column-gap: 8px; + -o-column-gap: 8px; + column-gap: 8px; +} +div.dt-button-collection > :last-child > * { + -webkit-column-break-inside: avoid; + break-inside: avoid; +} +div.dt-button-collection.two-column { + width: 400px; +} +div.dt-button-collection.two-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 2; + -moz-column-count: 2; + -ms-column-count: 2; + -o-column-count: 2; + column-count: 2; +} +div.dt-button-collection.three-column { + width: 450px; +} +div.dt-button-collection.three-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 3; + -moz-column-count: 3; + -ms-column-count: 3; + -o-column-count: 3; + column-count: 3; +} +div.dt-button-collection.four-column { + width: 600px; +} +div.dt-button-collection.four-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 4; + -moz-column-count: 4; + -ms-column-count: 4; + -o-column-count: 4; + column-count: 4; +} +div.dt-button-collection .dt-button { + border-radius: 0; +} + +div.dt-button-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + /* Fallback */ + background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* IE10 Consumer Preview */ + background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Firefox */ + background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Opera */ + background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); + /* Webkit (Safari/Chrome 10) */ + background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Webkit (Chrome 11+) */ + background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* W3C Markup, IE10 Release Preview */ + z-index: 2001; +} + +@media screen and (max-width: 640px) { + div.dt-buttons { + float: none !important; + text-align: center; + } +} +button.dt-button.processing, +div.dt-button.processing, +a.dt-button.processing { + color: rgba(0, 0, 0, 0.2); +} +button.dt-button.processing:after, +div.dt-button.processing:after, +a.dt-button.processing:after { + position: absolute; + top: 50%; + left: 50%; + width: 16px; + height: 16px; + margin: -8px 0 0 -8px; + box-sizing: border-box; + display: block; + content: ' '; + border: 2px solid #282828; + border-radius: 50%; + border-left-color: transparent; + border-right-color: transparent; + animation: dtb-spinner 1500ms infinite linear; + -o-animation: dtb-spinner 1500ms infinite linear; + -ms-animation: dtb-spinner 1500ms infinite linear; + -webkit-animation: dtb-spinner 1500ms infinite linear; + -moz-animation: dtb-spinner 1500ms infinite linear; +} diff --git a/helpdesk/static/helpdesk/vendor/datatables/js/buttons.colVis.js b/helpdesk/static/helpdesk/vendor/datatables/js/buttons.colVis.js new file mode 100644 index 00000000..b9529d29 --- /dev/null +++ b/helpdesk/static/helpdesk/vendor/datatables/js/buttons.colVis.js @@ -0,0 +1,206 @@ +/*! + * Column visibility buttons for Buttons and DataTables. + * 2016 SpryMedia Ltd - datatables.net/license + */ + +(function( factory ){ + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + root = window; + } + + if ( ! $ || ! $.fn.dataTable ) { + $ = require('datatables.net')(root, $).$; + } + + if ( ! $.fn.dataTable.Buttons ) { + require('datatables.net-buttons')(root, $); + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +}(function( $, window, document, undefined ) { +'use strict'; +var DataTable = $.fn.dataTable; + + +$.extend( DataTable.ext.buttons, { + // A collection of column visibility buttons + colvis: function ( dt, conf ) { + return { + extend: 'collection', + text: function ( dt ) { + return dt.i18n( 'buttons.colvis', 'Column visibility' ); + }, + className: 'buttons-colvis', + buttons: [ { + extend: 'columnsToggle', + columns: conf.columns, + columnText: conf.columnText + } ] + }; + }, + + // Selected columns with individual buttons - toggle column visibility + columnsToggle: function ( dt, conf ) { + var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) { + return { + extend: 'columnToggle', + columns: idx, + columnText: conf.columnText + }; + } ).toArray(); + + return columns; + }, + + // Single button to toggle column visibility + columnToggle: function ( dt, conf ) { + return { + extend: 'columnVisibility', + columns: conf.columns, + columnText: conf.columnText + }; + }, + + // Selected columns with individual buttons - set column visibility + columnsVisibility: function ( dt, conf ) { + var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) { + return { + extend: 'columnVisibility', + columns: idx, + visibility: conf.visibility, + columnText: conf.columnText + }; + } ).toArray(); + + return columns; + }, + + // Single button to set column visibility + columnVisibility: { + columns: undefined, // column selector + text: function ( dt, button, conf ) { + return conf._columnText( dt, conf ); + }, + className: 'buttons-columnVisibility', + action: function ( e, dt, button, conf ) { + var col = dt.columns( conf.columns ); + var curr = col.visible(); + + col.visible( conf.visibility !== undefined ? + conf.visibility : + ! (curr.length ? curr[0] : false ) + ); + }, + init: function ( dt, button, conf ) { + var that = this; + button.attr( 'data-cv-idx', conf.columns ); + + dt + .on( 'column-visibility.dt'+conf.namespace, function (e, settings) { + if ( ! settings.bDestroying && settings.nTable == dt.settings()[0].nTable ) { + that.active( dt.column( conf.columns ).visible() ); + } + } ) + .on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) { + if ( dt.columns( conf.columns ).count() !== 1 ) { + return; + } + + // This button controls the same column index but the text for the column has + // changed + button.text( conf._columnText( dt, conf ) ); + + // Since its a different column, we need to check its visibility + that.active( dt.column( conf.columns ).visible() ); + } ); + + this.active( dt.column( conf.columns ).visible() ); + }, + destroy: function ( dt, button, conf ) { + dt + .off( 'column-visibility.dt'+conf.namespace ) + .off( 'column-reorder.dt'+conf.namespace ); + }, + + _columnText: function ( dt, conf ) { + // Use DataTables' internal data structure until this is presented + // is a public API. The other option is to use + // `$( column(col).node() ).text()` but the node might not have been + // populated when Buttons is constructed. + var idx = dt.column( conf.columns ).index(); + var title = dt.settings()[0].aoColumns[ idx ].sTitle + .replace(/\n/g," ") // remove new lines + .replace(//gi, " ") // replace line breaks with spaces + .replace(//g, "") // remove select tags, including options text + .replace(//g, "") // strip HTML comments + .replace(/<.*?>/g, "") // strip HTML + .replace(/^\s+|\s+$/g,""); // trim + + return conf.columnText ? + conf.columnText( dt, idx, title ) : + title; + } + }, + + + colvisRestore: { + className: 'buttons-colvisRestore', + + text: function ( dt ) { + return dt.i18n( 'buttons.colvisRestore', 'Restore visibility' ); + }, + + init: function ( dt, button, conf ) { + conf._visOriginal = dt.columns().indexes().map( function ( idx ) { + return dt.column( idx ).visible(); + } ).toArray(); + }, + + action: function ( e, dt, button, conf ) { + dt.columns().every( function ( i ) { + // Take into account that ColReorder might have disrupted our + // indexes + var idx = dt.colReorder && dt.colReorder.transpose ? + dt.colReorder.transpose( i, 'toOriginal' ) : + i; + + this.visible( conf._visOriginal[ idx ] ); + } ); + } + }, + + + colvisGroup: { + className: 'buttons-colvisGroup', + + action: function ( e, dt, button, conf ) { + dt.columns( conf.show ).visible( true, false ); + dt.columns( conf.hide ).visible( false, false ); + + dt.columns.adjust(); + }, + + show: [], + + hide: [] + } +} ); + + +return DataTable.Buttons; +})); diff --git a/helpdesk/static/helpdesk/vendor/datatables/js/dataTables.buttons.js b/helpdesk/static/helpdesk/vendor/datatables/js/dataTables.buttons.js new file mode 100644 index 00000000..045bb8ce --- /dev/null +++ b/helpdesk/static/helpdesk/vendor/datatables/js/dataTables.buttons.js @@ -0,0 +1,2015 @@ +/*! Buttons for DataTables 1.6.1 + * ©2016-2019 SpryMedia Ltd - datatables.net/license + */ + +(function( factory ){ + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery', 'datatables.net'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + root = window; + } + + if ( ! $ || ! $.fn.dataTable ) { + $ = require('datatables.net')(root, $).$; + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +}(function( $, window, document, undefined ) { +'use strict'; +var DataTable = $.fn.dataTable; + + +// Used for namespacing events added to the document by each instance, so they +// can be removed on destroy +var _instCounter = 0; + +// Button namespacing counter for namespacing events on individual buttons +var _buttonCounter = 0; + +var _dtButtons = DataTable.ext.buttons; + +/** + * [Buttons description] + * @param {[type]} + * @param {[type]} + */ +var Buttons = function( dt, config ) +{ + // If not created with a `new` keyword then we return a wrapper function that + // will take the settings object for a DT. This allows easy use of new instances + // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`. + if ( !(this instanceof Buttons) ) { + return function (settings) { + return new Buttons( settings, dt ).container(); + }; + } + + // If there is no config set it to an empty object + if ( typeof( config ) === 'undefined' ) { + config = {}; + } + + // Allow a boolean true for defaults + if ( config === true ) { + config = {}; + } + + // For easy configuration of buttons an array can be given + if ( $.isArray( config ) ) { + config = { buttons: config }; + } + + this.c = $.extend( true, {}, Buttons.defaults, config ); + + // Don't want a deep copy for the buttons + if ( config.buttons ) { + this.c.buttons = config.buttons; + } + + this.s = { + dt: new DataTable.Api( dt ), + buttons: [], + listenKeys: '', + namespace: 'dtb'+(_instCounter++) + }; + + this.dom = { + container: $('<'+this.c.dom.container.tag+'/>') + .addClass( this.c.dom.container.className ) + }; + + this._constructor(); +}; + + +$.extend( Buttons.prototype, { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public methods + */ + + /** + * Get the action of a button + * @param {int|string} Button index + * @return {function} + *//** + * Set the action of a button + * @param {node} node Button element + * @param {function} action Function to set + * @return {Buttons} Self for chaining + */ + action: function ( node, action ) + { + var button = this._nodeToButton( node ); + + if ( action === undefined ) { + return button.conf.action; + } + + button.conf.action = action; + + return this; + }, + + /** + * Add an active class to the button to make to look active or get current + * active state. + * @param {node} node Button element + * @param {boolean} [flag] Enable / disable flag + * @return {Buttons} Self for chaining or boolean for getter + */ + active: function ( node, flag ) { + var button = this._nodeToButton( node ); + var klass = this.c.dom.button.active; + var jqNode = $(button.node); + + if ( flag === undefined ) { + return jqNode.hasClass( klass ); + } + + jqNode.toggleClass( klass, flag === undefined ? true : flag ); + + return this; + }, + + /** + * Add a new button + * @param {object} config Button configuration object, base string name or function + * @param {int|string} [idx] Button index for where to insert the button + * @return {Buttons} Self for chaining + */ + add: function ( config, idx ) + { + var buttons = this.s.buttons; + + if ( typeof idx === 'string' ) { + var split = idx.split('-'); + var base = this.s; + + for ( var i=0, ien=split.length-1 ; i=0 ; i-- ) { + this.remove( button.buttons[i].node ); + } + } + + // Allow the button to remove event handlers, etc + if ( button.conf.destroy ) { + button.conf.destroy.call( dt.button(node), dt, $(node), button.conf ); + } + + this._removeKey( button.conf ); + + $(button.node).remove(); + + var idx = $.inArray( button, host ); + host.splice( idx, 1 ); + + return this; + }, + + /** + * Get the text for a button + * @param {int|string} node Button index + * @return {string} Button text + *//** + * Set the text for a button + * @param {int|string|function} node Button index + * @param {string} label Text + * @return {Buttons} Self for chaining + */ + text: function ( node, label ) + { + var button = this._nodeToButton( node ); + var buttonLiner = this.c.dom.collection.buttonLiner; + var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ? + buttonLiner.tag : + this.c.dom.buttonLiner.tag; + var dt = this.s.dt; + var jqNode = $(button.node); + var text = function ( opt ) { + return typeof opt === 'function' ? + opt( dt, jqNode, button.conf ) : + opt; + }; + + if ( label === undefined ) { + return text( button.conf.text ); + } + + button.conf.text = label; + + if ( linerTag ) { + jqNode.children( linerTag ).html( text(label) ); + } + else { + jqNode.html( text(label) ); + } + + return this; + }, + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Constructor + */ + + /** + * Buttons constructor + * @private + */ + _constructor: function () + { + var that = this; + var dt = this.s.dt; + var dtSettings = dt.settings()[0]; + var buttons = this.c.buttons; + + if ( ! dtSettings._buttons ) { + dtSettings._buttons = []; + } + + dtSettings._buttons.push( { + inst: this, + name: this.c.name + } ); + + for ( var i=0, ien=buttons.length ; i'); + + built.conf._collection = built.collection; + + this._expandButton( built.buttons, built.conf.buttons, true, attachPoint ); + } + + // init call is made here, rather than buildButton as it needs to + // be selectable, and for that it needs to be in the buttons array + if ( conf.init ) { + conf.init.call( dt.button( built.node ), dt, $(built.node), conf ); + } + + buttonCounter++; + } + }, + + /** + * Create an individual button + * @param {object} config Resolved button configuration + * @param {boolean} inCollection `true` if a collection button + * @return {jQuery} Created button node (jQuery) + * @private + */ + _buildButton: function ( config, inCollection ) + { + var buttonDom = this.c.dom.button; + var linerDom = this.c.dom.buttonLiner; + var collectionDom = this.c.dom.collection; + var dt = this.s.dt; + var text = function ( opt ) { + return typeof opt === 'function' ? + opt( dt, button, config ) : + opt; + }; + + if ( inCollection && collectionDom.button ) { + buttonDom = collectionDom.button; + } + + if ( inCollection && collectionDom.buttonLiner ) { + linerDom = collectionDom.buttonLiner; + } + + // Make sure that the button is available based on whatever requirements + // it has. For example, Flash buttons require Flash + if ( config.available && ! config.available( dt, config ) ) { + return false; + } + + var action = function ( e, dt, button, config ) { + config.action.call( dt.button( button ), e, dt, button, config ); + + $(dt.table().node()).triggerHandler( 'buttons-action.dt', [ + dt.button( button ), dt, button, config + ] ); + }; + + var tag = config.tag || buttonDom.tag; + var clickBlurs = config.clickBlurs === undefined ? true : config.clickBlurs + var button = $('<'+tag+'/>') + .addClass( buttonDom.className ) + .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex ) + .attr( 'aria-controls', this.s.dt.table().node().id ) + .on( 'click.dtb', function (e) { + e.preventDefault(); + + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { + action( e, dt, button, config ); + } + if( clickBlurs ) { + button.blur(); + } + } ) + .on( 'keyup.dtb', function (e) { + if ( e.keyCode === 13 ) { + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { + action( e, dt, button, config ); + } + } + } ); + + // Make `a` tags act like a link + if ( tag.toLowerCase() === 'a' ) { + button.attr( 'href', '#' ); + } + + // Button tags should have `type=button` so they don't have any default behaviour + if ( tag.toLowerCase() === 'button' ) { + button.attr( 'type', 'button' ); + } + + if ( linerDom.tag ) { + var liner = $('<'+linerDom.tag+'/>') + .html( text( config.text ) ) + .addClass( linerDom.className ); + + if ( linerDom.tag.toLowerCase() === 'a' ) { + liner.attr( 'href', '#' ); + } + + button.append( liner ); + } + else { + button.html( text( config.text ) ); + } + + if ( config.enabled === false ) { + button.addClass( buttonDom.disabled ); + } + + if ( config.className ) { + button.addClass( config.className ); + } + + if ( config.titleAttr ) { + button.attr( 'title', text( config.titleAttr ) ); + } + + if ( config.attr ) { + button.attr( config.attr ); + } + + if ( ! config.namespace ) { + config.namespace = '.dt-button-'+(_buttonCounter++); + } + + var buttonContainer = this.c.dom.buttonContainer; + var inserter; + if ( buttonContainer && buttonContainer.tag ) { + inserter = $('<'+buttonContainer.tag+'/>') + .addClass( buttonContainer.className ) + .append( button ); + } + else { + inserter = button; + } + + this._addKey( config ); + + // Style integration callback for DOM manipulation + // Note that this is _not_ documented. It is currently + // for style integration only + if( this.c.buttonCreated ) { + inserter = this.c.buttonCreated( config, inserter ); + } + + return { + conf: config, + node: button.get(0), + inserter: inserter, + buttons: [], + inCollection: inCollection, + collection: null + }; + }, + + /** + * Get the button object from a node (recursive) + * @param {node} node Button node + * @param {array} [buttons] Button array, uses base if not defined + * @return {object} Button object + * @private + */ + _nodeToButton: function ( node, buttons ) + { + if ( ! buttons ) { + buttons = this.s.buttons; + } + + for ( var i=0, ien=buttons.length ; i 30 ) { + // Protect against misconfiguration killing the browser + throw 'Buttons: Too many iterations'; + } + } + + return $.isArray( base ) ? + base : + $.extend( {}, base ); + }; + + conf = toConfObject( conf ); + + while ( conf && conf.extend ) { + // Use `toConfObject` in case the button definition being extended + // is itself a string or a function + if ( ! _dtButtons[ conf.extend ] ) { + throw 'Cannot extend unknown button type: '+conf.extend; + } + + var objArray = toConfObject( _dtButtons[ conf.extend ] ); + if ( $.isArray( objArray ) ) { + return objArray; + } + else if ( ! objArray ) { + // This is a little brutal as it might be possible to have a + // valid button without the extend, but if there is no extend + // then the host button would be acting in an undefined state + return false; + } + + // Stash the current class name + var originalClassName = objArray.className; + + conf = $.extend( {}, objArray, conf ); + + // The extend will have overwritten the original class name if the + // `conf` object also assigned a class, but we want to concatenate + // them so they are list that is combined from all extended buttons + if ( originalClassName && conf.className !== originalClassName ) { + conf.className = originalClassName+' '+conf.className; + } + + // Buttons to be added to a collection -gives the ability to define + // if buttons should be added to the start or end of a collection + var postfixButtons = conf.postfixButtons; + if ( postfixButtons ) { + if ( ! conf.buttons ) { + conf.buttons = []; + } + + for ( i=0, ien=postfixButtons.length ; i') + .addClass('dt-button-collection') + .addClass(options.collectionLayout) + .css('display', 'none'); + + content = $(content) + .addClass(options.contentClassName) + .attr('role', 'menu') + .appendTo(display); + + hostNode.attr( 'aria-expanded', 'true' ); + + if ( hostNode.parents('body')[0] !== document.body ) { + hostNode = document.body.lastChild; + } + + if ( options.collectionTitle ) { + display.prepend('
    '+options.collectionTitle+'
    '); + } + + display + .insertAfter( hostNode ) + .fadeIn( options.fade ); + + var tableContainer = $( hostButton.table().container() ); + var position = display.css( 'position' ); + + if ( options.align === 'dt-container' ) { + hostNode = hostNode.parent(); + display.css('width', tableContainer.width()); + } + + if ( position === 'absolute' ) { + var hostPosition = hostNode.position(); + + display.css( { + top: hostPosition.top + hostNode.outerHeight(), + left: hostPosition.left + } ); + + // calculate overflow when positioned beneath + var collectionHeight = display.outerHeight(); + var collectionWidth = display.outerWidth(); + var tableBottom = tableContainer.offset().top + tableContainer.height(); + var listBottom = hostPosition.top + hostNode.outerHeight() + collectionHeight; + var bottomOverflow = listBottom - tableBottom; + + // calculate overflow when positioned above + var listTop = hostPosition.top - collectionHeight; + var tableTop = tableContainer.offset().top; + var topOverflow = tableTop - listTop; + + // if bottom overflow is larger, move to the top because it fits better, or if dropup is requested + var moveTop = hostPosition.top - collectionHeight - 5; + if ( (bottomOverflow > topOverflow || options.dropup) && -moveTop < tableTop ) { + display.css( 'top', moveTop); + } + + // Right alignment is enabled on a class, e.g. bootstrap: + // $.fn.dataTable.Buttons.defaults.dom.collection.className += " dropdown-menu-right"; + if ( display.hasClass( options.rightAlignClassName ) || options.align === 'button-right' ) { + display.css( 'left', hostPosition.left + hostNode.outerWidth() - collectionWidth ); + } + + // Right alignment in table container + var listRight = hostPosition.left + collectionWidth; + var tableRight = tableContainer.offset().left + tableContainer.width(); + if ( listRight > tableRight ) { + display.css( 'left', hostPosition.left - ( listRight - tableRight ) ); + } + + // Right alignment to window + var listOffsetRight = hostNode.offset().left + collectionWidth; + if ( listOffsetRight > $(window).width() ) { + display.css( 'left', hostPosition.left - (listOffsetRight-$(window).width()) ); + } + } + else { + // Fix position - centre on screen + var top = display.height() / 2; + if ( top > $(window).height() / 2 ) { + top = $(window).height() / 2; + } + + display.css( 'marginTop', top*-1 ); + } + + if ( options.background ) { + Buttons.background( true, options.backgroundClassName, options.fade, hostNode ); + } + + // This is bonkers, but if we don't have a click listener on the + // background element, iOS Safari will ignore the body click + // listener below. An empty function here is all that is + // required to make it work... + $('div.dt-button-background').on( 'click.dtb-collection', function () {} ); + + $('body') + .on( 'click.dtb-collection', function (e) { + // andSelf is deprecated in jQ1.8, but we want 1.7 compat + var back = $.fn.addBack ? 'addBack' : 'andSelf'; + + if ( ! $(e.target).parents()[back]().filter( content ).length ) { + close(); + } + } ) + .on( 'keyup.dtb-collection', function (e) { + if ( e.keyCode === 27 ) { + close(); + } + } ); + + if ( options.autoClose ) { + setTimeout( function () { + dt.on( 'buttons-action.b-internal', function (e, btn, dt, node) { + if ( node[0] === hostNode[0] ) { + return; + } + close(); + } ); + }, 0); + } + } +} ); + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Statics + */ + +/** + * Show / hide a background layer behind a collection + * @param {boolean} Flag to indicate if the background should be shown or + * hidden + * @param {string} Class to assign to the background + * @static + */ +Buttons.background = function ( show, className, fade, insertPoint ) { + if ( fade === undefined ) { + fade = 400; + } + if ( ! insertPoint ) { + insertPoint = document.body; + } + + if ( show ) { + $('
    ') + .addClass( className ) + .css( 'display', 'none' ) + .insertAfter( insertPoint ) + .stop() + .fadeIn( fade ); + } + else { + $('div.'+className) + .stop() + .fadeOut( fade, function () { + $(this) + .removeClass( className ) + .remove(); + } ); + } +}; + +/** + * Instance selector - select Buttons instances based on an instance selector + * value from the buttons assigned to a DataTable. This is only useful if + * multiple instances are attached to a DataTable. + * @param {string|int|array} Instance selector - see `instance-selector` + * documentation on the DataTables site + * @param {array} Button instance array that was attached to the DataTables + * settings object + * @return {array} Buttons instances + * @static + */ +Buttons.instanceSelector = function ( group, buttons ) +{ + if ( group === undefined || group === null ) { + return $.map( buttons, function ( v ) { + return v.inst; + } ); + } + + var ret = []; + var names = $.map( buttons, function ( v ) { + return v.name; + } ); + + // Flatten the group selector into an array of single options + var process = function ( input ) { + if ( $.isArray( input ) ) { + for ( var i=0, ien=input.length ; i` in IE - it has to be `` + tag: 'ActiveXObject' in window ? + 'a' : + 'button', + className: 'dt-button', + active: 'active', + disabled: 'disabled' + }, + buttonLiner: { + tag: 'span', + className: '' + } + } +}; + +/** + * Version information + * @type {string} + * @static + */ +Buttons.version = '1.6.1'; + + +$.extend( _dtButtons, { + collection: { + text: function ( dt ) { + return dt.i18n( 'buttons.collection', 'Collection' ); + }, + className: 'buttons-collection', + init: function ( dt, button, config ) { + button.attr( 'aria-expanded', false ); + }, + action: function ( e, dt, button, config ) { + e.stopPropagation(); + + if ( config._collection.parents('body').length ) { + this.popover(false, config); + } + else { + this.popover(config._collection, config); + } + }, + attr: { + 'aria-haspopup': true + } + // Also the popover options, defined in Buttons.popover + }, + copy: function ( dt, conf ) { + if ( _dtButtons.copyHtml5 ) { + return 'copyHtml5'; + } + if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) { + return 'copyFlash'; + } + }, + csv: function ( dt, conf ) { + // Common option that will use the HTML5 or Flash export buttons + if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) { + return 'csvHtml5'; + } + if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) { + return 'csvFlash'; + } + }, + excel: function ( dt, conf ) { + // Common option that will use the HTML5 or Flash export buttons + if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) { + return 'excelHtml5'; + } + if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) { + return 'excelFlash'; + } + }, + pdf: function ( dt, conf ) { + // Common option that will use the HTML5 or Flash export buttons + if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) { + return 'pdfHtml5'; + } + if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) { + return 'pdfFlash'; + } + }, + pageLength: function ( dt ) { + var lengthMenu = dt.settings()[0].aLengthMenu; + var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu; + var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu; + var text = function ( dt ) { + return dt.i18n( 'buttons.pageLength', { + "-1": 'Show all rows', + _: 'Show %d rows' + }, dt.page.len() ); + }; + + return { + extend: 'collection', + text: text, + className: 'buttons-page-length', + autoClose: true, + buttons: $.map( vals, function ( val, i ) { + return { + text: lang[i], + className: 'button-page-length', + action: function ( e, dt ) { + dt.page.len( val ).draw(); + }, + init: function ( dt, node, conf ) { + var that = this; + var fn = function () { + that.active( dt.page.len() === val ); + }; + + dt.on( 'length.dt'+conf.namespace, fn ); + fn(); + }, + destroy: function ( dt, node, conf ) { + dt.off( 'length.dt'+conf.namespace ); + } + }; + } ), + init: function ( dt, node, conf ) { + var that = this; + dt.on( 'length.dt'+conf.namespace, function () { + that.text( conf.text ); + } ); + }, + destroy: function ( dt, node, conf ) { + dt.off( 'length.dt'+conf.namespace ); + } + }; + } +} ); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * DataTables API + * + * For complete documentation, please refer to the docs/api directory or the + * DataTables site + */ + +// Buttons group and individual button selector +DataTable.Api.register( 'buttons()', function ( group, selector ) { + // Argument shifting + if ( selector === undefined ) { + selector = group; + group = undefined; + } + + this.selector.buttonGroup = group; + + var res = this.iterator( true, 'table', function ( ctx ) { + if ( ctx._buttons ) { + return Buttons.buttonSelector( + Buttons.instanceSelector( group, ctx._buttons ), + selector + ); + } + }, true ); + + res._groupSelector = group; + return res; +} ); + +// Individual button selector +DataTable.Api.register( 'button()', function ( group, selector ) { + // just run buttons() and truncate + var buttons = this.buttons( group, selector ); + + if ( buttons.length > 1 ) { + buttons.splice( 1, buttons.length ); + } + + return buttons; +} ); + +// Active buttons +DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) { + if ( flag === undefined ) { + return this.map( function ( set ) { + return set.inst.active( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.active( set.node, flag ); + } ); +} ); + +// Get / set button action +DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) { + if ( action === undefined ) { + return this.map( function ( set ) { + return set.inst.action( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.action( set.node, action ); + } ); +} ); + +// Enable / disable buttons +DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) { + return this.each( function ( set ) { + set.inst.enable( set.node, flag ); + } ); +} ); + +// Disable buttons +DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () { + return this.each( function ( set ) { + set.inst.disable( set.node ); + } ); +} ); + +// Get button nodes +DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () { + var jq = $(); + + // jQuery will automatically reduce duplicates to a single entry + $( this.each( function ( set ) { + jq = jq.add( set.inst.node( set.node ) ); + } ) ); + + return jq; +} ); + +// Get / set button processing state +DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) { + if ( flag === undefined ) { + return this.map( function ( set ) { + return set.inst.processing( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.processing( set.node, flag ); + } ); +} ); + +// Get / set button text (i.e. the button labels) +DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) { + if ( label === undefined ) { + return this.map( function ( set ) { + return set.inst.text( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.text( set.node, label ); + } ); +} ); + +// Trigger a button's action +DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () { + return this.each( function ( set ) { + set.inst.node( set.node ).trigger( 'click' ); + } ); +} ); + +// Button resolver to the popover +DataTable.Api.register( 'button().popover()', function (content, options) { + return this.map( function ( set ) { + return set.inst._popover( content, this.button(this[0].node), options ); + } ); +} ); + +// Get the container elements +DataTable.Api.register( 'buttons().containers()', function () { + var jq = $(); + var groupSelector = this._groupSelector; + + // We need to use the group selector directly, since if there are no buttons + // the result set will be empty + this.iterator( true, 'table', function ( ctx ) { + if ( ctx._buttons ) { + var insts = Buttons.instanceSelector( groupSelector, ctx._buttons ); + + for ( var i=0, ien=insts.length ; i'+title+'' : ''; + + $('
    ') + .html( title ) + .append( $('
    ')[ typeof message === 'string' ? 'html' : 'append' ]( message ) ) + .css( 'display', 'none' ) + .appendTo( 'body' ) + .fadeIn(); + + if ( time !== undefined && time !== 0 ) { + _infoTimer = setTimeout( function () { + that.buttons.info( false ); + }, time ); + } + + this.on('destroy.btn-info', function () { + that.buttons.info(false); + }); + + return this; +} ); + +// Get data from the table for export - this is common to a number of plug-in +// buttons so it is included in the Buttons core library +DataTable.Api.register( 'buttons.exportData()', function ( options ) { + if ( this.context.length ) { + return _exportData( new DataTable.Api( this.context[0] ), options ); + } +} ); + +// Get information about the export that is common to many of the export data +// types (DRY) +DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) { + if ( ! conf ) { + conf = {}; + } + + return { + filename: _filename( conf ), + title: _title( conf ), + messageTop: _message(this, conf.message || conf.messageTop, 'top'), + messageBottom: _message(this, conf.messageBottom, 'bottom') + }; +} ); + + + +/** + * Get the file name for an exported file. + * + * @param {object} config Button configuration + * @param {boolean} incExtension Include the file name extension + */ +var _filename = function ( config ) +{ + // Backwards compatibility + var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ? + config.title : + config.filename; + + if ( typeof filename === 'function' ) { + filename = filename(); + } + + if ( filename === undefined || filename === null ) { + return null; + } + + if ( filename.indexOf( '*' ) !== -1 ) { + filename = $.trim( filename.replace( '*', $('head > title').text() ) ); + } + + // Strip characters which the OS will object to + filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); + + var extension = _stringOrFunction( config.extension ); + if ( ! extension ) { + extension = ''; + } + + return filename + extension; +}; + +/** + * Simply utility method to allow parameters to be given as a function + * + * @param {undefined|string|function} option Option + * @return {null|string} Resolved value + */ +var _stringOrFunction = function ( option ) +{ + if ( option === null || option === undefined ) { + return null; + } + else if ( typeof option === 'function' ) { + return option(); + } + return option; +}; + +/** + * Get the title for an exported file. + * + * @param {object} config Button configuration + */ +var _title = function ( config ) +{ + var title = _stringOrFunction( config.title ); + + return title === null ? + null : title.indexOf( '*' ) !== -1 ? + title.replace( '*', $('head > title').text() || 'Exported data' ) : + title; +}; + +var _message = function ( dt, option, position ) +{ + var message = _stringOrFunction( option ); + if ( message === null ) { + return null; + } + + var caption = $('caption', dt.table().container()).eq(0); + if ( message === '*' ) { + var side = caption.css( 'caption-side' ); + if ( side !== position ) { + return null; + } + + return caption.length ? + caption.text() : + ''; + } + + return message; +}; + + + + + + + +var _exportTextarea = $('