From fa02a041b826fcc11d98df3fefd2da567ec2d923 Mon Sep 17 00:00:00 2001 From: CedricCarard Date: Thu, 14 Mar 2019 18:25:00 +0100 Subject: [PATCH 1/8] add option user pip install command rundemo --- Makefile | 4 ++-- README.rst | 2 +- demo/README.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c79dbbf1..45e7c764 100644 --- a/Makefile +++ b/Makefile @@ -83,8 +83,8 @@ readme: #: demo - Setup demo project using Python3. .PHONY: demo demo: - $(PIP) install -e . - $(PIP) install -e demo + $(PIP) install -e . --user + $(PIP) install -e demo --user demodesk migrate --noinput # Create superuser; user will be prompted to manually set a password # When you get a prompt, enter a password of your choosing. diff --git a/README.rst b/README.rst index a4521c28..e93c933a 100644 --- a/README.rst +++ b/README.rst @@ -41,7 +41,7 @@ resides in the `demo/` top-level folder. It's likely that you can start up a demo project server by running only the command:: - sudo make rundemo + make rundemo then pointing your web browser at `localhost:8080`. diff --git a/demo/README.rst b/demo/README.rst index 52d980bd..4d9c9128 100644 --- a/demo/README.rst +++ b/demo/README.rst @@ -28,7 +28,7 @@ Ideally, you'd use a virtualenv instead To use your system directory, from the top-level django-helpdesk directory, simply run: - sudo make rundemo + make rundemo Once the console gives a prompt that the HTTP server is listening, open your web browser From e4d6a54659302b9fe921ad0ad7a0857c4777f598 Mon Sep 17 00:00:00 2001 From: Cedric Carrard Date: Fri, 15 Mar 2019 13:57:43 +0100 Subject: [PATCH 2/8] fix public homepage with kb_category but without kb disabled --- helpdesk/tests/test_navigation.py | 9 +++++++++ helpdesk/views/public.py | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/helpdesk/tests/test_navigation.py b/helpdesk/tests/test_navigation.py index 1bde969a..18e2fbfa 100644 --- a/helpdesk/tests/test_navigation.py +++ b/helpdesk/tests/test_navigation.py @@ -2,6 +2,7 @@ from django.urls import reverse from django.test import TestCase +from helpdesk.models import KBCategory from helpdesk.tests.helpers import get_staff_user, reload_urlconf @@ -36,3 +37,11 @@ class TestKBDisabled(TestCase): raise else: self.assertEqual(response.status_code, 200) + + def test_public_homepage_with_kb_category(self): + KBCategory.objects.create(title="KB Cat 1", + slug="kbcat1", + description="Some category of KB info") + response = self.client.get(reverse('helpdesk:home')) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'helpdesk/public_homepage.html') diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index fbca7571..86166e79 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -87,7 +87,10 @@ def homepage(request): form.fields['queue'].choices = [('', '--------')] + [ (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)] - knowledgebase_categories = KBCategory.objects.all() + knowledgebase_categories = None + + if helpdesk_settings.HELPDESK_KB_ENABLED: + knowledgebase_categories = KBCategory.objects.all() return render(request, 'helpdesk/public_homepage.html', { 'form': form, From a10a597b3a9815947dc174f738d329c29779eb44 Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Fri, 15 Mar 2019 16:21:29 -0400 Subject: [PATCH 3/8] Add issue templates Initial issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..f3d5c415 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..11fc491e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From ba9c6b31bfb346c1fcde9afb57c4912ccea9f1c4 Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Fri, 15 Mar 2019 16:53:35 -0400 Subject: [PATCH 4/8] Update CONTRIBUTING --- CONTRIBUTING.rst | 112 ++++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5cf75977..d249e64e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,6 +1,10 @@ Contributing ============ +We're really glad you're reading this and considering contributing to +`django-helpdesk`! As an open source project, we rely on volunteers +to improve and grow the project. Welcome! + django-helpdesk is an open-source project and as such contributions from the community are welcomed and encouraged! @@ -10,38 +14,20 @@ repository is available at: https://github.com/django-helpdesk/django-helpdesk -Licensing ---------- -All contributions to django-helpdesk *must* be under the BSD license documented -in the LICENSE file in the top-level directory of this project. +Testing +------- -By submitting a contribution to this project (in any way: via e-mail, -via GitHub pull requests, ticket attachments, etc), you acknowledge that your -contribution is open-source and licensed under the BSD license. +If you don't mind testing pre-releases (don't use in production!), we appreciate +continuous feedback on the `develop` branch, which is our work toward the next +major release. Please file bug reports, and tag the report with the "pre-release" +tag. -If you or your organisation does not accept these license terms then we cannot -accept your contribution. Please reconsider! +This is an easy way to get involved that doesn't require programming. -Ways to Contribute ------------------- -We're happy to include any type of contribution! This can be: - -* back-end python/django code development -* front-end web development (HTML/Javascript, especially jQuery) -* language translations -* writing improved documentation and demos - -More details on each of theses tasks is below. - -If you have any questions on contributing, please start a discussion on -the GitHub issue tracker at - -https://github.com/django-helpdesk/django-helpdesk/issues - -Back-end Python/Django -`````````````````````` +Pull Requests +------------- Please fork the project on GitHub, make your changes, and submit a pull request back into the appropriate branch of the @@ -89,8 +75,35 @@ If your changes affect the Django models for `django-helpdesk`, be aware that your commits should include database schema python scripts; see the Database Schema Changes section below for more details. + +Coding Conventions +------------------ + +Be sure all Python code follows PEP8 conventions. + +Ideally, add comments and documentation whenever you touch code. + +HTML and Javascript templates should be appropriately indented. + + +Database schema changes +----------------------- + +As well as making your normal code changes to ``models.py``, please generate a +Django migration file and commit it with your code. You will want to use a +command similar to the following:: + + ./manage.py migrate helpdesk --auto [migration_name] + +Make sure that ``migration_name`` is a sensible single-string explanation of +what this migration does, such as *add_priority_options* or *add_basket_table*. + +This will add a file to the ``migrations/`` folder, which must be committed to +git with your other code changes. + + Tests -..... +----- Currently, test coverage is very low. We're working on increasing this, and to make life easier we are using Travis CI (http://travis-ci.org/) for continuous @@ -118,23 +131,27 @@ start a discussion on the GitHub issue tracker at https://github.com/django-helpdesk/django-helpdesk/issues -Database schema changes -....................... -As well as making your normal code changes to ``models.py``, please generate a -Django migration file and commit it with your code. You will want to use a -command similar to the following:: +Ways to Contribute +------------------ - ./manage.py migrate helpdesk --auto [migration_name] +We're happy to include any type of contribution! This can be: -Make sure that ``migration_name`` is a sensible single-string explanation of -what this migration does, such as *add_priority_options* or *add_basket_table*. +* back-end python/django code development +* front-end web development (HTML/Javascript, especially jQuery) +* language translations +* writing improved documentation and demos + +More details on each of theses tasks is below. + +If you have any questions on contributing, please start a discussion on +the GitHub issue tracker at + +https://github.com/django-helpdesk/django-helpdesk/issues -This will add a file to the ``migrations/`` folder, which must be committed to -git with your other code changes. Translations -```````````` +------------ Although `django-helpdesk` has originally been written for the English language, there are already multiple community translations, including Spanish, Polish, @@ -149,4 +166,19 @@ project: http://www.transifex.net/projects/p/django-helpdesk/resource/core/ Once you have translated content via Transifex, please raise an issue on the -project Github page to let us know it's ready to import. +project Github page and tag it as "translations" to let us know it's ready to +import. + + +Licensing +--------- + +All contributions to django-helpdesk *must* be under the BSD license documented +in the LICENSE file in the top-level directory of this project. + +By submitting a contribution to this project (in any way: via e-mail, +via GitHub pull requests, ticket attachments, etc), you acknowledge that your +contribution is open-source and licensed under the BSD license. + +If you or your organization does not accept these license terms then we cannot +accept your contribution. Please reconsider! From 9c85b8e8c1b30a432cdbabd599f8cf7e5c233123 Mon Sep 17 00:00:00 2001 From: Cedric Carrard Date: Wed, 20 Mar 2019 13:47:57 +0100 Subject: [PATCH 5/8] fix redirect loop --- helpdesk/decorators.py | 41 +++++++++- helpdesk/tests/helpers.py | 12 ++- helpdesk/tests/test_navigation.py | 49 +++++++++++- helpdesk/tests/test_savequery.py | 4 +- helpdesk/views/staff.py | 127 +++++++----------------------- 5 files changed, 126 insertions(+), 107 deletions(-) diff --git a/helpdesk/decorators.py b/helpdesk/decorators.py index 47dc5911..3ef8dfec 100644 --- a/helpdesk/decorators.py +++ b/helpdesk/decorators.py @@ -1,9 +1,12 @@ from functools import wraps -from django.urls import reverse -from django.http import HttpResponseRedirect, Http404 +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.shortcuts import redirect + from django.utils.decorators import available_attrs + from helpdesk import settings as helpdesk_settings @@ -15,9 +18,41 @@ def protect_view(view_func): @wraps(view_func, assigned=available_attrs(view_func)) def _wrapped_view(request, *args, **kwargs): if not request.user.is_authenticated and helpdesk_settings.HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT: - return HttpResponseRedirect(reverse('helpdesk:login')) + return redirect('helpdesk:login') elif not request.user.is_authenticated and helpdesk_settings.HELPDESK_ANON_ACCESS_RAISES_404: raise Http404 return view_func(request, *args, **kwargs) return _wrapped_view + + +def staff_member_required(view_func): + """ + Decorator for staff member the views checking user, redirecting + to the log-in page if necessary or returning 403 + """ + @wraps(view_func, assigned=available_attrs(view_func)) + def _wrapped_view(request, *args, **kwargs): + if not request.user.is_authenticated and not request.user.is_active: + return redirect('helpdesk:login') + if not helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE and not request.user.is_staff: + raise PermissionDenied() + return view_func(request, *args, **kwargs) + + return _wrapped_view + + +def superuser_required(view_func): + """ + Decorator for superuser member the views checking user, redirecting + to the log-in page if necessary or returning 403 + """ + @wraps(view_func, assigned=available_attrs(view_func)) + def _wrapped_view(request, *args, **kwargs): + if not request.user.is_authenticated and not request.user.is_active: + return redirect('helpdesk:login') + if not request.user.is_superuser: + raise PermissionDenied() + return view_func(request, *args, **kwargs) + + return _wrapped_view diff --git a/helpdesk/tests/helpers.py b/helpdesk/tests/helpers.py index 5d6c6bfc..39172978 100644 --- a/helpdesk/tests/helpers.py +++ b/helpdesk/tests/helpers.py @@ -5,12 +5,18 @@ from django.contrib.auth import get_user_model User = get_user_model() -def get_staff_user(username='helpdesk.staff', password='password'): +def get_user(username='helpdesk.staff', + password='password', + is_staff=False, + is_superuser=False): try: user = User.objects.get(username=username) except User.DoesNotExist: - user = User.objects.create_user(username=username, password=password, email='staff@example.com') - user.is_staff = True + user = User.objects.create_user(username=username, + password=password, + email='%s@example.com' % username) + user.is_staff = is_staff + user.is_superuser = is_superuser user.save() else: user.set_password(password) diff --git a/helpdesk/tests/test_navigation.py b/helpdesk/tests/test_navigation.py index 18e2fbfa..8da9e8d9 100644 --- a/helpdesk/tests/test_navigation.py +++ b/helpdesk/tests/test_navigation.py @@ -3,7 +3,7 @@ from django.urls import reverse from django.test import TestCase from helpdesk.models import KBCategory -from helpdesk.tests.helpers import get_staff_user, reload_urlconf +from helpdesk.tests.helpers import get_user, reload_urlconf class TestKBDisabled(TestCase): @@ -26,7 +26,7 @@ class TestKBDisabled(TestCase): """Test proper rendering of navigation.html by accessing the dashboard""" from django.urls import NoReverseMatch - self.client.login(username=get_staff_user().get_username(), password='password') + self.client.login(username=get_user(is_staff=True).get_username(), password='password') self.assertRaises(NoReverseMatch, reverse, 'helpdesk:kb_index') try: response = self.client.get(reverse('helpdesk:dashboard')) @@ -45,3 +45,48 @@ class TestKBDisabled(TestCase): response = self.client.get(reverse('helpdesk:home')) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'helpdesk/public_homepage.html') + + +class TestDecorator(TestCase): + + def test_staff_member_restrictions(self): + user = get_user(username='helpdesk.user', + password='password') + + self.client.login(username=user.get_username(), + password='password') + response = self.client.get(reverse('helpdesk:list')) + self.assertEqual(response.status_code, 403) + + def test_staff_member_access(self): + user = get_user(username='helpdesk.user', + password='password', + is_staff=True) + + self.client.login(username=user.get_username(), + password='password') + response = self.client.get(reverse('helpdesk:list')) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'helpdesk/ticket_list.html') + + def test_superuser_member_restrictions(self): + user = get_user(username='helpdesk.superuser', + password='password', + is_staff=True) + + self.client.login(username=user.get_username(), + password='password') + response = self.client.get(reverse('helpdesk:email_ignore')) + self.assertEqual(response.status_code, 403) + + def test_superuser_member_access(self): + user = get_user(username='helpdesk.superuser', + password='password', + is_staff=True, + is_superuser=True) + + self.client.login(username=user.get_username(), + password='password') + response = self.client.get(reverse('helpdesk:email_ignore')) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'helpdesk/email_ignore_list.html') diff --git a/helpdesk/tests/test_savequery.py b/helpdesk/tests/test_savequery.py index 8838b597..5916782b 100644 --- a/helpdesk/tests/test_savequery.py +++ b/helpdesk/tests/test_savequery.py @@ -2,7 +2,7 @@ from django.urls import reverse from django.test import TestCase from helpdesk.models import Queue -from helpdesk.tests.helpers import get_staff_user +from helpdesk.tests.helpers import get_user class TestSavingSharedQuery(TestCase): @@ -14,7 +14,7 @@ class TestSavingSharedQuery(TestCase): def test_cansavequery(self): """Can a query be saved""" url = reverse('helpdesk:savequery') - self.client.login(username=get_staff_user().get_username(), + self.client.login(username=get_user(is_staff=True).get_username(), password='password') response = self.client.post( url, diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index fb6ead3e..ff496156 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -13,10 +13,8 @@ import re from django import VERSION as DJANGO_VERSION from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.auth.decorators import user_passes_test from django.urls import reverse from django.core.exceptions import ValidationError, PermissionDenied -from django.db import connection from django.db.models import Q from django.http import HttpResponseRedirect, Http404, HttpResponse from django.shortcuts import render, get_object_or_404 @@ -32,8 +30,9 @@ from helpdesk.forms import ( TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm, TicketCCEmailForm, TicketCCUserForm, EditFollowUpForm, TicketDependencyForm ) +from helpdesk.decorators import staff_member_required, superuser_required from helpdesk.lib import ( - send_templated_mail, query_to_dict, apply_query, safe_template_context, + send_templated_mail, apply_query, safe_template_context, process_attachments, queue_template_context, ) from helpdesk.models import ( @@ -42,22 +41,10 @@ from helpdesk.models import ( ) from helpdesk import settings as helpdesk_settings + User = get_user_model() -if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: - # treat 'normal' users like 'staff' - staff_member_required = user_passes_test( - lambda u: u.is_authenticated and u.is_active) -else: - staff_member_required = user_passes_test( - lambda u: u.is_authenticated and u.is_active and u.is_staff) - - -superuser_required = user_passes_test( - lambda u: u.is_authenticated and u.is_active and u.is_superuser) - - def _get_user_queues(user): """Return the list of Queues the user can access. @@ -97,6 +84,7 @@ def _is_my_ticket(user, ticket): return False +@staff_member_required def dashboard(request): """ A quick summary overview for users: A list of their own tickets, a table @@ -162,9 +150,7 @@ def dashboard(request): }) -dashboard = staff_member_required(dashboard) - - +@staff_member_required def delete_ticket(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -181,9 +167,7 @@ def delete_ticket(request, ticket_id): return HttpResponseRedirect(reverse('helpdesk:home')) -delete_ticket = staff_member_required(delete_ticket) - - +@staff_member_required def followup_edit(request, ticket_id, followup_id): """Edit followup options with an ability to change the ticket.""" followup = get_object_or_404(FollowUp, id=followup_id) @@ -236,9 +220,7 @@ def followup_edit(request, ticket_id, followup_id): return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id])) -followup_edit = staff_member_required(followup_edit) - - +@staff_member_required def followup_delete(request, ticket_id, followup_id): """followup delete for superuser""" @@ -251,9 +233,7 @@ def followup_delete(request, ticket_id, followup_id): return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id])) -followup_delete = staff_member_required(followup_delete) - - +@staff_member_required def view_ticket(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -323,9 +303,6 @@ def view_ticket(request, ticket_id): }) -view_ticket = staff_member_required(view_ticket) - - def return_ticketccstring_and_show_subscribe(user, ticket): """used in view_ticket() and followup_edit()""" # create the ticketcc_string and check whether current user is already @@ -666,6 +643,7 @@ def return_to_ticket(user, helpdesk_settings, ticket): return HttpResponseRedirect(ticket.ticket_url) +@staff_member_required def mass_update(request): tickets = request.POST.getlist('ticket_id') action = request.POST.get('action', None) @@ -781,9 +759,7 @@ def mass_update(request): return HttpResponseRedirect(reverse('helpdesk:list')) -mass_update = staff_member_required(mass_update) - - +@staff_member_required def ticket_list(request): context = {} @@ -970,9 +946,7 @@ def ticket_list(request): )) -ticket_list = staff_member_required(ticket_list) - - +@staff_member_required def edit_ticket(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -991,9 +965,7 @@ def edit_ticket(request, ticket_id): return render(request, 'helpdesk/edit_ticket.html', {'form': form}) -edit_ticket = staff_member_required(edit_ticket) - - +@staff_member_required def create_ticket(request): if helpdesk_settings.HELPDESK_STAFF_ONLY_TICKET_OWNERS: assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by(User.USERNAME_FIELD) @@ -1030,9 +1002,7 @@ def create_ticket(request): return render(request, 'helpdesk/create_ticket.html', {'form': form}) -create_ticket = staff_member_required(create_ticket) - - +@staff_member_required def raw_details(request, type): # TODO: This currently only supports spewing out 'PreSetReply' objects, # in the future it needs to be expanded to include other items. All it @@ -1051,9 +1021,7 @@ def raw_details(request, type): raise Http404 -raw_details = staff_member_required(raw_details) - - +@staff_member_required def hold_ticket(request, ticket_id, unhold=False): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1082,23 +1050,17 @@ def hold_ticket(request, ticket_id, unhold=False): return HttpResponseRedirect(ticket.get_absolute_url()) -hold_ticket = staff_member_required(hold_ticket) - - +@staff_member_required def unhold_ticket(request, ticket_id): return hold_ticket(request, ticket_id, unhold=True) -unhold_ticket = staff_member_required(unhold_ticket) - - +@staff_member_required def rss_list(request): return render(request, 'helpdesk/rss_list.html', {'queues': Queue.objects.all()}) -rss_list = staff_member_required(rss_list) - - +@staff_member_required def report_index(request): number_tickets = Ticket.objects.all().count() saved_query = request.GET.get('saved_query', None) @@ -1133,9 +1095,7 @@ def report_index(request): }) -report_index = staff_member_required(report_index) - - +@staff_member_required def run_report(request, report): if Ticket.objects.all().count() == 0 or report not in ( 'queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', @@ -1339,9 +1299,7 @@ def run_report(request, report): }) -run_report = staff_member_required(run_report) - - +@staff_member_required def save_query(request): title = request.POST.get('title', None) shared = request.POST.get('shared', False) @@ -1358,9 +1316,7 @@ def save_query(request): return HttpResponseRedirect('%s?saved_query=%s' % (reverse('helpdesk:list'), query.id)) -save_query = staff_member_required(save_query) - - +@staff_member_required def delete_saved_query(request, id): query = get_object_or_404(SavedSearch, id=id, user=request.user) @@ -1371,9 +1327,7 @@ def delete_saved_query(request, id): return render(request, 'helpdesk/confirm_delete_saved_query.html', {'query': query}) -delete_saved_query = staff_member_required(delete_saved_query) - - +@staff_member_required def user_settings(request): s = request.user.usersettings_helpdesk if request.POST: @@ -1387,18 +1341,14 @@ def user_settings(request): return render(request, 'helpdesk/user_settings.html', {'form': form}) -user_settings = staff_member_required(user_settings) - - +@superuser_required def email_ignore(request): return render(request, 'helpdesk/email_ignore_list.html', { 'ignore_list': IgnoreEmail.objects.all(), }) -email_ignore = superuser_required(email_ignore) - - +@superuser_required def email_ignore_add(request): if request.method == 'POST': form = EmailIgnoreForm(request.POST) @@ -1411,9 +1361,7 @@ def email_ignore_add(request): return render(request, 'helpdesk/email_ignore_add.html', {'form': form}) -email_ignore_add = superuser_required(email_ignore_add) - - +@superuser_required def email_ignore_del(request, id): ignore = get_object_or_404(IgnoreEmail, id=id) if request.method == 'POST': @@ -1423,9 +1371,7 @@ def email_ignore_del(request, id): return render(request, 'helpdesk/email_ignore_del.html', {'ignore': ignore}) -email_ignore_del = superuser_required(email_ignore_del) - - +@staff_member_required def ticket_cc(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1440,9 +1386,7 @@ def ticket_cc(request, ticket_id): }) -ticket_cc = staff_member_required(ticket_cc) - - +@staff_member_required def ticket_cc_add(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1468,9 +1412,7 @@ def ticket_cc_add(request, ticket_id): }) -ticket_cc_add = staff_member_required(ticket_cc_add) - - +@staff_member_required def ticket_cc_del(request, ticket_id, cc_id): cc = get_object_or_404(TicketCC, ticket__id=ticket_id, id=cc_id) @@ -1481,9 +1423,7 @@ def ticket_cc_del(request, ticket_id, cc_id): return render(request, 'helpdesk/ticket_cc_del.html', {'cc': cc}) -ticket_cc_del = staff_member_required(ticket_cc_del) - - +@staff_member_required def ticket_dependency_add(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1506,9 +1446,7 @@ def ticket_dependency_add(request, ticket_id): }) -ticket_dependency_add = staff_member_required(ticket_dependency_add) - - +@staff_member_required def ticket_dependency_del(request, ticket_id, dependency_id): dependency = get_object_or_404(TicketDependency, ticket__id=ticket_id, id=dependency_id) if request.method == 'POST': @@ -1517,9 +1455,7 @@ def ticket_dependency_del(request, ticket_id, dependency_id): return render(request, 'helpdesk/ticket_dependency_del.html', {'dependency': dependency}) -ticket_dependency_del = staff_member_required(ticket_dependency_del) - - +@staff_member_required def attachment_del(request, ticket_id, attachment_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1537,9 +1473,6 @@ def attachment_del(request, ticket_id, attachment_id): }) -attachment_del = staff_member_required(attachment_del) - - def calc_average_nbr_days_until_ticket_resolved(Tickets): nbr_closed_tickets = len(Tickets) days_per_ticket = 0 From ce018d2ce51522c82a780b7acd6ff5d8d0f9a103 Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Sat, 25 May 2019 01:01:02 -0400 Subject: [PATCH 6/8] Copy emails to CC list when processing with get_email, for #744 --- helpdesk/management/commands/get_email.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpdesk/management/commands/get_email.py b/helpdesk/management/commands/get_email.py index 5ae13079..1c608cde 100755 --- a/helpdesk/management/commands/get_email.py +++ b/helpdesk/management/commands/get_email.py @@ -545,6 +545,18 @@ def ticket_from_message(message, queue, logger): sender=queue.from_address, fail_silently=True, ) + # copy email to all those CC'd to this particular ticket + for cc in t.ticketcc_set.all(): + # don't duplicate email to assignee + if t.assigned_to.email != cc.email_address: + send_templated_mail( + 'updated_cc', + context, + recipients=cc.email_address, + sender=queue.from_address, + fail_silently=True, + ) + return t From ae262164354eeb45c58c8ee9a71893191d529108 Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Sat, 25 May 2019 01:04:05 -0400 Subject: [PATCH 7/8] PEP-8 fixes for get_email --- helpdesk/management/commands/get_email.py | 1 - 1 file changed, 1 deletion(-) diff --git a/helpdesk/management/commands/get_email.py b/helpdesk/management/commands/get_email.py index 1c608cde..0e649dcf 100755 --- a/helpdesk/management/commands/get_email.py +++ b/helpdesk/management/commands/get_email.py @@ -556,7 +556,6 @@ def ticket_from_message(message, queue, logger): sender=queue.from_address, fail_silently=True, ) - return t From 951cc68ecd444630e40e3d3b1ccd53f15659ff68 Mon Sep 17 00:00:00 2001 From: Garret Wassermann Date: Sat, 25 May 2019 01:09:46 -0400 Subject: [PATCH 8/8] Bump to 0.2.16 --- demo/setup.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/setup.py b/demo/setup.py index 06385a1b..6e894301 100644 --- a/demo/setup.py +++ b/demo/setup.py @@ -13,7 +13,7 @@ project_root = os.path.dirname(here) NAME = 'django-helpdesk-demodesk' DESCRIPTION = 'A demo Django project using django-helpdesk' README = open(os.path.join(here, 'README.rst')).read() -VERSION = '0.2.15' +VERSION = '0.2.16' #VERSION = open(os.path.join(project_root, 'VERSION')).read().strip() AUTHOR = 'django-helpdesk team' URL = 'https://github.com/django-helpdesk/django-helpdesk' diff --git a/setup.py b/setup.py index 9f37bc37..4a266ca1 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from distutils.util import convert_path from fnmatch import fnmatchcase from setuptools import setup, find_packages -version = '0.2.15' +version = '0.2.16' # Provided as an attribute, so you can append to these instead # of replicating them: