diff --git a/demo/demodesk/config/settings.py b/demo/demodesk/config/settings.py index 55ab94f2..5851bb47 100644 --- a/demo/demodesk/config/settings.py +++ b/demo/demodesk/config/settings.py @@ -1,14 +1,8 @@ """ Django settings for django-helpdesk demodesk project. -For more information on this file, see -https://docs.djangoproject.com/en/1.11/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.11/ref/settings/ """ - import os @@ -39,9 +33,10 @@ ALLOWED_HOSTS = [] # an internal demo you don't need such security, but please # remember when setting up your own development / production server! +# Default teams mode to enabled unless overridden by an environment variable set to "false" +HELPDESK_TEAMS_MODE_ENABLED=os.getenv("HELPDESK_TEAMS_MODE_ENABLED", "true").lower() == "true" # Application definition - INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', @@ -52,13 +47,16 @@ INSTALLED_APPS = [ 'django.contrib.sites', 'django.contrib.humanize', 'bootstrap4form', - 'account', # Required by pinax-teams - 'pinax.invitations', # required by pinax-teams - 'pinax.teams', # team support - 'reversion', # required by pinax-teams 'helpdesk', # This is us! 'rest_framework', # required for the API ] +if HELPDESK_TEAMS_MODE_ENABLED: + INSTALLED_APPS.extend([ + 'account', # Required by pinax-teams + 'pinax.invitations', # required by pinax-teams + 'pinax.teams', # team support + 'reversion', # required by pinax-teams + ]) MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', diff --git a/docs/install.rst b/docs/install.rst index 9522c7f0..9f3fccf7 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,3 +1,4 @@ +.. _installation: Installation ============ @@ -18,7 +19,7 @@ Before getting started, ensure your system meets the following recommended depen Ensure any extra Django modules you wish to use are compatible before continuing. -**NOTE**: Python 2.7 support was deprecated in ``django-helpdesk`` as of version 0.2.x +**NOTE**: Python 2 support was deprecated in ``django-helpdesk`` as of version 0.2.x and completely removed in version 0.3.0. Users that still need Python 2 support should remain on version 0.2.x. @@ -31,12 +32,12 @@ Installing using PIP Try using ``pip install django-helpdesk``. Go and have a beer to celebrate Python packaging. -Checkout ``stable`` from git (Cutting Edge) +Checkout ``main`` branch from git (Cutting Edge) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you're planning on editing the code or just want to get whatever is the latest and greatest, you can clone the official Git repository with ``git clone git://github.com/django-helpdesk/django-helpdesk.git``. We use the ``stable`` branch as our development branch for the next major release of ``django-helpdesk``. +If you're planning on editing the code or just want to get whatever is the latest and greatest, you can clone the official Git repository with ``git clone git://github.com/django-helpdesk/django-helpdesk.git``. Each official release of ``django-helpdesk`` is tagged. -Copy the ``helpdesk`` folder into your ``PYTHONPATH``. +Copy the ``helpdesk`` folder into your ``PYTHONPATH`` or add it to your ``PYTHONPATH``. I just want a .tar.gz! ~~~~~~~~~~~~~~~~~~~~~~ @@ -52,30 +53,36 @@ If you're on a brand new Django installation, make sure you do a ``migrate`` **before** adding ``helpdesk`` to your ``INSTALLED_APPS``. This will avoid errors with trying to create User settings. -1. Edit your ``settings.py`` file and add ``helpdesk`` to the ``INSTALLED_APPS`` setting. You also need ``django.contrib.admin`` in ``INSTALLED_APPS`` if you haven't already added it. eg:: +1. Edit your ``settings.py`` file add the following entries: + - add ``helpdesk`` to the ``INSTALLED_APPS`` along with some other required entries in the ``django.contrib`` package. - INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', # Required for determining domain url for use in emails - '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.invitations', # Required by pinax-teams - 'pinax.teams', # Team support - 'reversion', # Required by pinax-teams - 'rest_framework', # required for the API - 'django_cleanup.apps.CleanupConfig', # Remove this if you do NOT want to delete files on the file system when the associated record is deleted in the database - 'helpdesk', # This is us! - ) + An example of the core ``INSTALLED_APPS`` requirements for this app are shown below:: - Note: you do not need to use pinax-teams. To disable teams see the :doc:`teams` section. + INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', # Required for determining domain url for use in emails + '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 + 'rest_framework', # required for the API + 'django_cleanup.apps.CleanupConfig', # Remove this if you do NOT want to delete files on the file system when the associated record is deleted in the database + 'helpdesk', # This is us! + ) - Your ``settings.py`` file should also define a ``SITE_ID`` that allows multiple projects to share - a single database, and is required by ``django.contrib.sites`` in Django 1.9+. - If you aren't running multiple sites, you can simply add a default ``SITE_ID`` to ``settings.py``:: + - Enable or disable Teams based ticket assignment by setting the boolean flag named ``HELPDESK_TEAMS_MODE_ENABLED`` to True or False. + + IMPORTANT NOTE: It is ENABLED by default if you do not set this flag to False + See the :doc::ref:`teams`. section for a detailed explanation of how to use teams mode. + Below is an example for disabling teams:: + HELPDESK_TEAMS_MODE_ENABLED=False + + - Your ``settings.py`` file should also define a ``SITE_ID`` + + This will allow multiple projects to share a single database, and is required by ``django.contrib.sites`` in Django 1.9+. + If you aren't running multiple sites, you can simply add a default ``SITE_ID`` to ``settings.py`` + Below is an example for setting a default:: SITE_ID = 1 diff --git a/docs/teams.rst b/docs/teams.rst index 8e093698..6f804a7d 100644 --- a/docs/teams.rst +++ b/docs/teams.rst @@ -1,3 +1,4 @@ +.. _teams: Working with teams and larger organizations =========================================== @@ -20,7 +21,6 @@ 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. - Implementing Custom Teams Functionality -------------------------------- If you want to use a different team app or implement your own team based app, you can hook it into Helpdesk using the following 3 settings: @@ -29,10 +29,23 @@ If you want to use a different team app or implement your own team based app, yo ``HELPDESK_KBITEM_TEAM_GETTER``: the method that will be called that must return a list of users who belong to a given team +Configuring Teams Functionality +----------------------------- +Teams functionality is enabled by default but can be disabled using this entry in your ``settings.py``:: + HELPDESK_TEAMS_MODE_ENABLED=False + +If you do not disable teams functionality then you must add additional apps into the ``INSTALLED_APPS`` in your ``settings.py``. +The following can be pasted into your settings.py BELOW the ``INSTALLED_APPS`` definition:: +INSTALLED_APPS.extend([ + 'account', # Required by pinax-teams + 'pinax.invitations', # required by pinax-teams + 'pinax.teams', # team support + 'reversion', # required by pinax-teams + ]) + +Alternatively just add the 4 apps listed above into the ``INSTALLED_APPS``. + Disabling Teams Functionality ----------------------------- -If you do not wish to use team functionality, you can disable teams by setting the following settings: - - HELPDESK_TEAMS_MODEL='auth.User', - HELPDESK_TEAMS_MIGRATION_DEPENDENCIES=[], - HELPDESK_KBITEM_TEAM_GETTER=lambda _: None, +Teams functionality is enabled by default but can be disabled using this entry in your ``settings.py``:: + HELPDESK_TEAMS_MODE_ENABLED=False diff --git a/helpdesk/settings.py b/helpdesk/settings.py index 7f2fa76c..8212ffa9 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -60,9 +60,6 @@ HELPDESK_ANON_ACCESS_RAISES_404 = getattr(settings, 'HELPDESK_ANON_ACCESS_RAISES_404', False) -# show knowledgebase links? -HELPDESK_KB_ENABLED = getattr(settings, 'HELPDESK_KB_ENABLED', True) - # Disable Timeline on ticket list HELPDESK_TICKETS_TIMELINE_ENABLED = getattr( settings, 'HELPDESK_TICKETS_TIMELINE_ENABLED', True) @@ -231,12 +228,23 @@ HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION = getattr( HELPDESK_USE_HTTPS_IN_EMAIL_LINK = getattr( settings, 'HELPDESK_USE_HTTPS_IN_EMAIL_LINK', settings.SECURE_SSL_REDIRECT) -HELPDESK_TEAMS_MODEL = getattr( - settings, 'HELPDESK_TEAMS_MODEL', 'pinax_teams.Team') -HELPDESK_TEAMS_MIGRATION_DEPENDENCIES = getattr(settings, 'HELPDESK_TEAMS_MIGRATION_DEPENDENCIES', [ +# Default to True for backwards compatibility +HELPDESK_TEAMS_MODE_ENABLED = getattr(settings, 'HELPDESK_TEAMS_MODE_ENABLED', True) +if HELPDESK_TEAMS_MODE_ENABLED: + HELPDESK_TEAMS_MODEL = getattr( + settings, 'HELPDESK_TEAMS_MODEL', 'pinax_teams.Team') + HELPDESK_TEAMS_MIGRATION_DEPENDENCIES = getattr(settings, 'HELPDESK_TEAMS_MIGRATION_DEPENDENCIES', [ ('pinax_teams', '0004_auto_20170511_0856')]) -HELPDESK_KBITEM_TEAM_GETTER = getattr( - settings, 'HELPDESK_KBITEM_TEAM_GETTER', lambda kbitem: kbitem.team) + HELPDESK_KBITEM_TEAM_GETTER = getattr( + settings, 'HELPDESK_KBITEM_TEAM_GETTER', lambda kbitem: kbitem.team) +else: + HELPDESK_TEAMS_MODEL = settings.AUTH_USER_MODEL + HELPDESK_TEAMS_MIGRATION_DEPENDENCIES = [] + HELPDESK_KBITEM_TEAM_GETTER = lambda _: None + +# show knowledgebase links? +# If Teams mode is enabled then it has to be on +HELPDESK_KB_ENABLED = True if HELPDESK_TEAMS_MODE_ENABLED else getattr(settings, 'HELPDESK_KB_ENABLED', True) # Include all signatures and forwards in the first ticket message if set # Useful if you get forwards dropped from them while they are useful part diff --git a/helpdesk/tests/test_get_email.py b/helpdesk/tests/test_get_email.py index 361b438a..9c8adb5f 100644 --- a/helpdesk/tests/test_get_email.py +++ b/helpdesk/tests/test_get_email.py @@ -341,27 +341,48 @@ class GetEmailCommonTests(TestCase): self.assertTrue(inline_found, "Inline file not found in email: %s" % (inline_att_filename)) - def test_email_with_txt_as_attachment(self): + def test_email_with_txt_as_attachment_with_simple_alternative_message(self): """ - Test an email with an txt extension email attachment to the email + Test an email with a txt extension email attachment to the email where the message part of the + email is in a multipart/alternative directly off the main multipart/mixed (no multipart/related) """ - email_message, _, _ = utils.generate_multipart_email(type_list=['plain']) - email_att_filename = 'test.txt' - file_part = utils.generate_file_mime_part("en_US", email_att_filename, "Testing a simple txt attachment.") - email_message.attach(file_part) + email_att_filename = 'the_quick_brown_fox.txt' + # Create the actual email with its plain and HTML parts + alt_email_message = MIMEMultipart("alternative") + # Create the plain and HTML that will reference the inline attachment + plain_body = "Is wet birds attached?\n\n" + plain_msg = MIMEText(plain_body) + alt_email_message.attach(plain_msg) + html_body = '
Is wet birds attached?

