Merge master 0.2.3 bugfixes into develop

This commit is contained in:
Garret Wassermann 2017-12-09 23:02:12 -05:00
commit 92b43ef495
23 changed files with 3472 additions and 540 deletions

View File

@ -7,7 +7,7 @@ python:
- "3.6"
env:
- DJANGO=1.11.4
- DJANGO=1.11.8
install:
- pip install -q Django==$DJANGO
@ -15,7 +15,7 @@ install:
- pip install -q -r requirements-testing.txt
before_script:
- "pep8 --exclude=migrations --ignore=E501 helpdesk"
- "pycodestyle --exclude=migrations --ignore=E501 helpdesk"
script:
- coverage run --source='.' quicktest.py helpdesk

View File

@ -62,6 +62,7 @@ TEMPLATES = [
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'debug': True,
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',

View File

@ -13,7 +13,7 @@ project_root = os.path.dirname(here)
NAME = 'django-helpdesk-demodesk'
DESCRIPTION = 'A demo Django project using django-helpdesk'
README = open(os.path.join(here, 'README.rst')).read()
VERSION = '0.2.0'
VERSION = '0.2.3'
#VERSION = open(os.path.join(project_root, 'VERSION')).read().strip()
AUTHOR = 'django-helpdesk team'
URL = 'https://github.com/django-helpdesk/django-helpdesk'

View File

@ -119,7 +119,7 @@ errors with trying to create User settings.
Ideally, accessing http://MEDIA_URL/helpdesk/attachments/ will give you a 403 access denied error.
7. If it's not already installed, install ``django-markdown-deux`` and ensure it's in your ``INSTALLED_APPS``::
7. If it's not already installed, install ``markdown_deux`` and ensure it's in your ``INSTALLED_APPS``::
pip install django-markdown-deux

View File

@ -1153,8 +1153,197 @@
"subject": "(Actualizado)",
"locale": "es"
}
},
{
"pk": 97,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "已分配_CC",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示, {{ ticket.submitter_email }}的工单 <a href=\"{{ ticket.staff_url }}\"><b>{{ ticket.ticket }}</b></a> (<em>{{ ticket.title }}</em>) {% if ticket.assigned_to %}已经分配给 {{ ticket.assigned_to }}{% else %}还未分配{% endif %}.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>",
"plain_text": "您好,\r\n\r\n温馨提示, {{ ticket.submitter_email }}提交的工单 {{ ticket.ticket }} (\"{{ ticket.title }}\") 已经 {% if ticket.assigned_to %}分配给 {{ ticket.assigned_to }}{% else %}未分配{% endif %}.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }}\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n",
"heading": "工单已分配",
"subject": "(已分配)",
"locale": "zh"
}
},
{
"pk": 98,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "assigned_owner",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示, 提交者{{ ticket.submitter_email }}的工单 <a href=\"{{ ticket.staff_url }}\"><b>{{ ticket.ticket }}</b></a> (<em>{{ ticket.title }}</em>) 已经分配给 <b>you</b>.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: YOU<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>",
"plain_text": "您好,\r\n\r\n温馨提示, 工单 提交者{{ ticket.submitter_email }}的工单{{ ticket.ticket }} 已经 分配给 you.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: YOU\r\n在线查看: {{ ticket.staff_url }}\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n",
"heading": "工单已分配给您",
"subject": "(已分配给您)",
"locale": "zh"
}
},
{
"pk": 99,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "closed_cc",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">工单 <i>{{ ticket.title }}</i> ('{{ ticket.title }}'){% if ticket.assigned_to %}, 分配给 {{ ticket.get_assigned_to }}{% endif %} 已经 关闭</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">提供的解决方案为:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ resolution }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果您想在线查看, 可以访问 <a href='{{ ticket.staff_url }}'>{{ ticket.staff_url }}</a>.</p>",
"plain_text": "您好,\r\n\r\n工单 {{ ticket.title }} (\"{{ ticket.title }}\"){% if ticket.assigned_to %}, 分配给 {{ ticket.assigned_to }}{% endif %} 已经 关闭\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n提供的解决方案为:\r\n\r\n{{ resolution }}\r\n\r\n",
"heading": "工单已关闭",
"subject": "(已关闭)",
"locale": "zh"
}
},
{
"pk": 100,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "closed_owner",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">以下分配给您的工单, 已经关闭</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">提供的解决方案为:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.resolution }}</blockquote>",
"plain_text": "您好,\r\n\r\n以下分配给您的工单, 已经关闭\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n如果您想在线查看 可以访问 {{ ticket.staff_url }}.\r\n\r\n",
"heading": "工单已关闭",
"subject": "(已关闭)",
"locale": "zh"
}
},
{
"pk": 101,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "closed_submitter",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">您最近记录了主题为<i>{{ ticket.title }}</i>的工单. 本邮件确认工单已经 关闭</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">已提供的方案为:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.resolution }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果您想在线查看, 可以访问 <a href=\"{{ ticket.ticket_url }}\">{{ ticket.ticket_url }}</a>. 如果您认为还需要后续工作, 请用原标题回复此邮件.</p>",
"plain_text": "您好,\r\n\r\n您最近记录了主题为\"{{ ticket.title }}\"的工单. 本邮件确认工单已经 关闭\r\n\r\nI如果您认为还需要后续工作 请用原标题回复此邮件.\r\n\r\n如果您想在线查看 可以访问 {{ ticket.ticket_url }}.\r\n\r\n提供的解决方案为:\r\n\r\n{{ ticket.resolution }}\r\n\r\n",
"heading": "工单已关闭",
"subject": "(已关闭)",
"locale": "zh"
}
},
{
"pk": 102,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "escalated_cc",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示, 工单 <i>{{ ticket.ticket }}</i> ('{{ ticket.title }}') 已经 自动提升优先级.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>",
"plain_text": "您好,\r\n\r\n温馨提示, 工单 {{ ticket.ticket }} (\"{{ ticket.title }}\") 已经自动提升优先级.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n",
"heading": "工单 已经提升优先级",
"subject": "(已经提升优先级)",
"locale": "zh"
}
},
{
"pk": 103,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "escalated_owner",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">A 当前分配给您的工单已经自动提升优先级 as it 已经 打开时间超过预期.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>",
"plain_text": "您好,\r\n\r\nA 分配给您的当前工单已经自动提升优先级, 因为打开时间已经超过预期.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n请查看此工单并尽快提供解决方案.\r\n\r\n",
"heading": "工单 已分配给 您 已经提升优先级",
"subject": "(已经提升优先级)",
"locale": "zh"
}
},
{
"pk": 104,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "escalated_submitter",
"html": "<p style=\"font-family: sans-serif; font-size: 11pt;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 11pt;\">您最近记录了主题为<i>{{ ticket.title }}</i>的工单. 本邮件是想提醒您工单自动升级, 因为已经 打开时间超过预期.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 11pt;\">我们将尽快查看并提供解决方案.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 11pt;\">如果您想在线查看, 可以访问 <a href=\"{{ ticket.ticket_url }}\">{{ ticket.ticket_url }}</a>.</p>",
"plain_text": "您好,\r\n\r\n您最近为我们记录了一个标题为 \"{{ ticket.title }}\" 的工单. 本邮件是想提醒您工单自动升级, 因为已经打开时间超过预期.\r\n\r\n我们将尽快查看并提供解决方案.\r\n\r\n如果您想在线查看 可以访问 {{ ticket.ticket_url }}.\r\n\r\n",
"heading": "您的 工单 已经 已经提升优先级",
"subject": "(已经提升优先级)",
"locale": "zh"
}
},
{
"pk": 105,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "newticket_cc",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示: 新工单已经打开.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">Description:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>",
"plain_text": "您好,\r\n\r\n温馨提示 新工单已经 打开.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\nDescription:\r\n{{ ticket.description }}\r\n\r\n",
"heading": "新工单已打开",
"subject": "(已打开)",
"locale": "zh"
}
},
{
"pk": 106,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "newticket_submitter",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示: 我们已收到您主题为 <i>{{ ticket.title }}</i>.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">您当前什么都不用做. 您的工单 已经 分配编号 <b>{{ ticket.ticket }}</b> 且将很快收到回复.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果您想告诉我们更多详情, 或者要查询此工单, 请在主题带上工单id <b>{{ ticket.ticket }}</b> . 最简单就是直接点这个消息的 \"回复\" 按钮.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果您希望在线查看并提供此工单的更多信息, 附加文件或者查看最近更新, 您可以访问 <a href=\"{{ ticket.ticket_url }}\">{{ ticket.ticket_url }}</a>.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">我们将调查您的问题并尽快解决. 您将通过此邮箱收到后续更新和解决方案.</p>",
"plain_text": "您好,\r\n\r\n温馨提示 我们已经收到您主题为 \"{{ ticket.title }}\" 的查询. \r\n\r\n您当前什么都不用做. 您的工单已经分配编号 {{ ticket.ticket }} 且将很快收到回复.\r\n\r\n如果您想告诉我们更多详情, 或者要查询此工单, 请在主题带上工单id '{{ ticket.ticket }}' . 最简单方式就是按下此消息 \"回复\" .\r\n\r\n如果您希望在线查看并提供此工单的更多信息, 附加文件或者查看最近更新, 您可以访问 {{ ticket.ticket_url }}.\r\n\r\n我们将调查您的问题并尽快解决. 您将通过此邮箱收到后续更新和解决方案.\r\n",
"heading": "您的工单已经打开",
"subject": "(已打开)",
"locale": "zh"
}
},
{
"pk": 107,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "解决_cc",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">以下工单 已经 解决.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">增加的解决方案为:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.resolution }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">此方案已经 邮件发送给提交者, 在您关闭之前需要他先确认.</p>",
"plain_text": "您好,\r\n\r\n以下工单 已经 解决:\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n提供的解决方案为:\r\n\r\n{{ ticket.resolution }}\r\n\r\n此方案已经 邮件发送给提交者, 在您关闭之前需要他先确认.\r\n\r\n",
"heading": "工单 解决",
"subject": "(解决)",
"locale": "zh"
}
},
{
"pk": 108,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "解决_owner",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">A 当前分配给您的工单已经 解决.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">增加的解决方案为:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.resolution }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">此方案已经 邮件发送给提交者, 在您关闭之前需要他先确认.</p>",
"plain_text": "您好,\r\n\r\nA 当前分配给您的工单已经 解决.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述为:\r\n\r\n{{ ticket.description }}\r\n\r\n提供的方案为:\r\n\r\n{{ ticket.resolution }}\r\n\r\n此方案已经 邮件发送给提交者, 在您关闭之前需要他先确认.\r\n\r\n",
"heading": "工单 解决",
"subject": "(解决)",
"locale": "zh"
}
},
{
"pk": 109,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "解决_submitter",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">您最近记录了主题为<i>{{ ticket.title }}</i>的工单. 此邮件是要告知您解决方案.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">工单添加了以下解决方案 <b>{{ ticket.ticket }}</b>:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ resolution }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">请您确认下此解决方案是否解决了您的问题, 这样我们可以关闭此工单? 如果有更多问题或者认为方案不够充分,, 请继续使用邮件的主题回复此邮件.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果您想在线查看, 可以访问 <a href=\"{{ ticket.ticket_url }}\">{{ ticket.ticket_url }}</a>.</p>",
"plain_text": "您好,\r\n\r\n您最近记录了主题为\"{{ ticket.title }}\"的工单. 此邮件是要告知您解决方案.\r\n\r\n工单添加了以下解决方案 {{ ticket.ticket }}:\r\n\r\n{{ resolution }}\r\n\r\n请您确认下此解决方案是否解决了您的问题 这样我们可以关闭此工单? 如果有更多问题或者认为方案不够充分,, 请继续使用邮件的主题回复此邮件.\r\n\r\n如果您想在线查看 可以访问 {{ ticket.ticket_url }}\r\n\r\n",
"heading": "您的 工单 已经 解决",
"subject": "(解决)",
"locale": "zh"
}
},
{
"pk": 110,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "updated_cc",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示, {{ ticket.submitter_email }} 的工单 {{ ticket.ticket }} (\"{{ ticket.title }}\") 已经更新.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">已添加以下评论:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ comment }}</blockquote>\r\n\r\n<p style=\"font-family: Tahoma, Arial, sans-serif; font-size: 11pt;\">本信息 {% if private %} 还没有 {% else %} 已经 {% endif %} 邮件发送给提交者.</p>",
"plain_text": "您好,\r\n\r\n温馨提示, 提交者{{ ticket.submitter_email }}的工单{{ ticket.ticket }} 已经更新.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述:\r\n\r\n{{ ticket.description }}\r\n\r\n已添加以下评论:\r\n\r\n{{ comment }}\r\n\r\n本信息 {% if private %}还没有{% else %} 已经 {% endif %} 邮件发送给提交者.\r\n\r\n如果您想在线查看 可以访问 {{ ticket.staff_url }}.\r\n\r\n",
"heading": "工单已更新",
"subject": "(已更新)",
"locale": "zh"
}
},
{
"pk": 111,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "updated_owner",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">温馨提示, 分配给您,提交者{{ ticket.submitter_email }}的工单{{ ticket.ticket }}, 已经更新.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">\r\n<b>工单 ID</b>: {{ ticket.ticket }}<br>\r\n<b>待办</b>: {{ queue.title }}<br>\r\n<b>标题</b>: {{ ticket.title }}<br>\r\n<b>已打开</b>: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}<br>\r\n<b>提交人</b>: {{ ticket.submitter_email|default:\"Unknown\" }}<br>\r\n<b>优先级</b>: {{ ticket.get_priority_display }}<br>\r\n<b>状态</b>: {{ ticket.get_status }}<br>\r\n<b>已分配给</b>: {{ ticket.get_assigned_to }}<br>\r\n<b><a href='{{ ticket.staff_url }}'>在线查看</a></b> 更新此工单 (需要登录)</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">原工单描述参考::</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ ticket.description|linebreaksbr }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">已添加以下评论:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ comment }}</blockquote>\r\n\r\n<p style=\"font-family: Tahoma, Arial, sans-serif; font-size: 11pt;\">本信息 {% if private %} 还未 {% else %} 已经 {% endif %} been 邮件发送给提交者.</p>",
"plain_text": "您好,\r\n\r\n温馨提示, 提交者分配给{{ ticket.submitter_email }} 您的工单 {{ ticket.ticket }} (\"{{ ticket.title }}\") , 已经更新.\r\n\r\n工单 ID: {{ ticket.ticket }}\r\n待办: {{ queue.title }}\r\n标题: {{ ticket.title }}\r\n已打开: {{ ticket.created|date:\"l N jS Y, \\a\\t P\" }}\r\n提交人: {{ ticket.submitter_email|default:\"Unknown\" }}\r\n优先级:{{ ticket.get_priority_display }}\r\n状态: {{ ticket.get_status }}\r\n已分配给: {{ ticket.get_assigned_to }}\r\n在线查看: {{ ticket.staff_url }} (需要登录)\r\n\r\n原始描述:\r\n\r\n{{ ticket.description }}\r\n\r\n添加了一下评论:\r\n\r\n{{ comment }}\r\n\r\n本信息 {% if private %}还没有 {% endif %} 邮件发送给提交者.\r\n\r\n如果您想在线查看 可以访问 {{ ticket.staff_url }}\r\n\r\n",
"heading": "工单已更新",
"subject": "(已更新)",
"locale": "zh"
}
},
{
"pk": 112,
"model": "helpdesk.emailtemplate",
"fields": {
"template_name": "updated_submitter",
"html": "<p style=\"font-family: sans-serif; font-size: 1em;\">您好,</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">您最近记录了主题为<i>{{ ticket.title }}</i>的工单. 此邮件是告知您工单的更新.</p>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">工单添加了以下评论 <b>{{ ticket.ticket }}</b>:</p>\r\n\r\n<blockquote style=\"font-family: sans-serif; font-size: 1em;\">{{ comment }}</blockquote>\r\n\r\n<p style=\"font-family: sans-serif; font-size: 1em;\">如果你需要提供更多信息, 请继续使用邮件的主题回复此邮件. 或者您可以在线查看和更新此工单 <a href=\"{{ ticket.ticket_url }}\">{{ ticket.ticket_url }}</a>.</p>",
"plain_text": "您好,\r\n\r\n您最近记录了主题为\"{{ ticket.title }}\"的工单. 此邮件是告知您工单的更新.\r\n\r\n工单添加了以下评论 {{ ticket.ticket }}:\r\n\r\n{{ comment }}\r\n\r\n如果你需要提供更多信息, 请继续使用邮件的主题回复此邮件. 或者您可以在线查看和更新此工单 {{ ticket.ticket_url }}\r\n\r\n",
"heading": "您的工单已经更新",
"subject": "(已更新)",
"locale": "zh"
}
}
]

