diff --git a/docs/settings.rst b/docs/settings.rst index 5a9b5088..405e8f8d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -223,3 +223,7 @@ The following settings were defined in previous versions and are no longer suppo - **HELPDESK_FOOTER_SHOW_CHANGE_LANGUAGE_LINK** Is never shown. Use your own template if required. - **HELPDESK_ENABLE_PER_QUEUE_MEMBERSHIP** Discontinued in favor of HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION. + +- **HELPDESK_FULL_FIRST_MESSAGE_FROM_EMAIL** Do not ignore fowarded and replied text from the email messages which create a new ticket; useful for cases when customer forwards some email (error from service or something) and wants support to see that + +- **HELPDESK_ALWAYS_SAVE_INCOMING_EMAIL_MESSAGE** Any incoming .eml message is saved and available, helps when customer spent some time doing fancy markup which has been corrupted during the email-to-ticket-comment translate process diff --git a/helpdesk/.flake8 b/helpdesk/.flake8 new file mode 100644 index 00000000..f119cca1 --- /dev/null +++ b/helpdesk/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 120 +import-order-style = pep8 diff --git a/helpdesk/email.py b/helpdesk/email.py index becc90d0..b1647e31 100644 --- a/helpdesk/email.py +++ b/helpdesk/email.py @@ -21,6 +21,7 @@ from os.path import isfile, join from time import ctime from bs4 import BeautifulSoup +from django.conf import settings as django_settings from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile @@ -394,7 +395,7 @@ def create_object_from_email_message(message, ticket_id, payload, files, logger) title=_('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date=now, public=True, - comment=payload['body'], + comment=payload.get('full_body', payload['body']) or "", message_id=message_id ) @@ -505,6 +506,7 @@ def object_from_message(message, queue, logger): ticket = None body = None + full_body = None counter = 0 files = [] @@ -523,7 +525,19 @@ def object_from_message(message, queue, logger): if part['Content-Transfer-Encoding'] == '8bit' and part.get_content_charset() == 'utf-8': body = body.decode('unicode_escape') body = decodeUnknown(part.get_content_charset(), body) - body = EmailReplyParser.parse_reply(body) + # have to use django_settings here so overwritting it works in tests + # the default value is False anyway + if ticket is None and getattr(django_settings, 'HELPDESK_FULL_FIRST_MESSAGE_FROM_EMAIL', False): + # first message in thread, we save full body to avoid losing forwards and things like that + body_parts = [] + for f in EmailReplyParser.read(body).fragments: + body_parts.append(f.content) + full_body = '\n\n'.join(body_parts) + body = EmailReplyParser.parse_reply(body) + else: + # second and other reply, save only first part of the message + body = EmailReplyParser.parse_reply(body) + full_body = body # workaround to get unicode text out rather than escaped text try: body = body.encode('ascii').decode('unicode_escape') @@ -536,13 +550,23 @@ def object_from_message(message, queue, logger): except UnicodeDecodeError: email_body = encoding.smart_text(part.get_payload(decode=False)) - payload = """ - - - - -%s -""" % email_body + if not body and not full_body: + # no text has been parsed so far - try such deep parsing for some messages + altered_body = email_body.replace("

", "

\n").replace("{email_body}" + + payload = ( + '' + '' + '' + '' + '%s' + '' + ) % email_body files.append( SimpleUploadedFile(_("email_html_body.html"), payload.encode("utf-8"), 'text/html') ) @@ -554,22 +578,22 @@ def object_from_message(message, queue, logger): else: name = ("part-%i_" % counter) + name - # FIXME: this code gets the paylods, then does something with it and then completely ignores it - # writing the part.get_payload(decode=True) instead; and then the payload variable is - # replaced by some dict later. - # the `payloadToWrite` has been also ignored so was commented - payload = part.get_payload() - if isinstance(payload, list): - payload = payload.pop().as_string() - # payloadToWrite = payload - # check version of python to ensure use of only the correct error type - non_b64_err = TypeError - try: - logger.debug("Try to base64 decode the attachment payload") - # payloadToWrite = base64.decodebytes(payload) - except non_b64_err: - logger.debug("Payload was not base64 encoded, using raw bytes") - # payloadToWrite = payload + # # FIXME: this code gets the paylods, then does something with it and then completely ignores it + # # writing the part.get_payload(decode=True) instead; and then the payload variable is + # # replaced by some dict later. + # # the `payloadToWrite` has been also ignored so was commented + # payload = part.get_payload() + # if isinstance(payload, list): + # payload = payload.pop().as_string() + # # payloadToWrite = payload + # # check version of python to ensure use of only the correct error type + # non_b64_err = TypeError + # try: + # logger.debug("Try to base64 decode the attachment payload") + # # payloadToWrite = base64.decodebytes(payload) + # except non_b64_err: + # logger.debug("Payload was not base64 encoded, using raw bytes") + # # payloadToWrite = payload files.append(SimpleUploadedFile(name, part.get_payload(decode=True), mimetypes.guess_type(name)[0])) logger.debug("Found MIME attachment %s" % name) @@ -581,11 +605,25 @@ def object_from_message(message, queue, logger): if beautiful_body: try: body = beautiful_body.text + full_body = body except AttributeError: pass if not body: body = "" + if getattr(django_settings, 'HELPDESK_ALWAYS_SAVE_INCOMING_EMAIL_MESSAGE', False): + # save message as attachment in case of some complex markup renders wrong + files.append( + SimpleUploadedFile( + _("original_message.eml").replace( + ".eml", + timezone.localtime().strftime("_%d-%m-%Y_%H:%M") + ".eml" + ), + str(message).encode("utf-8"), + 'text/plain' + ) + ) + smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = {'high', 'important', '1', 'urgent'} @@ -593,6 +631,7 @@ def object_from_message(message, queue, logger): payload = { 'body': body, + 'full_body': full_body or body, 'subject': subject, 'queue': queue, 'sender_email': sender_email, diff --git a/helpdesk/settings.py b/helpdesk/settings.py index 705b38bf..6b4c6967 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -162,3 +162,12 @@ HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION = getattr( # use https in the email links HELPDESK_USE_HTTPS_IN_EMAIL_LINK = getattr(settings, 'HELPDESK_USE_HTTPS_IN_EMAIL_LINK', False) + +# 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 of request +HELPDESK_FULL_FIRST_MESSAGE_FROM_EMAIL = getattr(settings, 'HELPDESK_FULL_FIRST_MESSAGE_FROM_EMAIL', False) + +# If set then we always save incoming emails as .eml attachments +# which is quite noisy but very helpful for complicated markup, forwards and so on +# (which gets stripped/corrupted otherwise) +HELPDESK_ALWAYS_SAVE_INCOMING_EMAIL_MESSAGE = getattr(settings, "HELPDESK_ALWAYS_SAVE_INCOMING_EMAIL_MESSAGE", False) diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index 20f55bc7..caf4bb3d 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -50,7 +50,7 @@

{% if followup.comment %} -

{{ followup.get_markdown|urlizetrunc:50|num_to_link|linebreaksbr }}

+

{{ followup.get_markdown|urlizetrunc:50|num_to_link }}

{% endif %} {% for change in followup.ticketchange_set.all %} {% if forloop.first %}