' + html_msg = MIMEText(html_body, "html") + alt_email_message.attach(html_msg) + # Now create the base multipart and attach all the other parts to it + base_message = MIMEMultipart("mixed") + base_message.attach(alt_email_message) + email_attachment = MIMEText("Wet birds don't fly at night.") + email_attachment.add_header('Content-Disposition', 'attachment', filename=email_att_filename) + base_message.attach(email_attachment) + utils.add_simple_email_headers(base_message, locale="en_US", use_short_email=True) # Now send the part to the email workflow - extract_email_metadata(email_message.as_string(), self.queue_public, self.logger) - + extract_email_metadata(base_message.as_string(), self.queue_public, self.logger) self.assertEqual(len(mail.outbox), 1) # @UndefinedVariable + #self.assertEqual(f'[test-1] {base_message.get("subject")} (Opened)', mail.outbox[0].subject) ticket = Ticket.objects.get() followup = ticket.followup_set.get() - attachments = FollowUpAttachment.objects.filter(followup=followup) - self.assertEqual(len(attachments), 1) - attachment = attachments[0] - self.assertTrue(attachment.filename.endswith(email_att_filename), "The txt file not found in email: %s" % (email_att_filename)) + # Check attachment is stored as attached file + email_attachment_found = False + for att_retrieved in followup.followupattachment_set.all(): + if (helpdesk.email.HTML_EMAIL_ATTACHMENT_FILENAME == att_retrieved.filename): + # Ignore the HTML formatted content of the email that is attached + continue + if att_retrieved.filename.endswith(email_att_filename): + email_attachment_found = True + else: + self.assertTrue(False, "Unexpected file in ticket attachments: %s" % att_retrieved.filename) + self.assertTrue(email_attachment_found, "Email attachment file not found ticket attachments: %s" % (email_att_filename)) + - class EmailTaskTests(TestCase): def setUp(self): diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index b13ad6a3..ff17c258 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -151,17 +151,20 @@ def dashboard(request): # closed & resolved tickets, assigned to current user tickets_closed_resolved = Ticket.objects.select_related('queue').filter( assigned_to=request.user, - status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS]) + status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS] + ) 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() + kbitems = None + # Teams mode uses assignment via knowledge base items so exclude tickets assigned to KB items + if helpdesk_settings.HELPDESK_TEAMS_MODE_ENABLED: + unassigned_tickets = unassigned_tickets.filter(kbitem__isnull=True) + kbitems = huser.get_assigned_kb_items() # all tickets, reported by current user all_tickets_reported_by_current_user = ''