View File

@ -267,13 +267,14 @@ def text_is_spam(text, request):
# False if it is not spam. If it cannot be checked for some reason, we
# assume it isn't spam.
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
try:
from helpdesk.akismet import Akismet
except:
except ImportError:
return False
try:
site = Site.objects.get_current()
except:
except ImproperlyConfigured:
site = Site(domain='configure-django-sites.com')
ak = Akismet(

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,8 @@ scripts/get_email.py - Designed to be run from cron, this script checks the
from __future__ import unicode_literals
from datetime import timedelta
import base64
import binascii
import email
import imaplib
import mimetypes
@ -21,10 +23,12 @@ from os.path import isfile, join
import poplib
import re
import socket
import base64
import binascii
import ssl
import sys
from time import ctime
from bs4 import BeautifulSoup
from email_reply_parser import EmailReplyParser
from django.core.files.base import ContentFile
@ -158,7 +162,17 @@ def process_queue(q, logger):
messagesInfo = server.list()[1]
logger.info("Received %d messages from POP3 server" % len(messagesInfo))
for msg in messagesInfo:
for msgRaw in messagesInfo:
if six.PY3 and type(msgRaw) is bytes:
# in py3, msgRaw may be a bytes object, decode to str
try:
msg = msgRaw.decode("utf-8")
except UnicodeError:
# if couldn't decode easily, just leave it raw
msg = msgRaw
else:
# already a str
msg = msgRaw
msgNum = msg.split(" ")[0]
logger.info("Processing message %s" % msgNum)
@ -189,11 +203,20 @@ def process_queue(q, logger):
logger.info("Attempting IMAP server login")
server.login(q.email_box_user or
settings.QUEUE_EMAIL_BOX_USER,
q.email_box_pass or
settings.QUEUE_EMAIL_BOX_PASSWORD)
server.select(q.email_box_imap_folder)
try:
server.login(q.email_box_user or
settings.QUEUE_EMAIL_BOX_USER,
q.email_box_pass or
settings.QUEUE_EMAIL_BOX_PASSWORD)
server.select(q.email_box_imap_folder)
except imaplib.IMAP.abort:
logger.error("IMAP login failed. Check that the server is accessible and that the username and password are correct.")
server.logout()
sys.exit()
except ssl.SSLError:
logger.error("IMAP login failed due to SSL error. This is often due to a timeout. Please check your connection and try again.")
server.logout()
sys.exit()
try:
status, data = server.search(None, 'NOT', 'DELETED')
@ -232,7 +255,7 @@ def process_queue(q, logger):
logger.info("Successfully processed message %d, ticket/comment created." % i)
try:
unlink(m) # delete message file if ticket was successful
except:
except OSError:
logger.error("Unable to delete message %d." % i)
else:
logger.info("Successfully deleted message %d." % i)
@ -245,7 +268,7 @@ def decodeUnknown(charset, string):
if not charset:
try:
return string.decode('utf-8', 'replace')
except:
except UnicodeError:
return string.decode('iso8859-1', 'replace')
return unicode(string, charset)
elif six.PY3:
@ -253,7 +276,7 @@ def decodeUnknown(charset, string):
if not charset:
try:
return str(string, encoding='utf-8', errors='replace')
except:
except UnicodeError:
return str(string, encoding='iso8859-1', errors='replace')
return str(string, encoding=charset, errors='replace')
return string
@ -344,10 +367,15 @@ def ticket_from_message(message, queue, logger):
if isinstance(payload, list):
payload = payload.pop().as_string()
payloadToWrite = payload
# check version of python to ensure use of only the correct error type
if six.PY2:
non_b64_err = binascii.Error
else:
non_b64_err = TypeError
try:
logger.debug("Try to base64 decode the attachment payload")
payloadToWrite = base64.decodestring(payload)
except (binascii.Error, TypeError):
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]))
@ -356,7 +384,12 @@ def ticket_from_message(message, queue, logger):
counter += 1
if not body:
body = _('No plain-text email body available. Please see attachment "email_html_body.html".')
mail = BeautifulSoup(part.get_payload(), "lxml")
if ">" in mail.text:
message_body = mail.text.split(">")[1]
body = message_body.encode('ascii', errors='ignore')
else:
body = mail.text
if ticket:
try:

