mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-12-01 12:23:16 +01:00
adding support for images as knowledgebase attachment
This commit is contained in:
parent
37be1346cd
commit
db0f286989
@ -2,7 +2,7 @@ from django.contrib import admin
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply, KBCategory
|
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply, KBCategory
|
||||||
from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
|
from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
|
||||||
from helpdesk.models import TicketChange, Attachment, IgnoreEmail
|
from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail
|
||||||
from helpdesk.models import CustomField
|
from helpdesk.models import CustomField
|
||||||
|
|
||||||
|
|
||||||
@ -43,15 +43,22 @@ class TicketAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
class TicketChangeInline(admin.StackedInline):
|
class TicketChangeInline(admin.StackedInline):
|
||||||
model = TicketChange
|
model = TicketChange
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class AttachmentInline(admin.StackedInline):
|
class FollowUpAttachmentInline(admin.StackedInline):
|
||||||
model = Attachment
|
model = FollowUpAttachment
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class KBIAttachmentInline(admin.StackedInline):
|
||||||
|
model = KBIAttachment
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
@admin.register(FollowUp)
|
@admin.register(FollowUp)
|
||||||
class FollowUpAdmin(admin.ModelAdmin):
|
class FollowUpAdmin(admin.ModelAdmin):
|
||||||
inlines = [TicketChangeInline, AttachmentInline]
|
inlines = [TicketChangeInline, FollowUpAttachmentInline]
|
||||||
list_display = ('ticket_get_ticket_for_url', 'title', 'date', 'ticket',
|
list_display = ('ticket_get_ticket_for_url', 'title', 'date', 'ticket',
|
||||||
'user', 'new_status', 'time_spent')
|
'user', 'new_status', 'time_spent')
|
||||||
list_filter = ('user', 'date', 'new_status')
|
list_filter = ('user', 'date', 'new_status')
|
||||||
@ -64,6 +71,7 @@ class FollowUpAdmin(admin.ModelAdmin):
|
|||||||
@admin.register(KBItem)
|
@admin.register(KBItem)
|
||||||
class KBItemAdmin(admin.ModelAdmin):
|
class KBItemAdmin(admin.ModelAdmin):
|
||||||
list_display = ('category', 'title', 'last_updated',)
|
list_display = ('category', 'title', 'last_updated',)
|
||||||
|
inlines = [KBIAttachmentInline]
|
||||||
readonly_fields = ('voted_by',)
|
readonly_fields = ('voted_by',)
|
||||||
|
|
||||||
list_display_links = ('title',)
|
list_display_links = ('title',)
|
||||||
|
@ -17,7 +17,7 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from helpdesk.lib import safe_template_context, process_attachments
|
from helpdesk.lib import safe_template_context, process_attachments
|
||||||
from helpdesk.models import (Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC,
|
from helpdesk.models import (Ticket, Queue, FollowUp, IgnoreEmail, TicketCC,
|
||||||
CustomField, TicketCustomFieldValue, TicketDependency, UserSettings)
|
CustomField, TicketCustomFieldValue, TicketDependency, UserSettings)
|
||||||
from helpdesk import settings as helpdesk_settings
|
from helpdesk import settings as helpdesk_settings
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from django.db.models import Q
|
|||||||
from django.utils.encoding import smart_text, smart_str
|
from django.utils.encoding import smart_text, smart_str
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from helpdesk.models import Attachment, EmailTemplate
|
from helpdesk.models import FollowUpAttachment, EmailTemplate
|
||||||
|
|
||||||
from model_utils import Choices
|
from model_utils import Choices
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ def process_attachments(followup, attached_files):
|
|||||||
|
|
||||||
if attached.size:
|
if attached.size:
|
||||||
filename = smart_text(attached.name)
|
filename = smart_text(attached.name)
|
||||||
att = Attachment(
|
att = FollowUpAttachment(
|
||||||
followup=followup,
|
followup=followup,
|
||||||
file=attached,
|
file=attached,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
|
36
helpdesk/migrations/0026_kbitem_attachments.py
Normal file
36
helpdesk/migrations/0026_kbitem_attachments.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 2.0.5 on 2019-03-07 20:30
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import helpdesk.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('helpdesk', '0025_queue_dedicated_time'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='KBIAttachment',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('file', models.FileField(max_length=1000, upload_to=helpdesk.models.attachment_path, verbose_name='File')),
|
||||||
|
('filename', models.CharField(max_length=1000, verbose_name='Filename')),
|
||||||
|
('mime_type', models.CharField(max_length=255, verbose_name='MIME Type')),
|
||||||
|
('size', models.IntegerField(help_text='Size of this file in bytes', verbose_name='Size')),
|
||||||
|
('kbitem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='helpdesk.KBItem', verbose_name='Knowledge base item')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Attachment',
|
||||||
|
'verbose_name_plural': 'Attachments',
|
||||||
|
'ordering': ('filename',),
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='Attachment',
|
||||||
|
new_name='FollowUpAttachment',
|
||||||
|
),
|
||||||
|
]
|
@ -17,6 +17,8 @@ from django.utils import timezone
|
|||||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import mimetypes
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
@ -904,18 +906,8 @@ class TicketChange(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
def attachment_path(instance, filename):
|
def attachment_path(instance, filename):
|
||||||
"""
|
"""Just bridge"""
|
||||||
Provide a file path that will help prevent files being overwritten, by
|
return instance.attachment_path(filename)
|
||||||
putting attachments in a folder off attachments for ticket/followup_id/.
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
os.umask(0)
|
|
||||||
path = 'helpdesk/attachments/%s-%s/%s' % (instance.followup.ticket.ticket_for_url, instance.followup.ticket.secret_key, instance.followup.id)
|
|
||||||
att_path = os.path.join(settings.MEDIA_ROOT, path)
|
|
||||||
if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage":
|
|
||||||
if not os.path.exists(att_path):
|
|
||||||
os.makedirs(att_path, 0o777)
|
|
||||||
return os.path.join(path, filename)
|
|
||||||
|
|
||||||
|
|
||||||
class Attachment(models.Model):
|
class Attachment(models.Model):
|
||||||
@ -924,12 +916,6 @@ class Attachment(models.Model):
|
|||||||
attachment, or it could be uploaded via the web interface.
|
attachment, or it could be uploaded via the web interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
followup = models.ForeignKey(
|
|
||||||
FollowUp,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name=_('Follow-up'),
|
|
||||||
)
|
|
||||||
|
|
||||||
file = models.FileField(
|
file = models.FileField(
|
||||||
_('File'),
|
_('File'),
|
||||||
upload_to=attachment_path,
|
upload_to=attachment_path,
|
||||||
@ -938,26 +924,102 @@ class Attachment(models.Model):
|
|||||||
|
|
||||||
filename = models.CharField(
|
filename = models.CharField(
|
||||||
_('Filename'),
|
_('Filename'),
|
||||||
|
blank=True,
|
||||||
max_length=1000,
|
max_length=1000,
|
||||||
)
|
)
|
||||||
|
|
||||||
mime_type = models.CharField(
|
mime_type = models.CharField(
|
||||||
_('MIME Type'),
|
_('MIME Type'),
|
||||||
|
blank=True,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
size = models.IntegerField(
|
size = models.IntegerField(
|
||||||
_('Size'),
|
_('Size'),
|
||||||
|
blank=True,
|
||||||
help_text=_('Size of this file in bytes'),
|
help_text=_('Size of this file in bytes'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s' % self.filename
|
return '%s' % self.filename
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
|
if not self.size:
|
||||||
|
self.size = self.get_size()
|
||||||
|
|
||||||
|
if not self.filename:
|
||||||
|
self.filename = self.get_filename()
|
||||||
|
|
||||||
|
if not self.mime_type:
|
||||||
|
self.mime_type = \
|
||||||
|
mimetypes.guess_type(self.filename, strict=False)[0] or \
|
||||||
|
'application/octet-stream'
|
||||||
|
|
||||||
|
return super(Attachment, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_filename(self):
|
||||||
|
return str(self.file)
|
||||||
|
|
||||||
|
def get_size(self):
|
||||||
|
return self.file.file.size
|
||||||
|
|
||||||
|
def attachment_path(self, filename):
|
||||||
|
"""Provide a file path that will help prevent files being overwritten, by
|
||||||
|
putting attachments in a folder off attachments for ticket/followup_id/.
|
||||||
|
"""
|
||||||
|
assert NotImplementedError(
|
||||||
|
"This method is to be implemented by Attachment classes"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('filename',)
|
ordering = ('filename',)
|
||||||
verbose_name = _('Attachment')
|
verbose_name = _('Attachment')
|
||||||
verbose_name_plural = _('Attachments')
|
verbose_name_plural = _('Attachments')
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class FollowUpAttachment(Attachment):
|
||||||
|
|
||||||
|
followup = models.ForeignKey(
|
||||||
|
FollowUp,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Follow-up'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def attachment_path(self, filename):
|
||||||
|
|
||||||
|
os.umask(0)
|
||||||
|
path = 'helpdesk/attachments/{ticket_for_url}-{secret_key}/{id_}'.format(
|
||||||
|
ticket_for_url=self.followup.ticket.ticket_for_url,
|
||||||
|
secret_key=self.followup.ticket.secret_key,
|
||||||
|
id_=self.followup.id)
|
||||||
|
att_path = os.path.join(settings.MEDIA_ROOT, path)
|
||||||
|
if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage":
|
||||||
|
if not os.path.exists(att_path):
|
||||||
|
os.makedirs(att_path, 0o777)
|
||||||
|
return os.path.join(path, filename)
|
||||||
|
|
||||||
|
|
||||||
|
class KBIAttachment(Attachment):
|
||||||
|
|
||||||
|
kbitem = models.ForeignKey(
|
||||||
|
"KBItem",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Knowledge base item'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def attachment_path(self, filename):
|
||||||
|
|
||||||
|
os.umask(0)
|
||||||
|
path = 'helpdesk/attachments/kb/{category}/{kbi}'.format(
|
||||||
|
category=self.kbitem.category,
|
||||||
|
kbi=self.kbitem.id)
|
||||||
|
att_path = os.path.join(settings.MEDIA_ROOT, path)
|
||||||
|
if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage":
|
||||||
|
if not os.path.exists(att_path):
|
||||||
|
os.makedirs(att_path, 0o777)
|
||||||
|
return os.path.join(path, filename)
|
||||||
|
|
||||||
|
|
||||||
class PreSetReply(models.Model):
|
class PreSetReply(models.Model):
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<li>{% blocktrans with change.field as field and change.old_value as old_value and change.new_value as new_value %}Changed {{ field }} from {{ old_value }} to {{ new_value }}.{% endblocktrans %}</li>
|
<li>{% blocktrans with change.field as field and change.old_value as old_value and change.new_value as new_value %}Changed {{ field }} from {{ old_value }} to {{ new_value }}.{% endblocktrans %}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul></div>{% endif %}
|
</ul></div>{% endif %}
|
||||||
{% for attachment in followup.attachment_set.all %}{% if forloop.first %}<div class='attachments'><ul>{% endif %}
|
{% for attachment in followup.followupattachment_set.all %}{% if forloop.first %}<div class='attachments'><ul>{% endif %}
|
||||||
<li><a href='{{ attachment.file.url }}'>{{ attachment.filename }}</a> ({{ attachment.mime_type }}, {{ attachment.size|filesizeformat }})</li>
|
<li><a href='{{ attachment.file.url }}'>{{ attachment.filename }}</a> ({{ attachment.mime_type }}, {{ attachment.size|filesizeformat }})</li>
|
||||||
{% if forloop.last %}</ul></div>{% endif %}
|
{% if forloop.last %}</ul></div>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -58,7 +58,7 @@ class AttachmentIntegrationTests(TestCase):
|
|||||||
self.assertContains(response, test_file.name)
|
self.assertContains(response, test_file.name)
|
||||||
|
|
||||||
# Ensure attachment is available with correct content
|
# Ensure attachment is available with correct content
|
||||||
att = models.Attachment.objects.get(followup__ticket=response.context['ticket'])
|
att = models.FollowUpAttachment.objects.get(followup__ticket=response.context['ticket'])
|
||||||
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk:
|
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk:
|
||||||
disk_content = file_on_disk.read()
|
disk_content = file_on_disk.read()
|
||||||
self.assertEqual(disk_content, 'attached file content')
|
self.assertEqual(disk_content, 'attached file content')
|
||||||
@ -76,7 +76,7 @@ class AttachmentIntegrationTests(TestCase):
|
|||||||
self.assertContains(response, test_file.name)
|
self.assertContains(response, test_file.name)
|
||||||
|
|
||||||
# Ensure attachment is available with correct content
|
# Ensure attachment is available with correct content
|
||||||
att = models.Attachment.objects.get(followup__ticket=response.context['ticket'])
|
att = models.FollowUpAttachment.objects.get(followup__ticket=response.context['ticket'])
|
||||||
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk:
|
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk:
|
||||||
disk_content = smart_text(file_on_disk.read(), 'utf-8')
|
disk_content = smart_text(file_on_disk.read(), 'utf-8')
|
||||||
self.assertEqual(disk_content, 'โจ')
|
self.assertEqual(disk_content, 'โจ')
|
||||||
@ -96,7 +96,7 @@ class AttachmentUnitTests(TestCase):
|
|||||||
self.test_file = SimpleUploadedFile.from_dict(self.file_attrs)
|
self.test_file = SimpleUploadedFile.from_dict(self.file_attrs)
|
||||||
self.follow_up = models.FollowUp(ticket=models.Ticket(queue=models.Queue()))
|
self.follow_up = models.FollowUp(ticket=models.Ticket(queue=models.Queue()))
|
||||||
|
|
||||||
@mock.patch('helpdesk.lib.Attachment', autospec=True)
|
@mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True)
|
||||||
def test_unicode_attachment_filename(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save):
|
def test_unicode_attachment_filename(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save):
|
||||||
""" check utf-8 data is parsed correcltly """
|
""" check utf-8 data is parsed correcltly """
|
||||||
filename, fileobj = lib.process_attachments(self.follow_up, [self.test_file])[0]
|
filename, fileobj = lib.process_attachments(self.follow_up, [self.test_file])[0]
|
||||||
@ -109,7 +109,42 @@ class AttachmentUnitTests(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(filename, self.file_attrs['filename'])
|
self.assertEqual(filename, self.file_attrs['filename'])
|
||||||
|
|
||||||
@mock.patch.object(lib.Attachment, 'save', autospec=True)
|
@mock.patch('helpdesk.lib.FollowUpAttachment', autospec=True)
|
||||||
|
def test_autofill(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save):
|
||||||
|
""" check utf-8 data is parsed correcltly """
|
||||||
|
self.follow_up.pk = 100
|
||||||
|
obj = models.FollowUpAttachment.objects.create(
|
||||||
|
followup=self.follow_up,
|
||||||
|
file=self.test_file
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.filename, self.file_attrs['filename'])
|
||||||
|
self.assertEqual(obj.size, len(self.file_attrs['content']))
|
||||||
|
self.assertEqual(obj.mime_type, "text/plain")
|
||||||
|
|
||||||
|
def test_kbi_attachment(self, mock_att_save, mock_queue_save, mock_ticket_save):
|
||||||
|
""" check utf-8 data is parsed correcltly """
|
||||||
|
|
||||||
|
kbcategory = models.KBCategory.objects.create(
|
||||||
|
title="Title",
|
||||||
|
slug="slug",
|
||||||
|
description="Description"
|
||||||
|
)
|
||||||
|
kbitem = models.KBItem.objects.create(
|
||||||
|
category=kbcategory,
|
||||||
|
title="Title",
|
||||||
|
question="Question",
|
||||||
|
answer="Answer"
|
||||||
|
)
|
||||||
|
|
||||||
|
obj = models.KBIAttachment.objects.create(
|
||||||
|
kbitem=kbitem,
|
||||||
|
file=self.test_file
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.filename, self.file_attrs['filename'])
|
||||||
|
self.assertEqual(obj.size, len(self.file_attrs['content']))
|
||||||
|
self.assertEqual(obj.mime_type, "text/plain")
|
||||||
|
|
||||||
|
@mock.patch.object(lib.FollowUpAttachment, 'save', autospec=True)
|
||||||
@override_settings(MEDIA_ROOT=MEDIA_DIR)
|
@override_settings(MEDIA_ROOT=MEDIA_DIR)
|
||||||
def test_unicode_filename_to_filesystem(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save):
|
def test_unicode_filename_to_filesystem(self, mock_att_save, mock_queue_save, mock_ticket_save, mock_follow_up_save):
|
||||||
""" don't mock saving to filesystem to test file renames caused by storage layer """
|
""" don't mock saving to filesystem to test file renames caused by storage layer """
|
||||||
@ -118,7 +153,7 @@ class AttachmentUnitTests(TestCase):
|
|||||||
attachment_obj = mock_att_save.call_args[0][0]
|
attachment_obj = mock_att_save.call_args[0][0]
|
||||||
|
|
||||||
mock_att_save.assert_called_once_with(attachment_obj)
|
mock_att_save.assert_called_once_with(attachment_obj)
|
||||||
self.assertIsInstance(attachment_obj, models.Attachment)
|
self.assertIsInstance(attachment_obj, models.FollowUpAttachment)
|
||||||
self.assertEqual(attachment_obj.filename, self.file_attrs['filename'])
|
self.assertEqual(attachment_obj.filename, self.file_attrs['filename'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from django.shortcuts import get_object_or_404
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
|
|
||||||
from helpdesk.models import Queue, Ticket, TicketCC, FollowUp, Attachment
|
from helpdesk.models import Queue, Ticket, TicketCC, FollowUp, FollowUpAttachment
|
||||||
import helpdesk.email
|
import helpdesk.email
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
@ -53,7 +53,7 @@ class GetEmailCommonTests(TestCase):
|
|||||||
with open(os.path.join(THIS_DIR, "test_files/blank-body-with-attachment.eml")) as fd:
|
with open(os.path.join(THIS_DIR, "test_files/blank-body-with-attachment.eml")) as fd:
|
||||||
test_email = fd.read()
|
test_email = fd.read()
|
||||||
ticket = helpdesk.email.object_from_message(test_email, self.queue_public, self.logger)
|
ticket = helpdesk.email.object_from_message(test_email, self.queue_public, self.logger)
|
||||||
self.assertEqual(ticket.title, "Attachment without body")
|
self.assertEqual(ticket.title, "FollowUpAttachment without body")
|
||||||
self.assertEqual(ticket.description, "")
|
self.assertEqual(ticket.description, "")
|
||||||
|
|
||||||
def test_email_with_blank_body_and_attachment(self):
|
def test_email_with_blank_body_and_attachment(self):
|
||||||
@ -68,7 +68,7 @@ class GetEmailCommonTests(TestCase):
|
|||||||
followups = FollowUp.objects.filter(ticket=ticket)
|
followups = FollowUp.objects.filter(ticket=ticket)
|
||||||
self.assertEqual(len(followups), 1)
|
self.assertEqual(len(followups), 1)
|
||||||
followup = followups[0]
|
followup = followups[0]
|
||||||
attachments = Attachment.objects.filter(followup=followup)
|
attachments = FollowUpAttachment.objects.filter(followup=followup)
|
||||||
self.assertEqual(len(attachments), 1)
|
self.assertEqual(len(attachments), 1)
|
||||||
attachment = attachments[0]
|
attachment = attachments[0]
|
||||||
self.assertEqual(attachment.file.read().decode("utf-8"), '<div dir="ltr">Tohle je test českých písmen odeslaných z gmailu.</div>\n')
|
self.assertEqual(attachment.file.read().decode("utf-8"), '<div dir="ltr">Tohle je test českých písmen odeslaných z gmailu.</div>\n')
|
||||||
@ -363,7 +363,7 @@ class GetEmailParametricTemplate(object):
|
|||||||
# HTML MIME part should be attached to follow up
|
# HTML MIME part should be attached to follow up
|
||||||
followup1 = get_object_or_404(FollowUp, pk=1)
|
followup1 = get_object_or_404(FollowUp, pk=1)
|
||||||
self.assertEqual(followup1.ticket.id, 1)
|
self.assertEqual(followup1.ticket.id, 1)
|
||||||
attach1 = get_object_or_404(Attachment, pk=1)
|
attach1 = get_object_or_404(FollowUpAttachment, pk=1)
|
||||||
self.assertEqual(attach1.followup.id, 1)
|
self.assertEqual(attach1.followup.id, 1)
|
||||||
self.assertEqual(attach1.filename, 'email_html_body.html')
|
self.assertEqual(attach1.filename, 'email_html_body.html')
|
||||||
cc0 = get_object_or_404(TicketCC, pk=1)
|
cc0 = get_object_or_404(TicketCC, pk=1)
|
||||||
@ -382,7 +382,7 @@ class GetEmailParametricTemplate(object):
|
|||||||
# HTML MIME part should be attached to follow up
|
# HTML MIME part should be attached to follow up
|
||||||
followup2 = get_object_or_404(FollowUp, pk=2)
|
followup2 = get_object_or_404(FollowUp, pk=2)
|
||||||
self.assertEqual(followup2.ticket.id, 2)
|
self.assertEqual(followup2.ticket.id, 2)
|
||||||
attach2 = get_object_or_404(Attachment, pk=2)
|
attach2 = get_object_or_404(FollowUpAttachment, pk=2)
|
||||||
self.assertEqual(attach2.followup.id, 2)
|
self.assertEqual(attach2.followup.id, 2)
|
||||||
self.assertEqual(attach2.filename, 'email_html_body.html')
|
self.assertEqual(attach2.filename, 'email_html_body.html')
|
||||||
|
|
||||||
@ -449,7 +449,7 @@ class GetEmailParametricTemplate(object):
|
|||||||
# MIME part should be attached to follow up
|
# MIME part should be attached to follow up
|
||||||
followup1 = get_object_or_404(FollowUp, pk=1)
|
followup1 = get_object_or_404(FollowUp, pk=1)
|
||||||
self.assertEqual(followup1.ticket.id, 1)
|
self.assertEqual(followup1.ticket.id, 1)
|
||||||
attach1 = get_object_or_404(Attachment, pk=1)
|
attach1 = get_object_or_404(FollowUpAttachment, pk=1)
|
||||||
self.assertEqual(attach1.followup.id, 1)
|
self.assertEqual(attach1.followup.id, 1)
|
||||||
self.assertEqual(attach1.filename, 'signature.asc')
|
self.assertEqual(attach1.filename, 'signature.asc')
|
||||||
self.assertEqual(attach1.file.read(), b"""-----BEGIN PGP SIGNATURE-----
|
self.assertEqual(attach1.file.read(), b"""-----BEGIN PGP SIGNATURE-----
|
||||||
|
@ -42,7 +42,7 @@ from helpdesk.lib import (
|
|||||||
process_attachments, queue_template_context,
|
process_attachments, queue_template_context,
|
||||||
)
|
)
|
||||||
from helpdesk.models import (
|
from helpdesk.models import (
|
||||||
Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch,
|
Ticket, Queue, FollowUp, TicketChange, PreSetReply, FollowUpAttachment, SavedSearch,
|
||||||
IgnoreEmail, TicketCC, TicketDependency, UserSettings,
|
IgnoreEmail, TicketCC, TicketDependency, UserSettings,
|
||||||
)
|
)
|
||||||
from helpdesk import settings as helpdesk_settings
|
from helpdesk import settings as helpdesk_settings
|
||||||
@ -269,7 +269,7 @@ def followup_edit(request, ticket_id, followup_id):
|
|||||||
new_followup.user = followup.user
|
new_followup.user = followup.user
|
||||||
new_followup.save()
|
new_followup.save()
|
||||||
# get list of old attachments & link them to new_followup
|
# get list of old attachments & link them to new_followup
|
||||||
attachments = Attachment.objects.filter(followup=followup)
|
attachments = FolllowUpAttachment.objects.filter(followup=followup)
|
||||||
for attachment in attachments:
|
for attachment in attachments:
|
||||||
attachment.followup = new_followup
|
attachment.followup = new_followup
|
||||||
attachment.save()
|
attachment.save()
|
||||||
@ -1581,7 +1581,7 @@ def attachment_del(request, ticket_id, attachment_id):
|
|||||||
if not _is_my_ticket(request.user, ticket):
|
if not _is_my_ticket(request.user, ticket):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
attachment = get_object_or_404(Attachment, id=attachment_id)
|
attachment = get_object_or_404(FolllowUpAttachment, id=attachment_id)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
attachment.delete()
|
attachment.delete()
|
||||||
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket_id]))
|
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket_id]))
|
||||||
|
Loading…
Reference in New Issue
Block a user