def action(request, mode, ticket): mod_path, cls_name = settings.TICKET_CLASS.rsplit('.', 1) mod_path = mod_path.split('.').pop(0) tic = apps.get_model(mod_path, cls_name).objects.get(pk=ticket) if hasattr(settings, 'KEEP_IT_SIMPLE') and settings.KEEP_IT_SIMPLE: keep_it_simple = True else: keep_it_simple = False if mode == 'view': if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): com = tickets_comments() com.comment = form.cleaned_data['comment'] com.ticket_id = ticket com.action = 6 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 6, com.comment) mail_comment(request, com.pk) jabber_comment(request, com.pk) else: if 'resolution' in request.POST: if request.POST['resolution'] and int( request.POST['resolution']) > 0: tic.resolution_id = request.POST['resolution'] tic.closed = True tic.close_date = timezone.now() tic.state = get_flow_end() tic.save(user=request.user) com = tickets_comments() com.comment = _( 'ticket closed - resolution: %(resolution)s\n\n%(comment)s' ) % { 'resolution': ticket_resolution.objects.get( pk=request.POST['resolution']).name, 'comment': request.POST.get('close_comment', '') } com.ticket_id = ticket com.action = 1 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 1, request.POST.get('close_comment', '')) mail_comment(request, com.pk) jabber_comment(request, com.pk) else: messages.add_message(request, messages.ERROR, _('no resolution selected')) else: messages.add_message(request, messages.ERROR, _('comment invalid')) # reload ticket => last_action date has changed tic = apps.get_model(mod_path, cls_name).objects.get(pk=ticket) excludes = [] form = TicketsForm(exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id, view_only=True) close = TicketCloseForm() reassign = TicketReassignForm(initial={ 'assigned': tic.assigned_id, 'state': tic.state }) flows = list( ticket_flow_edges.objects.select_related('next').filter( now=tic.state).exclude(next__type=2).values_list('next', flat=True)) flows.append(tic.state_id) reassign.fields['state'].queryset = reassign.fields[ 'state'].queryset.filter(id__in=flows) participants = tickets_participants.objects.select_related( 'user').filter(ticket=ticket) comments = tickets_comments.objects.select_related('c_user').filter( ticket=ticket).order_by('c_date') close_allowed = ticket_flow_edges.objects.select_related( 'next').filter(now=tic.state, next__type=2).count() > 0 files = tickets_files.objects.filter(ticket=ticket, active_record=True) paginator = Paginator(files, 10) page = request.GET.get('page') try: files_lines = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. files_lines = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. files_lines = paginator.page(paginator.num_pages) add_breadcrumbs(request, ticket, '#', caption=tic.caption[:20]) if 'YATSE' in request.GET and 'isUsingYATSE' not in request.session: request.session['isUsingYATSE'] = True return render( request, 'tickets/view.html', { 'layout': 'horizontal', 'ticket': tic, 'form': form, 'close': close, 'reassign': reassign, 'files': files_lines, 'comments': comments, 'participants': participants, 'close_allowed': close_allowed, 'keep_it_simple': keep_it_simple, 'last_action_date': http_date(time.mktime(tic.last_action_date.timetuple())) }) elif mode == 'gallery': images = tickets_files.objects.filter(ticket=ticket, active_record=True) return render(request, 'tickets/gallery.html', { 'layout': 'horizontal', 'ticket': tic, 'images': images }) elif mode == 'history': history = tickets_history.objects.filter(ticket=ticket) return render( request, 'tickets/history.html', { 'layout': 'horizontal', 'ticket': tic, 'history': history, 'keep_it_simple': keep_it_simple }) elif mode == 'reopen': if tic.closed: tic.closed = False tic.state = get_flow_start() tic.resolution = None tic.close_date = None tic.save(user=request.user) com = tickets_comments() com.comment = _('ticket reopend - resolution deleted') com.ticket_id = ticket com.action = 2 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 2, None) mail_comment(request, com.pk) jabber_comment(request, com.pk) return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif mode == 'move': if not tic.closed: old_state = tic.state tic.state = ticket_flow.objects.get(pk=request.POST['state']) tic.save(user=request.user) touch_ticket(request.user, ticket) oldUser = str(User.objects.get( pk=tic.assigned_id)) if tic.assigned_id else None history_data = { 'old': { 'comment': '', 'assigned': oldUser, 'state': str(old_state) }, 'new': { 'comment': _('ticket moved'), 'assigned': oldUser, 'state': str(tic.state) } } add_history(request, tic, 7, history_data) return HttpResponse('OK') elif mode == 'reassign': if not tic.closed: if 'assigned' in request.POST: if request.POST['assigned'] and int( request.POST['assigned']) > 0: old_assigned_user = tic.assigned old_state = tic.state tic.assigned_id = request.POST['assigned'] tic.state = ticket_flow.objects.get( pk=request.POST['state']) tic.save(user=request.user) newUser = User.objects.get(pk=request.POST['assigned']) com = tickets_comments() com.comment = _( 'ticket reassigned to %(user)s\nstate now: %(state)s\n\n%(comment)s' ) % { 'user': newUser, 'comment': request.POST.get('reassign_comment', ''), 'state': tic.state } com.ticket_id = ticket com.action = 7 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) if request.POST['assigned']: touch_ticket(newUser, ticket) mail_comment(request, com.pk) jabber_comment(request, com.pk) history_data = { 'old': { 'comment': '', 'assigned': str(old_assigned_user), 'state': str(old_state) }, 'new': { 'comment': request.POST.get('reassign_comment', ''), 'assigned': str(User.objects.get(pk=request.POST['assigned'])), 'state': str(tic.state) } } add_history(request, tic, 7, history_data) else: messages.add_message(request, messages.ERROR, _('missing assigned user')) return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif mode == 'edit' or (mode == 'simple' and (not tic.keep_it_simple or tic.closed) and keep_it_simple): excludes = ['resolution'] if request.method == 'POST': form = TicketsForm(request.POST, exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id) if form.is_valid(): tic = form.save() if tic.keep_it_simple: tic.keep_it_simple = False tic.save(user=request.user) assigned = form.cleaned_data.get('assigned') if assigned: touch_ticket(assigned, tic.pk) mail_ticket(request, tic.pk, form) jabber_ticket(request, tic.pk, form) remember_changes(request, form, tic) touch_ticket(request.user, tic.pk) return HttpResponseRedirect('/tickets/view/%s/' % ticket) else: form = TicketsForm(exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id) if 'state' in form.fields: form.fields['state'].queryset = form.fields[ 'state'].queryset.exclude(type=2) return render(request, 'tickets/edit.html', { 'ticket': tic, 'layout': 'horizontal', 'form': form }) elif mode == 'simple': if request.method == 'POST': form = SimpleTickets(request.POST, initial={ 'caption': tic.caption, 'description': tic.description, 'priority': tic.priority, 'assigned': tic.assigned }) if form.is_valid(): cd = form.cleaned_data tic.caption = cd['caption'] tic.description = cd['description'].replace(u"\u00A0", " ") tic.priority = cd['priority'] tic.assigned = cd['assigned'] tic.deadline = cd['deadline'] tic.show_start = cd['show_start'] tic.component = cd['component'] tic.save(user=request.user) if cd['assigned']: touch_ticket(cd['assigned'], tic.pk) remember_changes(request, form, tic) touch_ticket(request.user, tic.pk) mail_ticket(request, tic.pk, form) jabber_ticket(request, tic.pk, form) return HttpResponseRedirect('/tickets/view/%s/' % ticket) else: form = SimpleTickets( initial={ 'caption': tic.caption, 'description': tic.description, 'priority': tic.priority, 'assigned': tic.assigned, 'deadline': tic.deadline, 'show_start': tic.show_start, 'component': tic.component, }) return render(request, 'tickets/edit.html', { 'ticket': tic, 'layout': 'horizontal', 'form': form, 'mode': mode }) elif mode == 'download': fileid = request.GET.get('file', -1) file_data = tickets_files.objects.get(id=fileid, ticket=ticket) src = '%s%s.dat' % (settings.FILE_UPLOAD_PATH, fileid) content_type = file_data.content_type if request.GET.get('preview') == 'yes' and os.path.isfile( '%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid)): src = '%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid) content_type = 'imgae/png' if request.GET.get( 'resize', 'no') == 'yes' and ('image' in file_data.content_type or 'pdf' in file_data.content_type): img = resize_image('%s' % (src), (200, 150), 75) output = io.BytesIO() img.save(output, 'PNG') output.seek(0) response = StreamingHttpResponse(output, content_type='image/png') else: response = StreamingHttpResponse(open('%s' % (src), "rb"), content_type=content_type) if 'noDisposition' not in request.GET: if request.GET.get('preview') == 'yes' and os.path.isfile( '%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid)): response[ 'Content-Disposition'] = 'attachment;filename="%s"' % content_type else: response[ 'Content-Disposition'] = 'attachment;filename="%s"' % smart_str( file_data.name) return response elif mode == 'upload': if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): if tickets_files.objects.filter( active_record=True, ticket=ticket, checksum=request.FILES['file'].hash).count() > 0: messages.add_message( request, messages.ERROR, _('File already exists: %s') % request.FILES['file'].name) if request.GET.get('Ajax') == '1': return HttpResponse('OK') return HttpResponseRedirect('/tickets/view/%s/' % ticket) f = tickets_files() f.name = request.FILES['file'].name f.size = request.FILES['file'].size f.checksum = request.FILES['file'].hash f.content_type = request.FILES['file'].content_type f.ticket_id = ticket f.public = True f.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 5, request.FILES['file'].name) mail_file(request, f.pk) jabber_file(request, f.pk) dest = settings.FILE_UPLOAD_PATH if not os.path.exists(dest): os.makedirs(dest) with open('%s%s.dat' % (dest, f.id), 'wb+') as destination: for chunk in request.FILES['file'].chunks(): destination.write(chunk) if 'pdf' in f.content_type: convertPDFtoImg('%s/%s.dat' % (dest, f.id), '%s/%s.preview' % (dest, f.id)) else: if 'image' not in f.content_type and isPreviewable( f.content_type): tmp = convertOfficeTpPDF('%s/%s.dat' % (dest, f.id)) convertPDFtoImg(tmp, '%s/%s.preview' % (dest, f.id)) if os.path.isfile(tmp): os.unlink(tmp) return HttpResponseRedirect('/tickets/view/%s/' % tic.pk) else: msg = unicode(form.errors['file']) msg = re.sub('<[^<]+?>', '', msg) messages.add_message(request, messages.ERROR, msg) if request.GET.get('Ajax') == '1': return HttpResponse('OK') return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif request.method == 'PUT': # /tickets/upload/XXX/?filename=test1.txt upload_handlers = request.upload_handlers content_type = str(request.META.get('CONTENT_TYPE', "")) content_length = int(request.META.get('CONTENT_LENGTH', 0)) if content_type == "": return HttpResponse('missing ContentType', status=400) if content_length == 0: # both returned 0 return HttpResponse('missing ContentLength', status=400) content_type = content_type.split(";")[0].strip() try: charset = content_type.split(";")[1].strip() except IndexError: charset = "" # we can get the file name via the path, we don't actually file_name = request.GET['filename'] field_name = file_name counters = [0] * len(upload_handlers) for handler in upload_handlers: result = handler.handle_raw_input("", request.META, content_length, "", "") from django.core.files.uploadhandler import StopFutureHandlers for handler in upload_handlers: try: handler.new_file(field_name, file_name, content_type, content_length, charset) except StopFutureHandlers: break for i, handler in enumerate(upload_handlers): while True: chunk = request.read(handler.chunk_size) if chunk: handler.receive_data_chunk(chunk, counters[i]) counters[i] += len(chunk) else: # no chunk break for i, handler in enumerate(upload_handlers): file_obj = handler.file_complete(counters[i]) if file_obj: if settings.FILE_UPLOAD_VIRUS_SCAN and pyclamd: # virus scan try: if not hasattr(pyclamd, 'scan_stream'): cd = pyclamd.ClamdUnixSocket() else: pyclamd.init_network_socket('localhost', 3310) cd = pyclamd # We need to get a file object for clamav. We might have a path or we might # have to read the data into memory. if hasattr(file_obj, 'temporary_file_path'): os.chmod(file_obj.temporary_file_path(), 0664) result = cd.scan_file( file_obj.temporary_file_path()) else: if hasattr(file_obj, 'read'): result = cd.scan_stream(file_obj.read()) else: result = cd.scan_stream( file_obj['content']) except: from socket import gethostname raise Exception( _(u'unable to initialize scan engine on host %s' ) % gethostname()) if result: msg = ' '.join(result[result.keys()[0]]).replace( 'FOUND ', '') raise Exception( _(u"file is infected by virus: %s") % msg) hasher = hashlib.md5() # We need to get a file object for clamav. We might have a path or we might # have to read the data into memory. if hasattr(file_obj, 'temporary_file_path'): with open(file_obj.temporary_file_path(), 'rb') as afile: buf = afile.read() hasher.update(buf) hash = hasher.hexdigest() else: if hasattr(file_obj, 'read'): file_obj.seek(0) buf = file_obj.read() hasher.update(buf) else: hasher.update(file_obj['content'].read()) hash = hasher.hexdigest() if tickets_files.objects.filter(active_record=True, ticket=ticket, checksum=hash).count() > 0: raise Exception( 'duplicate hash value - file already exists in this ticket %s' % ticket) # todo: virusscan f = tickets_files() f.name = file_obj.name f.size = file_obj.size f.checksum = hash f.content_type = content_type f.ticket_id = ticket f.public = True f.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 5, file_obj.name) mail_file(request, f.pk) jabber_file(request, f.pk) dest = settings.FILE_UPLOAD_PATH if not os.path.exists(dest): os.makedirs(dest) with open('%s%s.dat' % (dest, f.id), 'wb+') as destination: for chunk in file_obj.chunks(): destination.write(chunk) if 'pdf' in f.content_type: convertPDFtoImg('%s/%s.dat' % (dest, f.id), '%s/%s.preview' % (dest, f.id)) else: if 'image' not in f.content_type and isPreviewable( f.content_type): try: tmp = convertOfficeTpPDF('%s/%s.dat' % (dest, f.id)) convertPDFtoImg(tmp, '%s/%s.preview' % (dest, f.id)) if os.path.isfile(tmp): os.unlink(tmp) except: pass if 'audio' in f.content_type: try: # https://realpython.com/python-speech-recognition/ import speech_recognition as sr AUDIO_FILE = '%s%s.dat' % (dest, f.id) r = sr.Recognizer() with sr.AudioFile(AUDIO_FILE) as source: audio = r.record( source) # read the entire audio file text = r.recognize_google(audio, language='de-DE') if text: com = tickets_comments() com.comment = text com.ticket_id = ticket com.action = 6 com.save(user=request.user) except: pass return HttpResponse(status=201) else: # some indication this didn't work? return HttpResponse(status=500) else: form = UploadFileForm() return render(request, 'tickets/file.html', { 'ticketid': ticket, 'layout': 'horizontal', 'form': form }) elif mode == 'delfile': file = tickets_files.objects.get(pk=request.GET['fileid'], ticket=ticket) file.delete(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 8, file.name) return HttpResponseRedirect('/tickets/view/%s/#files' % tic.pk) elif mode == 'notify': tickets_participants.objects.filter( ticket=tic, user=request.user).update(seen=True) return HttpResponse('OK') elif mode == 'sleep': interval = request.GET.get('interval') if interval in ['1', '2', '3', '4', '5', '6', '7', '14', '21', '30']: old = tic.show_start tic.show_start = timezone.now() + datetime.timedelta( days=int(interval)) tic.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 10, (tic.show_start, old)) return HttpResponse('OK') else: raise Exception('no interval given') elif mode == 'ignore': ig = tickets_ignorants() ig.ticket = tic ig.user = request.user ig.save() return HttpResponse('OK') elif mode == 'todo': class local: counter = 0 def ToDoDone(match): local.counter += 1 group = match.groups() if local.counter == pos: return u'[X]' else: return u'[%s]' % group[0] def ToDoUnDone(match): local.counter += 1 group = match.groups() if local.counter == pos: return u'[ ]' else: return u'[%s]' % group[0] form = ToDo(request.POST) if form.is_valid(): desc = tic.description cd = form.cleaned_data text = cd['text'] pos = cd['item'] set = cd['set'] if set: tic.description = re.sub(r'\[([ Xx])\]', ToDoDone, desc) old = _('undone: %s') % text new = _('done: %s') % text else: tic.description = re.sub(r'\[([ Xx])\]', ToDoUnDone, desc) new = _('undone: %s') % text old = _('done: %s') % text tic.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 9, (new, old)) data = { 'set': set, 'item': pos, 'text': text, } return JsonResponse(data, safe=False) elif mode == 'update_comment': if request.method == 'POST': comment_id = request.GET.get("comment_id") comment = tickets_comments.objects.get(pk=comment_id) if comment.c_user == request.user: comment.comment = request.POST.get("comment_body", "") comment.edited = True comment.save(user=request.user) return HttpResponseRedirect("/tickets/view/%s/#comment_id-%s" % (ticket, comment_id)) elif mode == 'last_modified': if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE') if if_modified_since: if_modified_since = parse_http_date_safe(if_modified_since) if time.mktime( tic.last_action_date.timetuple()) > if_modified_since: return HttpResponse('outdated', status=200) else: return HttpResponse('not modified', status=304) else: return HttpResponse('unknown', status=412)
def action(request, mode, ticket): mod_path, cls_name = settings.TICKET_CLASS.rsplit('.', 1) mod_path = mod_path.split('.').pop(0) tic = apps.get_model(mod_path, cls_name).objects.get(pk=ticket) if hasattr(settings, 'KEEP_IT_SIMPLE') and settings.KEEP_IT_SIMPLE: keep_it_simple = True else: keep_it_simple = False if mode == 'view': if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): com = tickets_comments() com.comment = form.cleaned_data['comment'] com.ticket_id = ticket com.action = 6 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 6, com.comment) mail_comment(request, com.pk) jabber_comment(request, com.pk) else: if 'resolution' in request.POST: if request.POST['resolution'] and int(request.POST['resolution']) > 0: tic.resolution_id = request.POST['resolution'] tic.closed = True tic.close_date = timezone.now() tic.state = get_flow_end() tic.save(user=request.user) com = tickets_comments() com.comment = _('ticket closed - resolution: %(resolution)s\n\n%(comment)s') % {'resolution': ticket_resolution.objects.get(pk=request.POST['resolution']).name, 'comment': request.POST.get('close_comment', '')} com.ticket_id = ticket com.action = 1 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 1, request.POST.get('close_comment', '')) mail_comment(request, com.pk) jabber_comment(request, com.pk) else: messages.add_message(request, messages.ERROR, _('no resolution selected')) else: messages.add_message(request, messages.ERROR, _('comment invalid')) excludes = [] form = TicketsForm(exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id, view_only=True) close = TicketCloseForm() reassign = TicketReassignForm(initial={'assigned': tic.assigned_id, 'state': tic.state}) flows = list(ticket_flow_edges.objects.select_related('next').filter(now=tic.state).exclude(next__type=2).values_list('next', flat=True)) flows.append(tic.state_id) reassign.fields['state'].queryset = reassign.fields['state'].queryset.filter(id__in=flows) participants = tickets_participants.objects.select_related('user').filter(ticket=ticket) comments = tickets_comments.objects.select_related('c_user').filter(ticket=ticket).order_by('c_date') close_allowed = ticket_flow_edges.objects.select_related('next').filter(now=tic.state, next__type=2).count() > 0 files = tickets_files.objects.filter(ticket=ticket, active_record=True) paginator = Paginator(files, 10) page = request.GET.get('page') try: files_lines = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. files_lines = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. files_lines = paginator.page(paginator.num_pages) add_breadcrumbs(request, ticket, '#', caption=tic.caption[:20]) if 'YATSE' in request.GET and 'isUsingYATSE' not in request.session: request.session['isUsingYATSE'] = True return render(request, 'tickets/view.html', {'layout': 'horizontal', 'ticket': tic, 'form': form, 'close': close, 'reassign': reassign, 'files': files_lines, 'comments': comments, 'participants': participants, 'close_allowed': close_allowed, 'keep_it_simple': keep_it_simple, 'last_action_date': http_date(time.mktime(tic.last_action_date.timetuple()))}) elif mode == 'gallery': images = tickets_files.objects.filter(ticket=ticket, active_record=True) return render(request, 'tickets/gallery.html', {'layout': 'horizontal', 'ticket': tic, 'images': images}) elif mode == 'history': history = tickets_history.objects.filter(ticket=ticket) return render(request, 'tickets/history.html', {'layout': 'horizontal', 'ticket': tic, 'history': history, 'keep_it_simple': keep_it_simple}) elif mode == 'reopen': if tic.closed: tic.closed = False tic.state = get_flow_start() tic.resolution = None tic.close_date = None tic.save(user=request.user) com = tickets_comments() com.comment = _('ticket reopend - resolution deleted') com.ticket_id = ticket com.action = 2 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) add_history(request, tic, 2, None) mail_comment(request, com.pk) jabber_comment(request, com.pk) return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif mode == 'move': if not tic.closed: old_state = tic.state tic.state = ticket_flow.objects.get(pk=request.POST['state']) tic.save(user=request.user) touch_ticket(request.user, ticket) oldUser = str(User.objects.get(pk=tic.assigned_id)) if tic.assigned_id else None history_data = { 'old': {'comment': '', 'assigned': oldUser, 'state': str(old_state)}, 'new': {'comment': _('ticket moved'), 'assigned': oldUser, 'state': str(tic.state)} } add_history(request, tic, 7, history_data) return HttpResponse('OK') elif mode == 'reassign': if not tic.closed: if 'assigned' in request.POST: if request.POST['assigned'] and int(request.POST['assigned']) > 0: old_assigned_user = tic.assigned old_state = tic.state tic.assigned_id = request.POST['assigned'] tic.state = ticket_flow.objects.get(pk=request.POST['state']) tic.save(user=request.user) newUser = User.objects.get(pk=request.POST['assigned']) com = tickets_comments() com.comment = _('ticket reassigned to %(user)s\nstate now: %(state)s\n\n%(comment)s') % {'user': newUser, 'comment': request.POST.get('reassign_comment', ''), 'state': tic.state} com.ticket_id = ticket com.action = 7 com.save(user=request.user) check_references(request, com) touch_ticket(request.user, ticket) if request.POST['assigned']: touch_ticket(newUser, ticket) mail_comment(request, com.pk) jabber_comment(request, com.pk) history_data = { 'old': {'comment': '', 'assigned': str(old_assigned_user), 'state': str(old_state)}, 'new': {'comment': request.POST.get('reassign_comment', ''), 'assigned': str(User.objects.get(pk=request.POST['assigned'])), 'state': str(tic.state)} } add_history(request, tic, 7, history_data) else: messages.add_message(request, messages.ERROR, _('missing assigned user')) return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif mode == 'edit' or (mode == 'simple' and (not tic.keep_it_simple or tic.closed) and keep_it_simple): excludes = ['resolution'] if request.method == 'POST': form = TicketsForm(request.POST, exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id) if form.is_valid(): tic = form.save() if tic.keep_it_simple: tic.keep_it_simple = False tic.save(user=request.user) assigned = form.cleaned_data.get('assigned') if assigned: touch_ticket(assigned, tic.pk) mail_ticket(request, tic.pk, form) jabber_ticket(request, tic.pk, form) remember_changes(request, form, tic) touch_ticket(request.user, tic.pk) return HttpResponseRedirect('/tickets/view/%s/' % ticket) else: form = TicketsForm(exclude_list=excludes, is_stuff=request.user.is_staff, user=request.user, instance=tic, customer=request.organisation.id) if 'state' in form.fields: form.fields['state'].queryset = form.fields['state'].queryset.exclude(type=2) return render(request, 'tickets/edit.html', {'ticket': tic, 'layout': 'horizontal', 'form': form}) elif mode == 'simple': if request.method == 'POST': form = SimpleTickets(request.POST, initial={ 'caption': tic.caption, 'description': tic.description, 'priority': tic.priority, 'assigned': tic.assigned }) if form.is_valid(): cd = form.cleaned_data tic.caption = cd['caption'] tic.description = cd['description'].replace(u"\u00A0", " ") tic.priority = cd['priority'] tic.assigned = cd['assigned'] tic.deadline = cd['deadline'] tic.show_start = cd['show_start'] tic.component = cd['component'] tic.save(user=request.user) if cd['assigned']: touch_ticket(cd['assigned'], tic.pk) remember_changes(request, form, tic) touch_ticket(request.user, tic.pk) mail_ticket(request, tic.pk, form) jabber_ticket(request, tic.pk, form) return HttpResponseRedirect('/tickets/view/%s/' % ticket) else: form = SimpleTickets(initial={ 'caption': tic.caption, 'description': tic.description, 'priority': tic.priority, 'assigned': tic.assigned, 'deadline': tic.deadline, 'show_start': tic.show_start, 'component': tic.component, }) return render(request, 'tickets/edit.html', {'ticket': tic, 'layout': 'horizontal', 'form': form, 'mode': mode}) elif mode == 'download': fileid = request.GET.get('file', -1) file_data = tickets_files.objects.get(id=fileid, ticket=ticket) src = '%s%s.dat' % (settings.FILE_UPLOAD_PATH, fileid) content_type = file_data.content_type if request.GET.get('preview') == 'yes' and os.path.isfile('%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid)): src = '%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid) content_type = 'imgae/png' if request.GET.get('resize', 'no') == 'yes' and ('image' in file_data.content_type or 'pdf' in file_data.content_type): img = resize_image('%s' % (src), (200, 150), 75) output = io.BytesIO() img.save(output, 'PNG') output.seek(0) response = StreamingHttpResponse(output, content_type='image/png') else: response = StreamingHttpResponse(open('%s' % (src), "rb"), content_type=content_type) if 'noDisposition' not in request.GET: if request.GET.get('preview') == 'yes' and os.path.isfile('%s%s.preview' % (settings.FILE_UPLOAD_PATH, fileid)): response['Content-Disposition'] = 'attachment;filename="%s"' % content_type else: response['Content-Disposition'] = 'attachment;filename="%s"' % smart_str(file_data.name) return response elif mode == 'upload': if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): if tickets_files.objects.filter(active_record=True, ticket=ticket, checksum=request.FILES['file'].hash).count() > 0: messages.add_message(request, messages.ERROR, _('File already exists: %s') % request.FILES['file'].name) if request.GET.get('Ajax') == '1': return HttpResponse('OK') return HttpResponseRedirect('/tickets/view/%s/' % ticket) f = tickets_files() f.name = request.FILES['file'].name f.size = request.FILES['file'].size f.checksum = request.FILES['file'].hash f.content_type = request.FILES['file'].content_type f.ticket_id = ticket f.public = True f.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 5, request.FILES['file'].name) mail_file(request, f.pk) jabber_file(request, f.pk) dest = settings.FILE_UPLOAD_PATH if not os.path.exists(dest): os.makedirs(dest) with open('%s%s.dat' % (dest, f.id), 'wb+') as destination: for chunk in request.FILES['file'].chunks(): destination.write(chunk) if 'pdf' in f.content_type: convertPDFtoImg('%s/%s.dat' % (dest, f.id), '%s/%s.preview' % (dest, f.id)) else: if 'image' not in f.content_type and isPreviewable(f.content_type): tmp = convertOfficeTpPDF('%s/%s.dat' % (dest, f.id)) convertPDFtoImg(tmp, '%s/%s.preview' % (dest, f.id)) if os.path.isfile(tmp): os.unlink(tmp) return HttpResponseRedirect('/tickets/view/%s/' % tic.pk) else: msg = unicode(form.errors['file']) msg = re.sub('<[^<]+?>', '', msg) messages.add_message(request, messages.ERROR, msg) if request.GET.get('Ajax') == '1': return HttpResponse('OK') return HttpResponseRedirect('/tickets/view/%s/' % ticket) elif request.method == 'PUT': # /tickets/upload/XXX/?filename=test1.txt upload_handlers = request.upload_handlers content_type = str(request.META.get('CONTENT_TYPE', "")) content_length = int(request.META.get('CONTENT_LENGTH', 0)) if content_type == "": return HttpResponse('missing ContentType', status=400) if content_length == 0: # both returned 0 return HttpResponse('missing ContentLength', status=400) content_type = content_type.split(";")[0].strip() try: charset = content_type.split(";")[1].strip() except IndexError: charset = "" # we can get the file name via the path, we don't actually file_name = request.GET['filename'] field_name = file_name counters = [0]*len(upload_handlers) for handler in upload_handlers: result = handler.handle_raw_input("", request.META, content_length, "", "") from django.core.files.uploadhandler import StopFutureHandlers for handler in upload_handlers: try: handler.new_file(field_name, file_name, content_type, content_length, charset) except StopFutureHandlers: break for i, handler in enumerate(upload_handlers): while True: chunk = request.read(handler.chunk_size) if chunk: handler.receive_data_chunk(chunk, counters[i]) counters[i] += len(chunk) else: # no chunk break for i, handler in enumerate(upload_handlers): file_obj = handler.file_complete(counters[i]) if file_obj: if settings.FILE_UPLOAD_VIRUS_SCAN and pyclamd: # virus scan try: if not hasattr(pyclamd, 'scan_stream'): cd = pyclamd.ClamdUnixSocket() else: pyclamd.init_network_socket('localhost', 3310) cd = pyclamd # We need to get a file object for clamav. We might have a path or we might # have to read the data into memory. if hasattr(file_obj, 'temporary_file_path'): os.chmod(file_obj.temporary_file_path(), 0664) result = cd.scan_file(file_obj.temporary_file_path()) else: if hasattr(file_obj, 'read'): result = cd.scan_stream(file_obj.read()) else: result = cd.scan_stream(file_obj['content']) except: from socket import gethostname raise Exception(_(u'unable to initialize scan engine on host %s') % gethostname()) if result: msg = ' '.join(result[result.keys()[0]]).replace('FOUND ', '') raise Exception(_(u"file is infected by virus: %s") % msg) hasher = hashlib.md5() # We need to get a file object for clamav. We might have a path or we might # have to read the data into memory. if hasattr(file_obj, 'temporary_file_path'): with open(file_obj.temporary_file_path(), 'rb') as afile: buf = afile.read() hasher.update(buf) hash = hasher.hexdigest() else: if hasattr(file_obj, 'read'): file_obj.seek(0) buf = file_obj.read() hasher.update(buf) else: hasher.update(file_obj['content'].read()) hash = hasher.hexdigest() if tickets_files.objects.filter(active_record=True, ticket=ticket, checksum=hash).count() > 0: raise Exception('duplicate hash value - file already exists in this ticket %s' % ticket) # todo: virusscan f = tickets_files() f.name = file_obj.name f.size = file_obj.size f.checksum = hash f.content_type = content_type f.ticket_id = ticket f.public = True f.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 5, file_obj.name) mail_file(request, f.pk) jabber_file(request, f.pk) dest = settings.FILE_UPLOAD_PATH if not os.path.exists(dest): os.makedirs(dest) with open('%s%s.dat' % (dest, f.id), 'wb+') as destination: for chunk in file_obj.chunks(): destination.write(chunk) if 'pdf' in f.content_type: convertPDFtoImg('%s/%s.dat' % (dest, f.id), '%s/%s.preview' % (dest, f.id)) else: if 'image' not in f.content_type and isPreviewable(f.content_type): try: tmp = convertOfficeTpPDF('%s/%s.dat' % (dest, f.id)) convertPDFtoImg(tmp, '%s/%s.preview' % (dest, f.id)) if os.path.isfile(tmp): os.unlink(tmp) except: pass if 'audio' in f.content_type: try: # https://realpython.com/python-speech-recognition/ import speech_recognition as sr AUDIO_FILE = '%s%s.dat' % (dest, f.id) r = sr.Recognizer() with sr.AudioFile(AUDIO_FILE) as source: audio = r.record(source) # read the entire audio file text = r.recognize_google(audio, language='de-DE') if text: com = tickets_comments() com.comment = text com.ticket_id = ticket com.action = 6 com.save(user=request.user) except: pass return HttpResponse(status=201) else: # some indication this didn't work? return HttpResponse(status=500) else: form = UploadFileForm() return render(request, 'tickets/file.html', {'ticketid': ticket, 'layout': 'horizontal', 'form': form}) elif mode == 'delfile': file = tickets_files.objects.get(pk=request.GET['fileid'], ticket=ticket) file.delete(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 8, file.name) return HttpResponseRedirect('/tickets/view/%s/#files' % tic.pk) elif mode == 'notify': tickets_participants.objects.filter(ticket=tic, user=request.user).update(seen=True) return HttpResponse('OK') elif mode == 'sleep': interval = request.GET.get('interval') if interval in ['1', '7', '30']: old = tic.show_start tic.show_start = timezone.now() + datetime.timedelta(days=int(interval)) tic.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 10, (tic.show_start, old)) return HttpResponse('OK') else: raise Exception('no interval given') elif mode == 'ignore': ig = tickets_ignorants() ig.ticket = tic ig.user = request.user ig.save() return HttpResponse('OK') elif mode == 'todo': class local: counter = 0 def ToDoDone(match): local.counter += 1 group = match.groups() if local.counter == pos: return u'[X]' else: return u'[%s]' % group[0] def ToDoUnDone(match): local.counter += 1 group = match.groups() if local.counter == pos: return u'[ ]' else: return u'[%s]' % group[0] form = ToDo(request.POST) if form.is_valid(): desc = tic.description cd = form.cleaned_data text = cd['text'] pos = cd['item'] set = cd['set'] if set: tic.description = re.sub(r'\[([ Xx])\]', ToDoDone, desc) old = _('undone: %s') % text new = _('done: %s') % text else: tic.description = re.sub(r'\[([ Xx])\]', ToDoUnDone, desc) new = _('undone: %s') % text old = _('done: %s') % text tic.save(user=request.user) touch_ticket(request.user, ticket) add_history(request, tic, 9, (new, old)) data = { 'set': set, 'item': pos, 'text': text, } return JsonResponse(data, safe=False) elif mode == 'update_comment': if request.method == 'POST': comment_id = request.GET.get("comment_id") comment = tickets_comments.objects.get(pk=comment_id) if comment.c_user == request.user: comment.comment = request.POST.get("comment_body", "") comment.edited = True comment.save(user=request.user) return HttpResponseRedirect("/tickets/view/%s/#comment_id-%s" % (ticket, comment_id)) elif mode == 'last_modified': if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE') if if_modified_since: if_modified_since = parse_http_date_safe(if_modified_since) if time.mktime(tic.last_action_date.timetuple()) > if_modified_since: return HttpResponse('outdated', status=200) else: return HttpResponse('not modified', status=304) else: return HttpResponse('unknown', status=412)