View File

@ -526,10 +526,11 @@ class Ticket(models.Model):
a URL to the submitter of a ticket.
"""
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
try:
site = Site.objects.get_current()
except:
except ImproperlyConfigured:
site = Site(domain='configure-django-sites.com')
return u"http://%s%s?ticket=%s&email=%s" % (
site.domain,
@ -545,10 +546,11 @@ class Ticket(models.Model):
a staff member (in emails etc)
"""
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
try:
site = Site.objects.get_current()
except:
except ImproperlyConfigured:
site = Site(domain='configure-django-sites.com')
return u"http://%s%s" % (
site.domain,
@ -1149,6 +1151,7 @@ def create_usersettings(sender, instance, created, **kwargs):
if created:
UserSettings.objects.create(user=instance, settings=DEFAULT_USER_SETTINGS)
models.signals.post_save.connect(create_usersettings, sender=settings.AUTH_USER_MODEL)

View File

@ -9,7 +9,7 @@ from django.core.exceptions import ImproperlyConfigured
try:
DEFAULT_USER_SETTINGS = settings.HELPDESK_DEFAULT_SETTINGS
except:
except AttributeError:
DEFAULT_USER_SETTINGS = None
if not isinstance(DEFAULT_USER_SETTINGS, dict):

View File

@ -21,5 +21,6 @@ from django import template
def in_list(value, arg):
return value in (arg or [])
register = template.Library()
register.filter(in_list)

View File

@ -19,5 +19,6 @@ def load_helpdesk_settings(request):
print(e, file=sys.stderr)
return ''
register = Library()
register.filter('load_helpdesk_settings', load_helpdesk_settings)

View File

@ -22,5 +22,6 @@ def saved_queries(user):
print(e, file=sys.stderr)
return ''
register = Library()
register.filter('saved_queries', saved_queries)

View File

@ -42,5 +42,6 @@ def num_to_link(text):
text[:match.start() + 1], url, style, match.groups()[0], text[match.end():])
return mark_safe(text)
register = template.Library()
register.filter(num_to_link)

