def ticket_from_message(message, queue, quiet): # 'message' must be an RFC822 formatted message. msg = message message = email.message_from_string(msg) subject = message.get('subject', _('Created from e-mail')) subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) subject = subject.replace("Re: ", "").replace("Fw: ", "").replace("RE: ", "").replace("FW: ", "").replace("Automatic reply: ", "").strip() sender = message.get('from', _('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' for ignore in IgnoreEmail.objects.filter(Q(queues=queue) | Q(queues__isnull=True)): if ignore.test(sender_email): if ignore.keep_in_mailbox: # By returning 'False' the message will be kept in the mailbox, # and the 'True' will cause the message to be deleted. return False return True matchobj = re.match(r".*\["+queue.slug+"-(?P<id>\d+)\]", subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') else: ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name == None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply(decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) else: body_html = part.get_payload(decode=True) else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({ 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type()}, ) counter += 1 if body_plain: body = body_plain else: body = _('No plain-text email body available. Please see attachment email_html_body.html.') if body_html: files.append({ 'filename': _("email_html_body.html"), 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False except Ticket.DoesNotExist: ticket = None priority = 3 smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = ('high', 'important', '1', 'urgent') if smtp_priority in high_priority_types or smtp_importance in high_priority_types: priority = 2 if ticket == None: t = Ticket( title=subject, queue=queue, submitter_email=sender_email, created=now, description=body, priority=priority, ) t.save() new = True update = '' elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() f = FollowUp( ticket = t, title = _('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date = timezone.now(), public = True, comment = body, ) if t.status == Ticket.REOPENED_STATUS: f.new_status = Ticket.REOPENED_STATUS f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}) f.save() if not quiet: print (" [%s-%s] %s" % (t.queue.slug, t.id, t.title,)).encode('ascii', 'replace') for file in files: if file['content']: filename = file['filename'].encode('ascii', 'replace').replace(' ', '_') filename = re.sub('[^a-zA-Z0-9._-]+', '', filename) a = Attachment( followup=f, filename=filename, mime_type=file['type'], size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() if not quiet: print " - %s" % filename context = safe_template_context(t) if new: if sender_email: send_templated_mail( 'newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True, ) if queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) else: context.update(comment=f.comment) if t.status == Ticket.REOPENED_STATUS: update = _(' (Reopened)') else: update = _(' (Updated)') if t.assigned_to: send_templated_mail( 'updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc: send_templated_mail( 'updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) return t
def update_ticket(request, ticket_id, public=False): if not (public or (request.user.is_authenticated() and request.user.is_active and (request.user.is_staff or helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE))): return HttpResponseRedirect('%s?next=%s' % (reverse('login'), request.path)) ticket = get_object_or_404(Ticket, id=ticket_id) comment = request.POST.get('comment', '') new_status = int(request.POST.get('new_status', ticket.status)) title = request.POST.get('title', '') public = request.POST.get('public', False) owner = int(request.POST.get('owner', -1)) priority = int(request.POST.get('priority', ticket.priority)) due_date_year = int(request.POST.get('due_date_year', 0)) due_date_month = int(request.POST.get('due_date_month', 0)) due_date_day = int(request.POST.get('due_date_day', 0)) if not (due_date_year and due_date_month and due_date_day): due_date = ticket.due_date else: if ticket.due_date: due_date = ticket.due_date else: due_date = timezone.now() due_date = due_date.replace(due_date_year, due_date_month, due_date_day) no_changes = all([ not request.FILES, not comment, new_status == ticket.status, title == ticket.title, priority == int(ticket.priority), due_date == ticket.due_date, (owner == -1) or (not owner and not ticket.assigned_to) or (owner and User.objects.get(id=owner) == ticket.assigned_to), ]) if no_changes: return return_to_ticket(request.user, helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE, ticket) # We need to allow the 'ticket' and 'queue' contexts to be applied to the # comment. from django.template import engines, Context context = safe_template_context(ticket) # this line sometimes creates problems if code is sent as a comment. # if comment contains some django code, like "why does {% if bla %} crash", # then the following line will give us a crash, since django expects {% if %} # to be closed with an {% endif %} tag. comment = engines['django'].from_string(comment).render(Context(context)) if owner is -1 and ticket.assigned_to: owner = ticket.assigned_to.id f = FollowUp(ticket=ticket, date=timezone.now(), comment=comment) if request.user.is_staff or helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: f.user = request.user f.public = public reassigned = False if owner is not -1: if owner != 0 and ((ticket.assigned_to and owner != ticket.assigned_to.id) or not ticket.assigned_to): new_user = User.objects.get(id=owner) f.title = _('Assigned to %(username)s') % { 'username': new_user.get_username(), } ticket.assigned_to = new_user reassigned = True # user changed owner to 'unassign' elif owner == 0 and ticket.assigned_to is not None: f.title = _('Unassigned') ticket.assigned_to = None if new_status != ticket.status: ticket.status = new_status ticket.save() f.new_status = new_status if f.title: f.title += ' and %s' % ticket.get_status_display() else: f.title = '%s' % ticket.get_status_display() if not f.title: if f.comment: f.title = _('Comment') else: f.title = _('Updated') f.save() files = [] if request.FILES: import mimetypes, os for file in request.FILES.getlist('attachment'): filename = file.name.encode('ascii', 'ignore') a = Attachment( followup=f, filename=filename, mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream', size=file.size, ) a.file.save(filename, file, save=False) a.save() if file.size < getattr(settings, 'MAX_EMAIL_ATTACHMENT_SIZE', 512000): # Only files smaller than 512kb (or as defined in # settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email. files.append([a.filename, a.file]) if title != ticket.title: c = TicketChange( followup=f, field=_('Title'), old_value=ticket.title, new_value=title, ) c.save() ticket.title = title if priority != ticket.priority: c = TicketChange( followup=f, field=_('Priority'), old_value=ticket.priority, new_value=priority, ) c.save() ticket.priority = priority if due_date != ticket.due_date: c = TicketChange( followup=f, field=_('Due on'), old_value=ticket.due_date, new_value=due_date, ) c.save() ticket.due_date = due_date if new_status in [ Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS ]: if new_status == Ticket.RESOLVED_STATUS or ticket.resolution is None: ticket.resolution = comment messages_sent_to = [] # ticket might have changed above, so we re-instantiate context with the # (possibly) updated ticket. context = safe_template_context(ticket) context.update( resolution=ticket.resolution, comment=f.comment, ) if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))): if f.new_status == Ticket.RESOLVED_STATUS: template = 'resolved_' elif f.new_status == Ticket.CLOSED_STATUS: template = 'closed_' else: template = 'updated_' template_suffix = 'submitter' if ticket.submitter_email: send_templated_mail( template + template_suffix, context, recipients=ticket.submitter_email, sender=ticket.queue.from_address, fail_silently=True, files=files, ) messages_sent_to.append(ticket.submitter_email) template_suffix = 'cc' for cc in ticket.ticketcc_set.all(): if cc.email_address not in messages_sent_to: send_templated_mail( template + template_suffix, context, recipients=cc.email_address, sender=ticket.queue.from_address, fail_silently=True, ) messages_sent_to.append(cc.email_address) if ticket.assigned_to and request.user != ticket.assigned_to and ticket.assigned_to.email and ticket.assigned_to.email not in messages_sent_to: # We only send e-mails to staff members if the ticket is updated by # another user. The actual template varies, depending on what has been # changed. if reassigned: template_staff = 'assigned_owner' elif f.new_status == Ticket.RESOLVED_STATUS: template_staff = 'resolved_owner' elif f.new_status == Ticket.CLOSED_STATUS: template_staff = 'closed_owner' else: template_staff = 'updated_owner' # if (not reassigned or ( reassigned and ticket.assigned_to.usersettings.settings.get('email_on_ticket_assign', False))) or (not reassigned and ticket.assigned_to.usersettings.settings.get('email_on_ticket_change', False)): # send_templated_mail( # template_staff, # context, # recipients=ticket.assigned_to.email, # sender=ticket.queue.from_address, # fail_silently=True, # files=files, # ) # messages_sent_to.append(ticket.assigned_to.email) if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to: if reassigned: template_cc = 'assigned_cc' elif f.new_status == Ticket.RESOLVED_STATUS: template_cc = 'resolved_cc' elif f.new_status == Ticket.CLOSED_STATUS: template_cc = 'closed_cc' else: template_cc = 'updated_cc' send_templated_mail( template_cc, context, recipients=ticket.queue.updated_ticket_cc, sender=ticket.queue.from_address, fail_silently=True, files=files, ) ticket.save() # auto subscribe user if enabled if helpdesk_settings.HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE and request.user.is_authenticated(): ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket) if SHOW_SUBSCRIBE: subscribe_staff_member_to_ticket(ticket, request.user) return return_to_ticket(request.user, helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE, ticket)