View File

@ -24,5 +24,6 @@ def user_admin_url(action):
user._meta.app_label, model_name,
action)
register = template.Library()
register.filter(user_admin_url)

View File

@ -355,6 +355,251 @@ class GetEmailParametricTemplate(object):
self.assertEqual(attach2.followup.id, 2)
self.assertEqual(attach2.filename, 'email_html_body.html')
def test_read_pgp_signed_email(self):
"""Tests reading a PGP signed email to ensure we handle base64
and PGP signatures appropriately."""
# example email text from #567 on GitHub
test_email = """Delivered-To: djangohelpdesk@example.com
Received: by 10.25.26.207 with SMTP id a198csp5858981lfa;
Wed, 8 Nov 2017 13:30:22 -0800 (PST)
X-Received: by 10.107.107.3 with SMTP id g3mr2603398ioc.250.1510176622046;
Wed, 08 Nov 2017 13:30:22 -0800 (PST)
ARC-Seal: i=2; a=rsa-sha256; t=1510176621; cv=pass;
d=google.com; s=arc-20160816;
b=qQ8kBj8+yIoWcJwFNHUlJDYz7P2NfILAxFsn9uPYzXNn/aRw695T1aNFgGL75KUhkA
nDw+h49SUGKDh9ehC+DEiPjwJIxAoz+86rqGWV6XPGW4gQ7GUkHs96CxWndTSD0hdcOl
vygeZrsgzpIOvDxJWrujDPZzcEjsPC2qy3KGsTqtbZGEsNhhRUD8rs/hBVVXaGBatLF+
Sz2krwBZz8Lm+mWRhScjmF12QIHcXe6qYrDLOLEK0+bRkRMS+ZXg9+GPwqHlp58GaHn+
6JncesW3q7k88RQsLlj/8PEw0z1wMndgBVWIcCEtLt4UhZtt/BDxmZSukNN0SzoH4e3k
mxOw==
ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
h=mime-version:user-agent:date:message-id:subject:from:to
:dkim-signature:arc-authentication-results:arc-message-signature
:arc-authentication-results;
bh=cQvDBdivwtDmp1Td9ZWaEf0S4IuZ4hPwaprxSv7XZuE=;
b=p/0Y4PgvEfGWZ8W3eqxzRnSGLbT9gObSU2OI/sLwiN4KFfVmGrBJYkx7DGija0A5eU
DBbETW/16pib+W0IOUtdD7Pt12oWA3Z/uRf7ybXnHIKZ+MObdCXqRJFkga6nY8tWD0H3
maquQR07Q54mYslVMEIKJUKJzVM86npLN2C756ZzZTXiGXf33iowO4/lciGmTAgi+y5p
fEDQCTMoSQ9iGbquFRgNHgMtIM5NWjeMksWKpnfbvZyKs0ZICcPklNxQkDCmDlrOBokT
Zs1RVsWZ7NyPdTomJ0SRyPeysM040aatmnwxFAzwe4GYFNUWZjaep7uPKKlZ4sV/aHBB
iHOQ==
ARC-Authentication-Results: i=2; mx.google.com;
dkim=pass header.i=@gmail.com header.s=20161025 header.b=AArzbi/1;
arc=pass (i=1 spf=pass spfdomain=gmail.com dkim=pass dkdomain=gmail.com dmarc=pass fromdomain=gmail.com);
spf=pass (google.com: domain of bugreporter@example.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=bugreporter@example.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gmail.com
Return-Path: <bugreporter@example.com>
Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41])
by mx.google.com with SMTPS id i86sor2420323ioo.204.2017.11.08.13.30.21
for <djangohelpdesk@example.com>
(Google Transport Security);
Wed, 08 Nov 2017 13:30:21 -0800 (PST)
Received-SPF: pass (google.com: domain of bugreporter@example.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41;
Authentication-Results: mx.google.com;
dkim=pass header.i=@gmail.com header.s=20161025 header.b=AArzbi/1;
arc=pass (i=1 spf=pass spfdomain=gmail.com dkim=pass dkdomain=gmail.com dmarc=pass fromdomain=gmail.com);
spf=pass (google.com: domain of bugreporter@example.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=bugreporter@example.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gmail.com
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20161025;
h=x-gm-message-state:dkim-signature:to:from:subject:message-id:date
:user-agent:mime-version;
bh=cQvDBdivwtDmp1Td9ZWaEf0S4IuZ4hPwaprxSv7XZuE=;
b=MCiZzHu6ZV3kMTQBRL/b5uBy4jbHFS97+z9apL239dYS+z0LlTiHpKbs3qohFe3As1
gu2l0SAcdGw0qeplgmOlX9HXvKetBRLldfHeX/JJZ2yokpjc6CxVT8gF8YP2UmfAs0cb
JI8TTDqiWmhayf7xfblRIUP7vfwyTH9cLmvKMMAqWvrppyUlqlxWgyO7xtzV9jdThpqP
O0jO9CqsRmbEDc4vZAtOTXm1O69jCz66oll6H4T5Nka9HUpyHFZzv7Z0j0F/5djfzjCQ
HCFZhzobEgZAmBC9o2Y5aDvKCnWJGR5kVTtBQaFCuxr57o4zq0D359V3gMMPRGMdujDP
hXAQ==
X-Google-Smtp-Source: ABhQp+SbAIRuabSw2EkD+7YFXtLiCFINtymAshxVYuNZhApd39ymv2m9UnIM3rZNIHonQBywtZ3VjalQxeN8lVuWD6OquEskEc8=
ARC-Seal: i=1; a=rsa-sha256; t=1510176621; cv=none;
d=google.com; s=arc-20160816;
b=mOqnqVV4oq14hoOdEA+yVvQYQd/sv/Qr//xmW6r94dKaUczdbFG+Uy8x7EbuF/ILJt
ByFmE8+HUH8tosfHn8+zFmsHFr3Wi7il64wdeuVqoOuDQS1HejcH9ln5LVjwsr7EE6Ly
6gCT7QupvSQ+FkhyNH+zNHuGztw5F4Sa2r5UlmR5VAJ4+V1MEfVYwzEr7vgPnmEj8jga
PtmD05EfYWrWt27Cw8oS+CgS0CNcHaaiRr7JX3EQbNRrLp5M9GjKhiq/ckt2a5NKJYMH
zISYQzxk7EgHGFrwn+JZx+oKqG3Zl2pd5oKmzJkFeSaGT+qYp3SES4z3Vi6z4VxGduox
f38g==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
h=mime-version:user-agent:date:message-id:subject:from:to
:dkim-signature:arc-authentication-results;
bh=cQvDBdivwtDmp1Td9ZWaEf0S4IuZ4hPwaprxSv7XZuE=;
b=R5FsED2qOoEJshMotswEPOAn8GyvaHHd4zM9wAH+qnzuoV9RFhSChbkAkypi73SPs/
D7K49dYKSfsuWPF1RXoD8qchVfROF5Y7kD0JHy7KJcuHXzwb5gYLNrZpB2R9XbBOGe1j
lgQvnEVwmgeJiLXKQVeQDECxs8DFlkIpPIbmJK02Ry/Q0S8TnBEs0mrWn49l70IsZB6U
0XCpUPAt9NhsIUxoZKZv+zOwpQq6uwJkqRa5ukH0OPRr891MpeZldw7+gINjxxEmPAS9
GYfMeCpX9afFbQMUizbUbKwOZPt7ahn3x1C5x4AwgQmtzXYfA/quyiXAukTzoYk8FUqs
U1QA==
ARC-Authentication-Results: i=1; gmr-mx.google.com;
dkim=pass header.i=@gmail.com header.s=20161025 header.b=AArzbi/1;
spf=pass (google.com: domain of bugreporter@example.com designates 2607:f8b0:400e:c00::233 as permitted sender) smtp.mailfrom=bugreporter@example.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gmail.com
Return-Path: <bugreporter@example.com>
Received: from mail-pf0-x233.google.com (mail-pf0-x233.google.com. [2607:f8b0:400e:c00::233])
by gmr-mx.google.com with ESMTPS id l10si463482ioc.2.2017.11.08.13.30.21
for <djangohelpdesk@example.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Wed, 08 Nov 2017 13:30:21 -0800 (PST)
Received-SPF: pass (google.com: domain of bugreporter@example.com designates 2607:f8b0:400e:c00::233 as permitted sender) client-ip=2607:f8b0:400e:c00::233;
Received: by mail-pf0-x233.google.com with SMTP id p87so2672006pfj.3
for <djangohelpdesk@example.com>; Wed, 08 Nov 2017 13:30:21 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20161025;
h=to:from:subject:message-id:date:user-agent:mime-version;
bh=cQvDBdivwtDmp1Td9ZWaEf0S4IuZ4hPwaprxSv7XZuE=;
b=AArzbi/1RXhgTnCQBzU6vCwndc0/vqLV9FCgiOTp3deq8kFYhtdJCaEBX9s7iJduV+
HobvLGsbmWU04Y1O3w8m4jyq5H4HJ1jAr1+i0Tf5jl264kmyu4eowOMkwIFo6UaSVQ/a
zP+EYW09fWSSNhljubLkGf62vZ9gD/RF5Awoady6u5/N1GU4GPVCEgsmiK7DmPB2EtSE
7YPz3o9l+kDy8bRnUFw0744B7VKiXrAcIqpfltJuItM4T7bS/jyjYMQbRn8W2MXpyGlI
LNwt3vUNdKtkcPTK54cs44HMaVA8wGCDaMHFP8JmoTKWSsOgZQja3cdEj/rooM8uz+dq
er5g==
X-Received: by 10.99.191.78 with SMTP id i14mr1746749pgo.220.1510176620834;
Wed, 08 Nov 2017 13:30:20 -0800 (PST)
Return-Path: <bugreporter@example.com>
Received: from [10.1.1.4] (d114-72-199-247.hum1.act.optusnet.com.au. [114.72.199.247])
by smtp.gmail.com with ESMTPSA id u131sm8656745pgc.89.2017.11.08.13.30.18
for <djangohelpdesk@example.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Wed, 08 Nov 2017 13:30:19 -0800 (PST)
To: djangohelpdesk@example.com
From: Bug Reporter <bugreporter@example.com>
Subject: example email that crashes django-helpdesk get_email
Message-ID: <8eef2077-8aff-9fb4-0e2a-9876ba2530b1@gmail.com>
Date: Thu, 9 Nov 2017 08:30:15 +1100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101
Thunderbird/52.4.0
MIME-Version: 1.0
Content-Type: multipart/signed; micalg=pgp-sha256;
protocol="application/pgp-signature";
boundary="vnaePdRl5oElllhQPTiU2WarPFVGINT69"
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--vnaePdRl5oElllhQPTiU2WarPFVGINT69
Content-Type: multipart/mixed; boundary="ckOQ1U5bPjO3W1sVnjdBaEigXBiwem2Rn";
protected-headers="v1"
From: Bug Reporter <bugreporter@example.com>
To: djangohelpdesk@example.com
Message-ID: <8eef2077-8aff-9fb4-0e2a-9876ba2530b1@gmail.com>
Subject: example email that crashes django-helpdesk get_email
--ckOQ1U5bPjO3W1sVnjdBaEigXBiwem2Rn
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Content-Language: en-US
hi, thanks for looking into this :)
https://github.com/django-helpdesk/django-helpdesk/issues/567#issuecommen=
t-342954233
--ckOQ1U5bPjO3W1sVnjdBaEigXBiwem2Rn--
--vnaePdRl5oElllhQPTiU2WarPFVGINT69
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"
-----BEGIN PGP SIGNATURE-----
iQIcBAEBCAAGBQJaA3dnAAoJELBLc7QPITnLN54P/3Zsu7+AIQWDFTvziJfCqswG
u99fG+iWa6ER+iuZG0YU1BdIxIjSKt1pvqB0yXITlT9FCdf1zc0pmeJ08I0a5pVa
iaym5prVUro5BNQ6Vqoo0jvOCKNrACtFNv85zDzXbPNP8TrUss41U+ackPHkOHov
cmJ5YZFQebYXXpibFSIDimVGfwI57vyTWvolttZFLSI1mgGX7MvHaKh253QLdXIo
EUih40rOw3f/nYPEKyW8QA72ImBsZdcZI5buiiCC1bgMkKSFSNAFiIanYEpGNMnO
3zYKBpbpBhnWSi5orwx47/v4/Yb/qVr5ppuV23+YoMfEGT8cHPTAdYpnpE27ByAv
jvpxKEwmkUzD1WxOmQdCcPJPyWz1OBUVvjj0nn0Espnz8V8esl9+IFs739lpFBHu
fWWA315LTmIJMGH5Ujf4myiQeXDo6Gsy6WhE13q7MKTq3tnyi5dJG9GJCBf646dL
RwcDf9O7MvKSV2kSPmryLnUF7D+2fva+Cy+CvJDVJCo5zr4ucXPXZ4htpI6Pjpd5
oPHvbqxSCMJrQ7eAFTYmBNGauSyr0XvGM1qmHBZD/laQEJHYgLT2ILrymZhVDHtK
W7tXhGjMoUvqAxiKkmG3UHFqN4k3EYo13PwoOWyJHD1M9ArbX/Sk9l8DDguCh3DW
a9eiiQ+3V1v+7wWHXCzq
=6JeP
-----END PGP SIGNATURE-----
--vnaePdRl5oElllhQPTiU2WarPFVGINT69--
"""
test_mail_len = len(test_email)
if self.socks:
from socks import ProxyConnectionError
with self.assertRaisesRegexp(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email')
else:
# Test local email reading
if self.method == 'local':
with mock.patch('helpdesk.management.commands.get_email.listdir') as mocked_listdir, \
mock.patch('helpdesk.management.commands.get_email.isfile') as mocked_isfile, \
mock.patch('builtins.open' if six.PY3 else '__builtin__.open', mock.mock_open(read_data=test_email)):
mocked_isfile.return_value = True
mocked_listdir.return_value = ['filename1']
call_command('get_email')
mocked_listdir.assert_called_with('/var/lib/mail/helpdesk/')
mocked_isfile.assert_any_call('/var/lib/mail/helpdesk/filename1')
elif self.method == 'pop3':
# mock poplib.POP3's list and retr methods to provide responses as per RFC 1939
pop3_emails = {
'1': ("+OK", test_email.split('\n')),
}
pop3_mail_list = ("+OK 1 message", ("1 %d" % test_mail_len))
mocked_poplib_server = mock.Mock()
mocked_poplib_server.list = mock.Mock(return_value=pop3_mail_list)
mocked_poplib_server.retr = mock.Mock(side_effect=lambda x: pop3_emails['1'])
with mock.patch('helpdesk.management.commands.get_email.poplib', autospec=True) as mocked_poplib:
mocked_poplib.POP3 = mock.Mock(return_value=mocked_poplib_server)
call_command('get_email')
elif self.method == 'imap':
# mock imaplib.IMAP4's search and fetch methods with responses from RFC 3501
imap_emails = {
"1": ("OK", (("1", test_email),)),
}
imap_mail_list = ("OK", ("1",))
mocked_imaplib_server = mock.Mock()
mocked_imaplib_server.search = mock.Mock(return_value=imap_mail_list)
# we ignore the second arg as the data item/mime-part is constant (RFC822)
mocked_imaplib_server.fetch = mock.Mock(side_effect=lambda x, _: imap_emails[x])
with mock.patch('helpdesk.management.commands.get_email.imaplib', autospec=True) as mocked_imaplib:
mocked_imaplib.IMAP4 = mock.Mock(return_value=mocked_imaplib_server)
call_command('get_email')
ticket1 = get_object_or_404(Ticket, pk=1)
self.assertEqual(ticket1.ticket_for_url, "QQ-%s" % ticket1.id)
self.assertEqual(ticket1.title, "example email that crashes django-helpdesk get_email")
self.assertEqual(ticket1.description, """hi, thanks for looking into this :)\n\nhttps://github.com/django-helpdesk/django-helpdesk/issues/567#issuecomment-342954233""")
# MIME part should be attached to follow up
followup1 = get_object_or_404(FollowUp, pk=1)
self.assertEqual(followup1.ticket.id, 1)
attach1 = get_object_or_404(Attachment, pk=1)
self.assertEqual(attach1.followup.id, 1)
self.assertEqual(attach1.filename, 'signature.asc')
self.assertEqual(attach1.file.read(), b"""-----BEGIN PGP SIGNATURE-----
iQIcBAEBCAAGBQJaA3dnAAoJELBLc7QPITnLN54P/3Zsu7+AIQWDFTvziJfCqswG
u99fG+iWa6ER+iuZG0YU1BdIxIjSKt1pvqB0yXITlT9FCdf1zc0pmeJ08I0a5pVa
iaym5prVUro5BNQ6Vqoo0jvOCKNrACtFNv85zDzXbPNP8TrUss41U+ackPHkOHov
cmJ5YZFQebYXXpibFSIDimVGfwI57vyTWvolttZFLSI1mgGX7MvHaKh253QLdXIo
EUih40rOw3f/nYPEKyW8QA72ImBsZdcZI5buiiCC1bgMkKSFSNAFiIanYEpGNMnO
3zYKBpbpBhnWSi5orwx47/v4/Yb/qVr5ppuV23+YoMfEGT8cHPTAdYpnpE27ByAv
jvpxKEwmkUzD1WxOmQdCcPJPyWz1OBUVvjj0nn0Espnz8V8esl9+IFs739lpFBHu
fWWA315LTmIJMGH5Ujf4myiQeXDo6Gsy6WhE13q7MKTq3tnyi5dJG9GJCBf646dL
RwcDf9O7MvKSV2kSPmryLnUF7D+2fva+Cy+CvJDVJCo5zr4ucXPXZ4htpI6Pjpd5
oPHvbqxSCMJrQ7eAFTYmBNGauSyr0XvGM1qmHBZD/laQEJHYgLT2ILrymZhVDHtK
W7tXhGjMoUvqAxiKkmG3UHFqN4k3EYo13PwoOWyJHD1M9ArbX/Sk9l8DDguCh3DW
a9eiiQ+3V1v+7wWHXCzq
=6JeP
-----END PGP SIGNATURE-----
""")
# should this be 'application/pgp-signature'?
# self.assertEqual(attach1.mime_type, 'text/plain')
class GetEmailCCHandling(TestCase):
"""TestCase that checks CC handling in email. Needs its own test harness."""

View File

@ -29,6 +29,7 @@ class DirectTemplateView(TemplateView):
context[key] = value
return context
app_name = 'helpdesk'
urlpatterns = [

View File

@ -168,6 +168,8 @@ def dashboard(request):
'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user,
'basic_ticket_stats': basic_ticket_stats,
})
dashboard = staff_member_required(dashboard)
@helpdesk_staff_member_required
@ -183,6 +185,8 @@ def delete_ticket(request, ticket_id):
else:
ticket.delete()
return HttpResponseRedirect(reverse('helpdesk:home'))
delete_ticket = staff_member_required(delete_ticket)
@ -234,6 +238,8 @@ def followup_edit(request, ticket_id, followup_id):
# delete old followup
followup.delete()
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id]))
followup_edit = staff_member_required(followup_edit)
@ -248,6 +254,8 @@ def followup_delete(request, ticket_id, followup_id):
followup = get_object_or_404(FollowUp, id=followup_id)
followup.delete()
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id]))
followup_delete = staff_member_required(followup_delete)
@ -317,6 +325,8 @@ def view_ticket(request, ticket_id):
'ticketcc_string': ticketcc_string,
'SHOW_SUBSCRIBE': show_subscribe,
})
view_ticket = staff_member_required(view_ticket)
@ -759,6 +769,8 @@ def mass_update(request):
t.delete()
return HttpResponseRedirect(reverse('helpdesk:list'))
mass_update = staff_member_required(mass_update)
@ -943,6 +955,8 @@ def ticket_list(request):
saved_query=saved_query,
search_message=search_message,
))
ticket_list = staff_member_required(ticket_list)
@ -961,6 +975,8 @@ def edit_ticket(request, ticket_id):
form = EditTicketForm(instance=ticket)
return render(request, 'helpdesk/edit_ticket.html', {'form': form})
edit_ticket = staff_member_required(edit_ticket)
@ -999,6 +1015,8 @@ def create_ticket(request):
form.fields['assigned_to'].widget = forms.HiddenInput()
return render(request, 'helpdesk/create_ticket.html', {'form': form})
create_ticket = staff_member_required(create_ticket)
@ -1021,6 +1039,9 @@ def raw_details(request, type):
raise Http404
raw_details = staff_member_required(raw_details)
@helpdesk_staff_member_required
def hold_ticket(request, ticket_id, unhold=False):
ticket = get_object_or_404(Ticket, id=ticket_id)
@ -1048,14 +1069,22 @@ def hold_ticket(request, ticket_id, unhold=False):
return HttpResponseRedirect(ticket.get_absolute_url())
hold_ticket = staff_member_required(hold_ticket)
@helpdesk_staff_member_required
def unhold_ticket(request, ticket_id):
return hold_ticket(request, ticket_id, unhold=True)
unhold_ticket = staff_member_required(unhold_ticket)
@helpdesk_staff_member_required
def rss_list(request):
return render(request, 'helpdesk/rss_list.html', {'queues': Queue.objects.all()})
rss_list = staff_member_required(rss_list)
@ -1105,6 +1134,8 @@ def report_index(request):
'basic_ticket_stats': basic_ticket_stats,
'dash_tickets': dash_tickets,
})
report_index = staff_member_required(report_index)
@ -1138,7 +1169,7 @@ def run_report(request, report):
query_params = json.loads(b64decode(str(saved_query.query)).decode())
else:
query_params = json.loads(b64decode(str(saved_query.query)))
except:
except json.JSONDecodeError:
return HttpResponseRedirect(reverse('helpdesk:report_index'))
report_queryset = apply_query(report_queryset, query_params)
@ -1306,6 +1337,8 @@ def run_report(request, report):
'from_saved_query': from_saved_query,
'saved_query': saved_query,
})
run_report = staff_member_required(run_report)
@ -1324,6 +1357,8 @@ def save_query(request):
query.save()
return HttpResponseRedirect('%s?saved_query=%s' % (reverse('helpdesk:list'), query.id))
save_query = staff_member_required(save_query)
@ -1336,6 +1371,8 @@ def delete_saved_query(request, id):
return HttpResponseRedirect(reverse('helpdesk:list'))
else:
return render(request, 'helpdesk/confirm_delete_saved_query.html', {'query': query})
delete_saved_query = staff_member_required(delete_saved_query)
@ -1351,6 +1388,8 @@ def user_settings(request):
form = UserSettingsForm(s.settings)
return render(request, 'helpdesk/user_settings.html', {'form': form})
user_settings = staff_member_required(user_settings)
@ -1359,6 +1398,8 @@ def email_ignore(request):
return render(request, 'helpdesk/email_ignore_list.html', {
'ignore_list': IgnoreEmail.objects.all(),
})
email_ignore = superuser_required(email_ignore)
@ -1373,6 +1414,8 @@ def email_ignore_add(request):
form = EmailIgnoreForm(request.GET)
return render(request, 'helpdesk/email_ignore_add.html', {'form': form})
email_ignore_add = superuser_required(email_ignore_add)
@ -1384,6 +1427,8 @@ def email_ignore_del(request, id):
return HttpResponseRedirect(reverse('helpdesk:email_ignore'))
else:
return render(request, 'helpdesk/email_ignore_del.html', {'ignore': ignore})
email_ignore_del = superuser_required(email_ignore_del)
@ -1398,6 +1443,8 @@ def ticket_cc(request, ticket_id):
'copies_to': copies_to,
'ticket': ticket,
})
ticket_cc = staff_member_required(ticket_cc)
@ -1423,6 +1470,8 @@ def ticket_cc_add(request, ticket_id):
'form_email': form_email,
'form_user': form_user,
})
ticket_cc_add = staff_member_required(ticket_cc_add)
@ -1435,6 +1484,8 @@ def ticket_cc_del(request, ticket_id, cc_id):
return HttpResponseRedirect(reverse('helpdesk:ticket_cc',
kwargs={'ticket_id': cc.ticket.id}))
return render(request, 'helpdesk/ticket_cc_del.html', {'cc': cc})
ticket_cc_del = staff_member_required(ticket_cc_del)
@ -1457,6 +1508,8 @@ def ticket_dependency_add(request, ticket_id):
'ticket': ticket,
'form': form,
})
ticket_dependency_add = staff_member_required(ticket_dependency_add)
@ -1467,6 +1520,8 @@ def ticket_dependency_del(request, ticket_id, dependency_id):
dependency.delete()
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket_id]))
return render(request, 'helpdesk/ticket_dependency_del.html', {'dependency': dependency})
ticket_dependency_del = staff_member_required(ticket_dependency_del)
@ -1484,6 +1539,8 @@ def attachment_del(request, ticket_id, attachment_id):
'attachment': attachment,
'filename': attachment.filename,
})
attachment_del = staff_member_required(attachment_del)

View File

@ -1,5 +1,5 @@
pysocks
pep8
pycodestyle
codecov
coverage
argparse

View File

@ -2,6 +2,8 @@ Django>=1.11,<2
django-bootstrap-form>=3.3,<4
email-reply-parser
django-markdown-deux
beautifulsoup4
lxml
simplejson
pytz
six