Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
ebe129b3
Commit
ebe129b3
authored
Nov 15, 2019
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Update] 修改工单
parent
18f88647
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
406 additions
and
520 deletions
+406
-520
login_confirm.py
apps/authentication/api/login_confirm.py
+4
-4
mixins.py
apps/authentication/mixins.py
+2
-2
models.py
apps/authentication/models.py
+11
-9
login_wait_confirm.html
...tication/templates/authentication/login_wait_confirm.html
+2
-2
api_urls.py
apps/authentication/urls/api_urls.py
+1
-1
login.py
apps/authentication/views/login.py
+3
-3
django.mo
apps/locale/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/locale/zh/LC_MESSAGES/django.po
+148
-117
_nav.html
apps/templates/_nav.html
+3
-5
__init__.py
apps/tickets/api/__init__.py
+1
-2
login_confirm.py
apps/tickets/api/login_confirm.py
+0
-16
ticket.py
apps/tickets/api/ticket.py
+7
-1
0001_initial.py
apps/tickets/migrations/0001_initial.py
+6
-16
0002_auto_20191114_1105.py
apps/tickets/migrations/0002_auto_20191114_1105.py
+0
-24
__init__.py
apps/tickets/models/__init__.py
+1
-2
login_confirm.py
apps/tickets/models/login_confirm.py
+0
-33
ticket.py
apps/tickets/models/ticket.py
+30
-0
__init__.py
apps/tickets/serializers/__init__.py
+1
-2
login_confirm.py
apps/tickets/serializers/login_confirm.py
+0
-53
ticket.py
apps/tickets/serializers/ticket.py
+19
-0
signals_handler.py
apps/tickets/signals_handler.py
+8
-8
login_confirm_ticket_detail.html
...ickets/templates/tickets/login_confirm_ticket_detail.html
+0
-34
login_confirm_ticket_list.html
.../tickets/templates/tickets/login_confirm_ticket_list.html
+0
-154
ticket_detail.html
apps/tickets/templates/tickets/ticket_detail.html
+27
-8
ticket_list.html
apps/tickets/templates/tickets/ticket_list.html
+115
-0
api_urls.py
apps/tickets/urls/api_urls.py
+0
-1
views_urls.py
apps/tickets/urls/views_urls.py
+2
-2
utils.py
apps/tickets/utils.py
+5
-13
views.py
apps/tickets/views.py
+10
-8
No files found.
apps/authentication/api/login_confirm.py
View file @
ebe129b3
...
@@ -12,7 +12,7 @@ from ..models import LoginConfirmSetting
...
@@ -12,7 +12,7 @@ from ..models import LoginConfirmSetting
from
..serializers
import
LoginConfirmSettingSerializer
from
..serializers
import
LoginConfirmSettingSerializer
from
..
import
errors
,
mixins
from
..
import
errors
,
mixins
__all__
=
[
'LoginConfirmSettingUpdateApi'
,
'
LoginConfirm
TicketStatusApi'
]
__all__
=
[
'LoginConfirmSettingUpdateApi'
,
'TicketStatusApi'
]
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
...
@@ -31,17 +31,17 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
...
@@ -31,17 +31,17 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
return
s
return
s
class
LoginConfirm
TicketStatusApi
(
mixins
.
AuthMixin
,
APIView
):
class
TicketStatusApi
(
mixins
.
AuthMixin
,
APIView
):
permission_classes
=
()
permission_classes
=
()
def
get_ticket
(
self
):
def
get_ticket
(
self
):
from
tickets.models
import
LoginConfirm
Ticket
from
tickets.models
import
Ticket
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
logger
.
debug
(
'Login confirm ticket id: {}'
.
format
(
ticket_id
))
logger
.
debug
(
'Login confirm ticket id: {}'
.
format
(
ticket_id
))
if
not
ticket_id
:
if
not
ticket_id
:
ticket
=
None
ticket
=
None
else
:
else
:
ticket
=
get_object_or_none
(
LoginConfirm
Ticket
,
pk
=
ticket_id
)
ticket
=
get_object_or_none
(
Ticket
,
pk
=
ticket_id
)
return
ticket
return
ticket
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
...
...
apps/authentication/mixins.py
View file @
ebe129b3
...
@@ -104,13 +104,13 @@ class AuthMixin:
...
@@ -104,13 +104,13 @@ class AuthMixin:
raise
errors
.
MFAFailedError
(
username
=
user
.
username
,
request
=
self
.
request
)
raise
errors
.
MFAFailedError
(
username
=
user
.
username
,
request
=
self
.
request
)
def
get_ticket
(
self
):
def
get_ticket
(
self
):
from
tickets.models
import
LoginConfirm
Ticket
from
tickets.models
import
Ticket
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
logger
.
debug
(
'Login confirm ticket id: {}'
.
format
(
ticket_id
))
logger
.
debug
(
'Login confirm ticket id: {}'
.
format
(
ticket_id
))
if
not
ticket_id
:
if
not
ticket_id
:
ticket
=
None
ticket
=
None
else
:
else
:
ticket
=
get_object_or_none
(
LoginConfirm
Ticket
,
pk
=
ticket_id
)
ticket
=
get_object_or_none
(
Ticket
,
pk
=
ticket_id
)
return
ticket
return
ticket
def
get_ticket_or_create
(
self
,
confirm_setting
):
def
get_ticket_or_create
(
self
,
confirm_setting
):
...
...
apps/authentication/models.py
View file @
ebe129b3
...
@@ -49,23 +49,25 @@ class LoginConfirmSetting(CommonModelMixin):
...
@@ -49,23 +49,25 @@ class LoginConfirmSetting(CommonModelMixin):
return
get_object_or_none
(
cls
,
user
=
user
)
return
get_object_or_none
(
cls
,
user
=
user
)
def
create_confirm_ticket
(
self
,
request
=
None
):
def
create_confirm_ticket
(
self
,
request
=
None
):
from
tickets.models
import
LoginConfirm
Ticket
from
tickets.models
import
Ticket
title
=
_
(
'User login confirm: {}'
)
.
format
(
self
.
user
)
title
=
'['
+
__
(
'Login confirm'
)
+
']: {}'
.
format
(
self
.
user
)
if
request
:
if
request
:
remote_addr
=
get_request_ip
(
request
)
remote_addr
=
get_request_ip
(
request
)
city
=
get_ip_city
(
remote_addr
)
city
=
get_ip_city
(
remote_addr
)
body
=
_
(
"User: {}
\n
IP: {}
\n
City: {}
\n
Date: {}
\n
"
)
.
format
(
body
=
__
(
"{user_key}: {username}<br>"
self
.
user
,
remote_addr
,
city
,
timezone
.
now
()
"IP: {ip}<br>"
"{city_key}: {city}<br>"
"{date_key}: {date}<br>"
)
.
format
(
user_key
=
__
(
"User"
),
username
=
self
.
user
,
ip
=
remote_addr
,
city_key
=
_
(
"City"
),
city
=
city
,
date_key
=
__
(
"Datetime"
),
date
=
timezone
.
now
()
)
)
else
:
else
:
city
=
'Localhost'
remote_addr
=
'127.0.0.1'
body
=
''
body
=
''
reviewer
=
self
.
reviewers
.
all
()
reviewer
=
self
.
reviewers
.
all
()
ticket
=
LoginConfirm
Ticket
.
objects
.
create
(
ticket
=
Ticket
.
objects
.
create
(
user
=
self
.
user
,
title
=
title
,
body
=
body
,
user
=
self
.
user
,
title
=
title
,
body
=
body
,
city
=
city
,
ip
=
remote_addr
,
type
=
Ticket
.
TYPE_LOGIN_CONFIRM
,
type
=
LoginConfirmTicket
.
TYPE_LOGIN_CONFIRM
,
)
)
ticket
.
assignees
.
set
(
reviewer
)
ticket
.
assignees
.
set
(
reviewer
)
return
ticket
return
ticket
...
...
apps/authentication/templates/authentication/login_wait_confirm.html
View file @
ebe129b3
...
@@ -126,7 +126,7 @@ function handleProgressBar() {
...
@@ -126,7 +126,7 @@ function handleProgressBar() {
progressBarRef
.
attr
(
'aria-valuenow'
,
offset
);
progressBarRef
.
attr
(
'aria-valuenow'
,
offset
);
}
}
function
cancel
LoginConfirm
Ticket
()
{
function
cancelTicket
()
{
requestApi
({
requestApi
({
url
:
url
,
url
:
url
,
method
:
"DELETE"
,
method
:
"DELETE"
,
...
@@ -144,7 +144,7 @@ function setCloseConfirm() {
...
@@ -144,7 +144,7 @@ function setCloseConfirm() {
return
'Confirm'
;
return
'Confirm'
;
};
};
window
.
onunload
=
function
(
e
)
{
window
.
onunload
=
function
(
e
)
{
cancel
LoginConfirm
Ticket
();
cancelTicket
();
}
}
}
}
...
...
apps/authentication/urls/api_urls.py
View file @
ebe129b3
...
@@ -18,7 +18,7 @@ urlpatterns = [
...
@@ -18,7 +18,7 @@ urlpatterns = [
path
(
'connection-token/'
,
path
(
'connection-token/'
,
api
.
UserConnectionTokenApi
.
as_view
(),
name
=
'connection-token'
),
api
.
UserConnectionTokenApi
.
as_view
(),
name
=
'connection-token'
),
path
(
'otp/verify/'
,
api
.
UserOtpVerifyApi
.
as_view
(),
name
=
'user-otp-verify'
),
path
(
'otp/verify/'
,
api
.
UserOtpVerifyApi
.
as_view
(),
name
=
'user-otp-verify'
),
path
(
'login-confirm-ticket/status/'
,
api
.
LoginConfirm
TicketStatusApi
.
as_view
(),
name
=
'login-confirm-ticket-status'
),
path
(
'login-confirm-ticket/status/'
,
api
.
TicketStatusApi
.
as_view
(),
name
=
'login-confirm-ticket-status'
),
path
(
'login-confirm-settings/<uuid:user_id>/'
,
api
.
LoginConfirmSettingUpdateApi
.
as_view
(),
name
=
'login-confirm-setting-update'
)
path
(
'login-confirm-settings/<uuid:user_id>/'
,
api
.
LoginConfirmSettingUpdateApi
.
as_view
(),
name
=
'login-confirm-setting-update'
)
]
]
...
...
apps/authentication/views/login.py
View file @
ebe129b3
...
@@ -144,16 +144,16 @@ class UserLoginWaitConfirmView(TemplateView):
...
@@ -144,16 +144,16 @@ class UserLoginWaitConfirmView(TemplateView):
template_name
=
'authentication/login_wait_confirm.html'
template_name
=
'authentication/login_wait_confirm.html'
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
from
tickets.models
import
LoginConfirm
Ticket
from
tickets.models
import
Ticket
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
ticket_id
=
self
.
request
.
session
.
get
(
"auth_ticket_id"
)
if
not
ticket_id
:
if
not
ticket_id
:
ticket
=
None
ticket
=
None
else
:
else
:
ticket
=
get_object_or_none
(
LoginConfirm
Ticket
,
pk
=
ticket_id
)
ticket
=
get_object_or_none
(
Ticket
,
pk
=
ticket_id
)
context
=
super
()
.
get_context_data
(
**
kwargs
)
context
=
super
()
.
get_context_data
(
**
kwargs
)
if
ticket
:
if
ticket
:
timestamp_created
=
datetime
.
datetime
.
timestamp
(
ticket
.
date_created
)
timestamp_created
=
datetime
.
datetime
.
timestamp
(
ticket
.
date_created
)
ticket_detail_url
=
reverse
(
'tickets:
login-confirm-
ticket-detail'
,
kwargs
=
{
'pk'
:
ticket_id
})
ticket_detail_url
=
reverse
(
'tickets:ticket-detail'
,
kwargs
=
{
'pk'
:
ticket_id
})
msg
=
_
(
"""Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>
msg
=
_
(
"""Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>
Don't close this page"""
)
.
format
(
ticket
.
assignees_display
)
Don't close this page"""
)
.
format
(
ticket
.
assignees_display
)
else
:
else
:
...
...
apps/locale/zh/LC_MESSAGES/django.mo
View file @
ebe129b3
No preview for this file type
apps/locale/zh/LC_MESSAGES/django.po
View file @
ebe129b3
...
@@ -8,7 +8,7 @@ msgid ""
...
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-1
4 19:2
9+0800\n"
"POT-Creation-Date: 2019-11-1
5 17:3
9+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
...
@@ -354,7 +354,6 @@ msgstr "重置"
...
@@ -354,7 +354,6 @@ msgstr "重置"
#: terminal/templates/terminal/command_list.html:47
#: terminal/templates/terminal/command_list.html:47
#: terminal/templates/terminal/session_list.html:52
#: terminal/templates/terminal/session_list.html:52
#: terminal/templates/terminal/terminal_update.html:46
#: terminal/templates/terminal/terminal_update.html:46
#: tickets/templates/tickets/login_confirm_ticket_list.html:32
#: users/templates/users/_user.html:52
#: users/templates/users/_user.html:52
#: users/templates/users/forgot_password.html:42
#: users/templates/users/forgot_password.html:42
#: users/templates/users/user_bulk_update.html:24
#: users/templates/users/user_bulk_update.html:24
...
@@ -528,8 +527,7 @@ msgstr "创建远程应用"
...
@@ -528,8 +527,7 @@ msgstr "创建远程应用"
#: settings/templates/settings/terminal_setting.html:107
#: settings/templates/settings/terminal_setting.html:107
#: terminal/templates/terminal/session_list.html:36
#: terminal/templates/terminal/session_list.html:36
#: terminal/templates/terminal/terminal_list.html:36
#: terminal/templates/terminal/terminal_list.html:36
#: tickets/templates/tickets/login_confirm_ticket_list.html:18
#: tickets/templates/tickets/ticket_list.html:93
#: tickets/templates/tickets/login_confirm_ticket_list.html:106
#: users/templates/users/_granted_assets.html:34
#: users/templates/users/_granted_assets.html:34
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_list.html:41
#: users/templates/users/user_list.html:41
...
@@ -1046,7 +1044,8 @@ msgstr "过滤器"
...
@@ -1046,7 +1044,8 @@ msgstr "过滤器"
#: settings/templates/settings/replay_storage_create.html:31
#: settings/templates/settings/replay_storage_create.html:31
#: settings/templates/settings/terminal_setting.html:84
#: settings/templates/settings/terminal_setting.html:84
#: settings/templates/settings/terminal_setting.html:106
#: settings/templates/settings/terminal_setting.html:106
#: tickets/models/base.py:34 tickets/templates/tickets/ticket_detail.html:33
#: tickets/models/ticket.py:42 tickets/templates/tickets/ticket_detail.html:33
#: tickets/templates/tickets/ticket_list.html:23
msgid "Type"
msgid "Type"
msgstr "类型"
msgstr "类型"
...
@@ -1127,11 +1126,10 @@ msgstr "默认资产组"
...
@@ -1127,11 +1126,10 @@ msgstr "默认资产组"
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:29
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:29
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:71 tickets/models/base.py:25
#: terminal/templates/terminal/session_list.html:71 tickets/models/ticket.py:32
#: tickets/models/base.py:68
#: tickets/models/ticket.py:85 tickets/templates/tickets/ticket_detail.html:32
#: tickets/templates/tickets/login_confirm_ticket_list.html:15
#: tickets/templates/tickets/ticket_list.html:22
#: tickets/templates/tickets/login_confirm_ticket_list.html:101
#: tickets/templates/tickets/ticket_list.html:88 users/forms.py:339
#: tickets/templates/tickets/ticket_detail.html:32 users/forms.py:339
#: users/models/user.py:149 users/models/user.py:165 users/models/user.py:537
#: users/models/user.py:149 users/models/user.py:165 users/models/user.py:537
#: users/serializers/group.py:21
#: users/serializers/group.py:21
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_detail.html:78
...
@@ -1506,7 +1504,7 @@ msgstr "获取认证信息错误"
...
@@ -1506,7 +1504,7 @@ msgstr "获取认证信息错误"
#: authentication/templates/authentication/_access_key_modal.html:142
#: authentication/templates/authentication/_access_key_modal.html:142
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
#: settings/templates/settings/_ldap_list_users_modal.html:171
#: settings/templates/settings/_ldap_list_users_modal.html:171
#: templates/_modal.html:22 tickets/models/
base.py:50
#: templates/_modal.html:22 tickets/models/
ticket.py:67
#: tickets/templates/tickets/ticket_detail.html:103
#: tickets/templates/tickets/ticket_detail.html:103
msgid "Close"
msgid "Close"
msgstr "关闭"
msgstr "关闭"
...
@@ -1517,7 +1515,7 @@ msgstr "关闭"
...
@@ -1517,7 +1515,7 @@ msgstr "关闭"
#: ops/templates/ops/task_adhoc.html:63
#: ops/templates/ops/task_adhoc.html:63
#: terminal/templates/terminal/command_list.html:33
#: terminal/templates/terminal/command_list.html:33
#: terminal/templates/terminal/session_detail.html:50
#: terminal/templates/terminal/session_detail.html:50
#: tickets/templates/tickets/
login_confirm_ticket_list.html:17
#: tickets/templates/tickets/
ticket_list.html:25
msgid "Datetime"
msgid "Datetime"
msgstr "日期"
msgstr "日期"
...
@@ -1717,7 +1715,7 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件
...
@@ -1717,7 +1715,7 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件
#: users/templates/users/user_group_list.html:10
#: users/templates/users/user_group_list.html:10
#: users/templates/users/user_list.html:10
#: users/templates/users/user_list.html:10
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:49
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:49
#: xpack/plugins/vault/templates/vault/vault.html:
55
#: xpack/plugins/vault/templates/vault/vault.html:
47
msgid "Export"
msgid "Export"
msgstr "导出"
msgstr "导出"
...
@@ -1728,7 +1726,7 @@ msgstr "导出"
...
@@ -1728,7 +1726,7 @@ msgstr "导出"
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:15
#: users/templates/users/user_list.html:15
#: xpack/plugins/license/templates/license/license_detail.html:110
#: xpack/plugins/license/templates/license/license_detail.html:110
#: xpack/plugins/vault/templates/vault/vault.html:
60
#: xpack/plugins/vault/templates/vault/vault.html:
52
msgid "Import"
msgid "Import"
msgstr "导入"
msgstr "导入"
...
@@ -1747,7 +1745,7 @@ msgstr "创建管理用户"
...
@@ -1747,7 +1745,7 @@ msgstr "创建管理用户"
#: users/templates/users/user_group_list.html:195
#: users/templates/users/user_group_list.html:195
#: users/templates/users/user_list.html:165
#: users/templates/users/user_list.html:165
#: users/templates/users/user_list.html:197
#: users/templates/users/user_list.html:197
#: xpack/plugins/vault/templates/vault/vault.html:2
24
#: xpack/plugins/vault/templates/vault/vault.html:2
00
msgid "Please select file"
msgid "Please select file"
msgstr "选择文件"
msgstr "选择文件"
...
@@ -2239,7 +2237,7 @@ msgstr "成功"
...
@@ -2239,7 +2237,7 @@ msgstr "成功"
#: audits/models.py:33
#: audits/models.py:33
#: authentication/templates/authentication/_access_key_modal.html:22
#: authentication/templates/authentication/_access_key_modal.html:22
#: xpack/plugins/vault/templates/vault/vault.html:
46
#: xpack/plugins/vault/templates/vault/vault.html:
38
msgid "Create"
msgid "Create"
msgstr "创建"
msgstr "创建"
...
@@ -2306,9 +2304,9 @@ msgid "Reason"
...
@@ -2306,9 +2304,9 @@ msgid "Reason"
msgstr "原因"
msgstr "原因"
#: audits/models.py:88 audits/templates/audits/login_log_list.html:64
#: audits/models.py:88 audits/templates/audits/login_log_list.html:64
#: tickets/templates/tickets/login_confirm_ticket_list.html:16
#: tickets/templates/tickets/login_confirm_ticket_list.html:102
#: tickets/templates/tickets/ticket_detail.html:34
#: tickets/templates/tickets/ticket_detail.html:34
#: tickets/templates/tickets/ticket_list.html:24
#: tickets/templates/tickets/ticket_list.html:89
#: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
...
@@ -2368,7 +2366,7 @@ msgstr "ID"
...
@@ -2368,7 +2366,7 @@ msgstr "ID"
msgid "UA"
msgid "UA"
msgstr "Agent"
msgstr "Agent"
#: audits/templates/audits/login_log_list.html:61
#: audits/templates/audits/login_log_list.html:61
authentication/models.py:62
msgid "City"
msgid "City"
msgstr "城市"
msgstr "城市"
...
@@ -2379,23 +2377,23 @@ msgid "Date"
...
@@ -2379,23 +2377,23 @@ msgid "Date"
msgstr "日期"
msgstr "日期"
#: audits/views.py:86 audits/views.py:130 audits/views.py:167
#: audits/views.py:86 audits/views.py:130 audits/views.py:167
#: audits/views.py:212 audits/views.py:244 templates/_nav.html:13
9
#: audits/views.py:212 audits/views.py:244 templates/_nav.html:13
7
msgid "Audits"
msgid "Audits"
msgstr "日志审计"
msgstr "日志审计"
#: audits/views.py:87 templates/_nav.html:14
3
#: audits/views.py:87 templates/_nav.html:14
1
msgid "FTP log"
msgid "FTP log"
msgstr "FTP日志"
msgstr "FTP日志"
#: audits/views.py:131 templates/_nav.html:14
4
#: audits/views.py:131 templates/_nav.html:14
2
msgid "Operate log"
msgid "Operate log"
msgstr "操作日志"
msgstr "操作日志"
#: audits/views.py:168 templates/_nav.html:14
5
#: audits/views.py:168 templates/_nav.html:14
3
msgid "Password change log"
msgid "Password change log"
msgstr "改密日志"
msgstr "改密日志"
#: audits/views.py:213 templates/_nav.html:14
2
#: audits/views.py:213 templates/_nav.html:14
0
msgid "Login log"
msgid "Login log"
msgstr "登录日志"
msgstr "登录日志"
...
@@ -2530,22 +2528,6 @@ msgstr "ssh密钥"
...
@@ -2530,22 +2528,6 @@ msgstr "ssh密钥"
msgid "Reviewers"
msgid "Reviewers"
msgstr "审批人"
msgstr "审批人"
#: authentication/models.py:53
msgid "User login confirm: {}"
msgstr "用户登录复核: {}"
#: authentication/models.py:57
msgid ""
"User: {}\n"
"IP: {}\n"
"City: {}\n"
"Date: {}\n"
msgstr ""
"用户: {}\n"
"IP: {}\n"
"城市: {}\n"
"日期: {}\n"
#: authentication/templates/authentication/_access_key_modal.html:6
#: authentication/templates/authentication/_access_key_modal.html:6
msgid "API key list"
msgid "API key list"
msgstr "API Key列表"
msgstr "API Key列表"
...
@@ -4025,7 +4007,7 @@ msgstr "在ou:{}中没有匹配条目"
...
@@ -4025,7 +4007,7 @@ msgstr "在ou:{}中没有匹配条目"
#: settings/views.py:20 settings/views.py:47 settings/views.py:74
#: settings/views.py:20 settings/views.py:47 settings/views.py:74
#: settings/views.py:105 settings/views.py:133 settings/views.py:146
#: settings/views.py:105 settings/views.py:133 settings/views.py:146
#: settings/views.py:160 settings/views.py:187 templates/_nav.html:1
80
#: settings/views.py:160 settings/views.py:187 templates/_nav.html:1
78
msgid "Settings"
msgid "Settings"
msgstr "系统设置"
msgstr "系统设置"
...
@@ -4218,7 +4200,7 @@ msgstr "终端管理"
...
@@ -4218,7 +4200,7 @@ msgstr "终端管理"
msgid "Job Center"
msgid "Job Center"
msgstr "作业中心"
msgstr "作业中心"
#: templates/_nav.html:116 templates/_nav.html:14
6
#: templates/_nav.html:116 templates/_nav.html:14
4
msgid "Batch command"
msgid "Batch command"
msgstr "批量命令"
msgstr "批量命令"
...
@@ -4226,24 +4208,19 @@ msgstr "批量命令"
...
@@ -4226,24 +4208,19 @@ msgstr "批量命令"
msgid "Task monitor"
msgid "Task monitor"
msgstr "任务监控"
msgstr "任务监控"
#: templates/_nav.html:12
7 tickets/views.py:16 tickets/views.py:30
#: templates/_nav.html:12
8 tickets/views.py:17 tickets/views.py:32
msgid "Tickets"
msgid "Tickets"
msgstr "工单管理"
msgstr "工单管理"
#: templates/_nav.html:130 tickets/models/base.py:23
#: templates/_nav.html:154
#: users/templates/users/user_detail.html:257
msgid "Login confirm"
msgstr "登录复核"
#: templates/_nav.html:156
msgid "XPack"
msgid "XPack"
msgstr ""
msgstr ""
#: templates/_nav.html:16
4
xpack/plugins/cloud/views.py:28
#: templates/_nav.html:16
2
xpack/plugins/cloud/views.py:28
msgid "Account list"
msgid "Account list"
msgstr "账户列表"
msgstr "账户列表"
#: templates/_nav.html:16
5
#: templates/_nav.html:16
3
msgid "Sync instance"
msgid "Sync instance"
msgstr "同步实例"
msgstr "同步实例"
...
@@ -4585,10 +4562,7 @@ msgid "Accept"
...
@@ -4585,10 +4562,7 @@ msgid "Accept"
msgstr "接受"
msgstr "接受"
#: terminal/templates/terminal/terminal_list.html:80
#: terminal/templates/terminal/terminal_list.html:80
#: tickets/models/login_confirm.py:16
#: tickets/models/ticket.py:30 tickets/templates/tickets/ticket_list.html:95
#: tickets/templates/tickets/login_confirm_ticket_detail.html:10
#: tickets/templates/tickets/login_confirm_ticket_list.html:70
#: tickets/templates/tickets/login_confirm_ticket_list.html:108
msgid "Reject"
msgid "Reject"
msgstr "拒绝"
msgstr "拒绝"
...
@@ -4625,78 +4599,77 @@ msgid ""
...
@@ -4625,78 +4599,77 @@ msgid ""
"You should use your ssh client tools connect terminal: {} <br /> <br />{}"
"You should use your ssh client tools connect terminal: {} <br /> <br />{}"
msgstr "你可以使用ssh客户端工具连接终端"
msgstr "你可以使用ssh客户端工具连接终端"
#: tickets/models/
base.py:16 tickets/models/base.py:52
#: tickets/models/
ticket.py:17 tickets/models/ticket.py:69
#: tickets/templates/tickets/
login_confirm_ticket_list.html:103
#: tickets/templates/tickets/
ticket_list.html:90
msgid "Open"
msgid "Open"
msgstr "开启"
msgstr "开启"
#: tickets/models/base.py:17
#: tickets/models/ticket.py:18 tickets/templates/tickets/ticket_list.html:91
#: tickets/templates/tickets/login_confirm_ticket_list.html:104
msgid "Closed"
msgid "Closed"
msgstr "关闭"
msgstr "关闭"
#: tickets/models/
base.py:22
#: tickets/models/
ticket.py:23
msgid "General"
msgid "General"
msgstr "一般"
msgstr "一般"
#: tickets/models/base.py:26 tickets/models/base.py:69
#: tickets/models/ticket.py:24 users/templates/users/user_detail.html:257
msgid "Login confirm"
msgstr "登录复核"
#: tickets/models/ticket.py:29 tickets/templates/tickets/ticket_list.html:94
msgid "Approve"
msgstr "同意"
#: tickets/models/ticket.py:33 tickets/models/ticket.py:86
msgid "User display name"
msgid "User display name"
msgstr "用户显示名称"
msgstr "用户显示名称"
#: tickets/models/base.py:28
#: tickets/models/ticket.py:35 tickets/templates/tickets/ticket_list.html:21
#: tickets/templates/tickets/login_confirm_ticket_list.html:14
#: tickets/templates/tickets/ticket_list.html:87
#: tickets/templates/tickets/login_confirm_ticket_list.html:100
msgid "Title"
msgid "Title"
msgstr "标题"
msgstr "标题"
#: tickets/models/
base.py:29 tickets/models/base.py:70
#: tickets/models/
ticket.py:36 tickets/models/ticket.py:87
msgid "Body"
msgid "Body"
msgstr "内容"
msgstr "内容"
#: tickets/models/base.py:30 tickets/templates/tickets/ticket_detail.html:51
#: tickets/models/ticket.py:37
msgid "Meta"
msgstr ""
#: tickets/models/ticket.py:38 tickets/templates/tickets/ticket_detail.html:51
msgid "Assignee"
msgid "Assignee"
msgstr "处理人"
msgstr "处理人"
#: tickets/models/
base.py:31
#: tickets/models/
ticket.py:39
msgid "Assignee display name"
msgid "Assignee display name"
msgstr "处理人名称"
msgstr "处理人名称"
#: tickets/models/
base.py:32
tickets/templates/tickets/ticket_detail.html:50
#: tickets/models/
ticket.py:40
tickets/templates/tickets/ticket_detail.html:50
msgid "Assignees"
msgid "Assignees"
msgstr "待处理人"
msgstr "待处理人"
#: tickets/models/
base.py:33
#: tickets/models/
ticket.py:41
msgid "Assignees display name"
msgid "Assignees display name"
msgstr "待处理人名称"
msgstr "待处理人名称"
#: tickets/models/
base.py:53
#: tickets/models/
ticket.py:70
msgid "{} {} this ticket"
msgid "{} {} this ticket"
msgstr "{} {} 这个工单"
msgstr "{} {} 这个工单"
#: tickets/models/login_confirm.py:15
#: tickets/templates/tickets/login_confirm_ticket_detail.html:9
#: tickets/templates/tickets/login_confirm_ticket_list.html:69
#: tickets/templates/tickets/login_confirm_ticket_list.html:107
msgid "Approve"
msgstr "同意"
#: tickets/models/login_confirm.py:24
msgid "this order"
msgstr "这个工单"
#: tickets/templates/tickets/login_confirm_ticket_list.html:27
msgid "Approve selected"
msgstr "同意所选"
#: tickets/templates/tickets/login_confirm_ticket_list.html:28
msgid "Reject selected"
msgstr "拒绝所选"
#: tickets/templates/tickets/ticket_detail.html:66
#: tickets/templates/tickets/ticket_detail.html:66
#: tickets/templates/tickets/ticket_detail.html:81
#: tickets/templates/tickets/ticket_detail.html:81
msgid "ago"
msgid "ago"
msgstr "前"
msgstr "前"
#: tickets/templates/tickets/ticket_list.html:9
msgid "My tickets"
msgstr "我的工单"
#: tickets/templates/tickets/ticket_list.html:10
msgid "Assigned me"
msgstr "待处理"
#: tickets/utils.py:18
#: tickets/utils.py:18
msgid "New ticket"
msgid "New ticket"
msgstr "新工单"
msgstr "新工单"
...
@@ -4708,15 +4681,7 @@ msgid ""
...
@@ -4708,15 +4681,7 @@ msgid ""
" <div>\n"
" <div>\n"
" <p>Your has a new ticket</p>\n"
" <p>Your has a new ticket</p>\n"
" <div>\n"
" <div>\n"
" <b>Title:</b> {ticket.title}\n"
" {body}\n"
" <br/>\n"
" <b>User:</b> {user}\n"
" <br/>\n"
" <b>Assignees:</b> {ticket.assignees_display}\n"
" <br/>\n"
" <b>City:</b> {ticket.city}\n"
" <br/>\n"
" <b>IP:</b> {ticket.ip}\n"
" <br/>\n"
" <br/>\n"
" <a href={url}>click here to review</a> \n"
" <a href={url}>click here to review</a> \n"
" </div>\n"
" </div>\n"
...
@@ -4725,28 +4690,20 @@ msgid ""
...
@@ -4725,28 +4690,20 @@ msgid ""
msgstr ""
msgstr ""
"\n"
"\n"
" <div>\n"
" <div>\n"
" <p>
您
有一个新工单</p>\n"
" <p>
你
有一个新工单</p>\n"
" <div>\n"
" <div>\n"
" <b>标题:</b> {ticket.title}\n"
" {body}\n"
" <br/>\n"
" <b>用户:</b> {user}\n"
" <br/>\n"
" <b>待处理人:</b> {ticket.assignees_display}\n"
" <br/>\n"
" <b>城市:</b> {ticket.city}\n"
" <br/>\n"
" <b>IP:</b> {ticket.ip}\n"
" <br/>\n"
" <br/>\n"
" <a href={url}>点我查看</a> \n"
" <a href={url}>点
击
我查看</a> \n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" "
" "
#: tickets/utils.py:4
8
#: tickets/utils.py:4
0
msgid "Ticket has been reply"
msgid "Ticket has been reply"
msgstr "工单已被回复"
msgstr "工单已被回复"
#: tickets/utils.py:4
9
#: tickets/utils.py:4
1
#, python-brace-format
#, python-brace-format
msgid ""
msgid ""
"\n"
"\n"
...
@@ -4777,13 +4734,13 @@ msgstr ""
...
@@ -4777,13 +4734,13 @@ msgstr ""
" </div>\n"
" </div>\n"
" "
" "
#: tickets/views.py:1
7
#: tickets/views.py:1
8
msgid "
Login confirm t
icket list"
msgid "
T
icket list"
msgstr "
登录复核
工单列表"
msgstr "工单列表"
#: tickets/views.py:3
1
#: tickets/views.py:3
3
msgid "
Login confirm t
icket detail"
msgid "
T
icket detail"
msgstr "
登录复核
工单详情"
msgstr "工单详情"
#: users/api/user.py:173
#: users/api/user.py:173
msgid "Could not reset self otp, use profile reset instead"
msgid "Could not reset self otp, use profile reset instead"
...
@@ -6455,6 +6412,80 @@ msgstr "密码匣子"
...
@@ -6455,6 +6412,80 @@ msgstr "密码匣子"
msgid "vault create"
msgid "vault create"
msgstr "创建"
msgstr "创建"
#~ msgid "Assigned ticket"
#~ msgstr "处理人"
#~ msgid "My ticket"
#~ msgstr "我的工单"
#~ msgid "User login confirm: {}"
#~ msgstr "用户登录复核: {}"
#~ msgid ""
#~ "User: {}\n"
#~ "IP: {}\n"
#~ "City: {}\n"
#~ "Date: {}\n"
#~ msgstr ""
#~ "用户: {}\n"
#~ "IP: {}\n"
#~ "城市: {}\n"
#~ "日期: {}\n"
#~ msgid "this order"
#~ msgstr "这个工单"
#~ msgid "Approve selected"
#~ msgstr "同意所选"
#~ msgid "Reject selected"
#~ msgstr "拒绝所选"
#~ msgid ""
#~ "\n"
#~ " <div>\n"
#~ " <p>Your has a new ticket</p>\n"
#~ " <div>\n"
#~ " <b>Title:</b> {ticket.title}\n"
#~ " <br/>\n"
#~ " <b>User:</b> {user}\n"
#~ " <br/>\n"
#~ " <b>Assignees:</b> {ticket.assignees_display}\n"
#~ " <br/>\n"
#~ " <b>City:</b> {ticket.city}\n"
#~ " <br/>\n"
#~ " <b>IP:</b> {ticket.ip}\n"
#~ " <br/>\n"
#~ " <a href={url}>click here to review</a> \n"
#~ " </div>\n"
#~ " </div>\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " <div>\n"
#~ " <p>您有一个新工单</p>\n"
#~ " <div>\n"
#~ " <b>标题:</b> {ticket.title}\n"
#~ " <br/>\n"
#~ " <b>用户:</b> {user}\n"
#~ " <br/>\n"
#~ " <b>待处理人:</b> {ticket.assignees_display}\n"
#~ " <br/>\n"
#~ " <b>城市:</b> {ticket.city}\n"
#~ " <br/>\n"
#~ " <b>IP:</b> {ticket.ip}\n"
#~ " <br/>\n"
#~ " <a href={url}>点我查看</a> \n"
#~ " </div>\n"
#~ " </div>\n"
#~ " "
#~ msgid "Login confirm ticket list"
#~ msgstr "登录复核工单列表"
#~ msgid "Login confirm ticket detail"
#~ msgstr "登录复核工单详情"
#, fuzzy
#, fuzzy
#~| msgid "Login"
#~| msgid "Login"
#~ msgid "Login IP"
#~ msgid "Login IP"
...
...
apps/templates/_nav.html
View file @
ebe129b3
...
@@ -123,12 +123,10 @@
...
@@ -123,12 +123,10 @@
{% if request.user.can_admin_current_org and LOGIN_CONFIRM_ENABLE %}
{% if request.user.can_admin_current_org and LOGIN_CONFIRM_ENABLE %}
<li
id=
"tickets"
>
<li
id=
"tickets"
>
<a>
<a
href=
"{% url 'tickets:ticket-list' %}"
>
<i
class=
"fa fa-check-square-o"
style=
"width: 14px"
></i>
<span
class=
"nav-label"
>
{% trans 'Tickets' %}
</span><span
class=
"fa arrow"
></span>
<i
class=
"fa fa-check-square-o"
style=
"width: 14px"
></i>
<span
class=
"nav-label"
>
{% trans 'Tickets' %}
</span>
</a>
</a>
<ul
class=
"nav nav-second-level"
>
<li
id=
"login-confirm-tickets"
><a
href=
"{% url 'tickets:login-confirm-ticket-list' %}"
>
{% trans 'Login confirm' %}
</a></li>
</ul>
</li>
</li>
{% endif %}
{% endif %}
...
...
apps/tickets/api/__init__.py
View file @
ebe129b3
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
.base
import
*
from
.ticket
import
*
from
.login_confirm
import
*
apps/tickets/api/login_confirm.py
deleted
100644 → 0
View file @
18f88647
# -*- coding: utf-8 -*-
#
from
rest_framework_bulk
import
BulkModelViewSet
from
common.permissions
import
IsValidUser
from
common.mixins
import
CommonApiMixin
from
..
import
serializers
,
mixins
from
..models
import
LoginConfirmTicket
class
LoginConfirmTicketViewSet
(
CommonApiMixin
,
mixins
.
TicketMixin
,
BulkModelViewSet
):
serializer_class
=
serializers
.
LoginConfirmTicketSerializer
permission_classes
=
(
IsValidUser
,)
queryset
=
LoginConfirmTicket
.
objects
.
all
()
filter_fields
=
[
'status'
,
'title'
,
'action'
,
'ip'
]
search_fields
=
[
'user_display'
,
'title'
,
'ip'
,
'city'
]
apps/tickets/api/
base
.py
→
apps/tickets/api/
ticket
.py
View file @
ebe129b3
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
from
rest_framework
import
viewsets
from
rest_framework
import
viewsets
from
django.shortcuts
import
get_object_or_404
from
django.shortcuts
import
get_object_or_404
from
common.permissions
import
IsValidUser
from
common.utils
import
lazyproperty
from
common.utils
import
lazyproperty
from
..
import
serializers
,
models
,
mixins
from
..
import
serializers
,
models
,
mixins
...
@@ -11,14 +12,19 @@ from .. import serializers, models, mixins
...
@@ -11,14 +12,19 @@ from .. import serializers, models, mixins
class
TicketViewSet
(
mixins
.
TicketMixin
,
viewsets
.
ModelViewSet
):
class
TicketViewSet
(
mixins
.
TicketMixin
,
viewsets
.
ModelViewSet
):
serializer_class
=
serializers
.
TicketSerializer
serializer_class
=
serializers
.
TicketSerializer
queryset
=
models
.
Ticket
.
objects
.
all
()
queryset
=
models
.
Ticket
.
objects
.
all
()
permission_classes
=
(
IsValidUser
,)
filter_fields
=
[
'status'
,
'title'
,
'action'
]
search_fields
=
[
'user_display'
,
'title'
]
class
TicketCommentViewSet
(
viewsets
.
ModelViewSet
):
class
TicketCommentViewSet
(
viewsets
.
ModelViewSet
):
serializer_class
=
serializers
.
CommentSerializer
serializer_class
=
serializers
.
CommentSerializer
http_method_names
=
[
'get'
,
'post'
]
def
check_permissions
(
self
,
request
):
def
check_permissions
(
self
,
request
):
ticket
=
self
.
ticket
ticket
=
self
.
ticket
if
request
.
user
==
ticket
.
user
or
request
.
user
in
ticket
.
assignees
.
all
():
if
request
.
user
==
ticket
.
user
or
\
request
.
user
in
ticket
.
assignees
.
all
():
return
True
return
True
return
False
return
False
...
...
apps/tickets/migrations/0001_initial.py
View file @
ebe129b3
# Generated by Django 2.2.5 on 2019-11-
07 08:02
# Generated by Django 2.2.5 on 2019-11-
15 06:57
import
common.fields.model
from
django.conf
import
settings
from
django.conf
import
settings
from
django.db
import
migrations
,
models
from
django.db
import
migrations
,
models
import
django.db.models.deletion
import
django.db.models.deletion
...
@@ -25,10 +26,12 @@ class Migration(migrations.Migration):
...
@@ -25,10 +26,12 @@ class Migration(migrations.Migration):
(
'user_display'
,
models
.
CharField
(
max_length
=
128
,
verbose_name
=
'User display name'
)),
(
'user_display'
,
models
.
CharField
(
max_length
=
128
,
verbose_name
=
'User display name'
)),
(
'title'
,
models
.
CharField
(
max_length
=
256
,
verbose_name
=
'Title'
)),
(
'title'
,
models
.
CharField
(
max_length
=
256
,
verbose_name
=
'Title'
)),
(
'body'
,
models
.
TextField
(
verbose_name
=
'Body'
)),
(
'body'
,
models
.
TextField
(
verbose_name
=
'Body'
)),
(
'meta'
,
common
.
fields
.
model
.
JsonDictTextField
(
default
=
'{}'
,
verbose_name
=
'Meta'
)),
(
'assignee_display'
,
models
.
CharField
(
blank
=
True
,
max_length
=
128
,
null
=
True
,
verbose_name
=
'Assignee display name'
)),
(
'assignee_display'
,
models
.
CharField
(
blank
=
True
,
max_length
=
128
,
null
=
True
,
verbose_name
=
'Assignee display name'
)),
(
'assignees_display'
,
models
.
CharField
(
blank
=
True
,
max_length
=
128
,
verbose_name
=
'Assignees display name'
)),
(
'assignees_display'
,
models
.
CharField
(
blank
=
True
,
max_length
=
128
,
verbose_name
=
'Assignees display name'
)),
(
'type'
,
models
.
CharField
(
default
=
'general'
,
max_length
=
16
,
verbose_name
=
'Type'
)),
(
'type'
,
models
.
CharField
(
choices
=
[(
'general'
,
'General'
),
(
'login_confirm'
,
'Login confirm'
)],
default
=
'general'
,
max_length
=
16
,
verbose_name
=
'Type'
)),
(
'status'
,
models
.
CharField
(
choices
=
[(
'open'
,
'Open'
),
(
'closed'
,
'Closed'
)],
default
=
'open'
,
max_length
=
16
)),
(
'status'
,
models
.
CharField
(
choices
=
[(
'open'
,
'Open'
),
(
'closed'
,
'Closed'
)],
default
=
'open'
,
max_length
=
16
)),
(
'action'
,
models
.
CharField
(
blank
=
True
,
choices
=
[(
'approve'
,
'Approve'
),
(
'reject'
,
'Reject'
)],
default
=
''
,
max_length
=
16
)),
(
'assignee'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'ticket_handled'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'Assignee'
)),
(
'assignee'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'ticket_handled'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'Assignee'
)),
(
'assignees'
,
models
.
ManyToManyField
(
related_name
=
'ticket_assigned'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'Assignees'
)),
(
'assignees'
,
models
.
ManyToManyField
(
related_name
=
'ticket_assigned'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'Assignees'
)),
(
'user'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'ticket_requested'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'User'
)),
(
'user'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'ticket_requested'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'User'
)),
...
@@ -37,19 +40,6 @@ class Migration(migrations.Migration):
...
@@ -37,19 +40,6 @@ class Migration(migrations.Migration):
'ordering'
:
(
'-date_created'
,),
'ordering'
:
(
'-date_created'
,),
},
},
),
),
migrations
.
CreateModel
(
name
=
'LoginConfirmTicket'
,
fields
=
[
(
'ticket_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
parent_link
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tickets.Ticket'
)),
(
'ip'
,
models
.
GenericIPAddressField
(
blank
=
True
,
null
=
True
)),
(
'city'
,
models
.
CharField
(
blank
=
True
,
default
=
''
,
max_length
=
16
)),
(
'action'
,
models
.
CharField
(
blank
=
True
,
choices
=
[(
'approve'
,
'Approve'
),
(
'reject'
,
'Reject'
)],
default
=
''
,
max_length
=
16
)),
],
options
=
{
'abstract'
:
False
,
},
bases
=
(
'tickets.ticket'
,),
),
migrations
.
CreateModel
(
migrations
.
CreateModel
(
name
=
'Comment'
,
name
=
'Comment'
,
fields
=
[
fields
=
[
...
@@ -59,7 +49,7 @@ class Migration(migrations.Migration):
...
@@ -59,7 +49,7 @@ class Migration(migrations.Migration):
(
'date_updated'
,
models
.
DateTimeField
(
auto_now
=
True
,
verbose_name
=
'Date updated'
)),
(
'date_updated'
,
models
.
DateTimeField
(
auto_now
=
True
,
verbose_name
=
'Date updated'
)),
(
'user_display'
,
models
.
CharField
(
max_length
=
128
,
verbose_name
=
'User display name'
)),
(
'user_display'
,
models
.
CharField
(
max_length
=
128
,
verbose_name
=
'User display name'
)),
(
'body'
,
models
.
TextField
(
verbose_name
=
'Body'
)),
(
'body'
,
models
.
TextField
(
verbose_name
=
'Body'
)),
(
'ticket'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
to
=
'tickets.Ticket'
)),
(
'ticket'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
related_name
=
'comments'
,
to
=
'tickets.Ticket'
)),
(
'user'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'comments'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'User'
)),
(
'user'
,
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'comments'
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'User'
)),
],
],
options
=
{
options
=
{
...
...
apps/tickets/migrations/0002_auto_20191114_1105.py
deleted
100644 → 0
View file @
18f88647
# Generated by Django 2.2.5 on 2019-11-14 03:05
from
django.db
import
migrations
,
models
import
django.db.models.deletion
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'tickets'
,
'0001_initial'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'comment'
,
name
=
'ticket'
,
field
=
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
related_name
=
'comments'
,
to
=
'tickets.Ticket'
),
),
migrations
.
AlterField
(
model_name
=
'ticket'
,
name
=
'type'
,
field
=
models
.
CharField
(
choices
=
[(
'general'
,
'General'
),
(
'login_confirm'
,
'Login confirm'
)],
default
=
'general'
,
max_length
=
16
,
verbose_name
=
'Type'
),
),
]
apps/tickets/models/__init__.py
View file @
ebe129b3
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
.base
import
*
from
.ticket
import
*
from
.login_confirm
import
*
apps/tickets/models/login_confirm.py
deleted
100644 → 0
View file @
18f88647
# -*- coding: utf-8 -*-
#
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
.base
import
Ticket
__all__
=
[
'LoginConfirmTicket'
]
class
LoginConfirmTicket
(
Ticket
):
ACTION_APPROVE
=
'approve'
ACTION_REJECT
=
'reject'
ACTION_CHOICES
=
(
(
ACTION_APPROVE
,
_
(
'Approve'
)),
(
ACTION_REJECT
,
_
(
'Reject'
)),
)
ip
=
models
.
GenericIPAddressField
(
blank
=
True
,
null
=
True
)
city
=
models
.
CharField
(
max_length
=
16
,
blank
=
True
,
default
=
''
)
action
=
models
.
CharField
(
choices
=
ACTION_CHOICES
,
max_length
=
16
,
default
=
''
,
blank
=
True
)
def
create_action_comment
(
self
,
action
,
user
):
action_display
=
dict
(
self
.
ACTION_CHOICES
)
.
get
(
action
)
body
=
'{} {} {}'
.
format
(
user
,
action_display
,
_
(
"this order"
))
self
.
comments
.
create
(
body
=
body
,
user
=
user
,
user_display
=
str
(
user
))
def
perform_action
(
self
,
action
,
user
):
self
.
create_action_comment
(
action
,
user
)
self
.
action
=
action
self
.
status
=
self
.
STATUS_CLOSED
self
.
assignee
=
user
self
.
assignees_display
=
str
(
user
)
self
.
save
()
apps/tickets/models/
base
.py
→
apps/tickets/models/
ticket
.py
View file @
ebe129b3
...
@@ -5,6 +5,7 @@ from django.db import models
...
@@ -5,6 +5,7 @@ from django.db import models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
common.mixins.models
import
CommonModelMixin
from
common.mixins.models
import
CommonModelMixin
from
common.fields.model
import
JsonDictTextField
__all__
=
[
'Ticket'
,
'Comment'
]
__all__
=
[
'Ticket'
,
'Comment'
]
...
@@ -22,17 +23,25 @@ class Ticket(CommonModelMixin):
...
@@ -22,17 +23,25 @@ class Ticket(CommonModelMixin):
(
TYPE_GENERAL
,
_
(
"General"
)),
(
TYPE_GENERAL
,
_
(
"General"
)),
(
TYPE_LOGIN_CONFIRM
,
_
(
"Login confirm"
))
(
TYPE_LOGIN_CONFIRM
,
_
(
"Login confirm"
))
)
)
ACTION_APPROVE
=
'approve'
ACTION_REJECT
=
'reject'
ACTION_CHOICES
=
(
(
ACTION_APPROVE
,
_
(
'Approve'
)),
(
ACTION_REJECT
,
_
(
'Reject'
)),
)
user
=
models
.
ForeignKey
(
'users.User'
,
on_delete
=
models
.
SET_NULL
,
null
=
True
,
related_name
=
'
%(class)
s_requested'
,
verbose_name
=
_
(
"User"
))
user
=
models
.
ForeignKey
(
'users.User'
,
on_delete
=
models
.
SET_NULL
,
null
=
True
,
related_name
=
'
%(class)
s_requested'
,
verbose_name
=
_
(
"User"
))
user_display
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"User display name"
))
user_display
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"User display name"
))
title
=
models
.
CharField
(
max_length
=
256
,
verbose_name
=
_
(
"Title"
))
title
=
models
.
CharField
(
max_length
=
256
,
verbose_name
=
_
(
"Title"
))
body
=
models
.
TextField
(
verbose_name
=
_
(
"Body"
))
body
=
models
.
TextField
(
verbose_name
=
_
(
"Body"
))
meta
=
JsonDictTextField
(
verbose_name
=
_
(
"Meta"
),
default
=
'{}'
)
assignee
=
models
.
ForeignKey
(
'users.User'
,
on_delete
=
models
.
SET_NULL
,
null
=
True
,
related_name
=
'
%(class)
s_handled'
,
verbose_name
=
_
(
"Assignee"
))
assignee
=
models
.
ForeignKey
(
'users.User'
,
on_delete
=
models
.
SET_NULL
,
null
=
True
,
related_name
=
'
%(class)
s_handled'
,
verbose_name
=
_
(
"Assignee"
))
assignee_display
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
"Assignee display name"
))
assignee_display
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
"Assignee display name"
))
assignees
=
models
.
ManyToManyField
(
'users.User'
,
related_name
=
'
%(class)
s_assigned'
,
verbose_name
=
_
(
"Assignees"
))
assignees
=
models
.
ManyToManyField
(
'users.User'
,
related_name
=
'
%(class)
s_assigned'
,
verbose_name
=
_
(
"Assignees"
))
assignees_display
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Assignees display name"
),
blank
=
True
)
assignees_display
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Assignees display name"
),
blank
=
True
)
type
=
models
.
CharField
(
max_length
=
16
,
choices
=
TYPE_CHOICES
,
default
=
TYPE_GENERAL
,
verbose_name
=
_
(
"Type"
))
type
=
models
.
CharField
(
max_length
=
16
,
choices
=
TYPE_CHOICES
,
default
=
TYPE_GENERAL
,
verbose_name
=
_
(
"Type"
))
status
=
models
.
CharField
(
choices
=
STATUS_CHOICES
,
max_length
=
16
,
default
=
'open'
)
status
=
models
.
CharField
(
choices
=
STATUS_CHOICES
,
max_length
=
16
,
default
=
'open'
)
action
=
models
.
CharField
(
choices
=
ACTION_CHOICES
,
max_length
=
16
,
default
=
''
,
blank
=
True
)
def
__str__
(
self
):
def
__str__
(
self
):
return
'{}: {}'
.
format
(
self
.
user_display
,
self
.
title
)
return
'{}: {}'
.
format
(
self
.
user_display
,
self
.
title
)
...
@@ -45,6 +54,14 @@ class Ticket(CommonModelMixin):
...
@@ -45,6 +54,14 @@ class Ticket(CommonModelMixin):
def
status_display
(
self
):
def
status_display
(
self
):
return
self
.
get_status_display
()
return
self
.
get_status_display
()
@property
def
type_display
(
self
):
return
self
.
get_type_display
()
@property
def
action_display
(
self
):
return
self
.
get_action_display
()
def
create_status_comment
(
self
,
status
,
user
):
def
create_status_comment
(
self
,
status
,
user
):
if
status
==
self
.
STATUS_CLOSED
:
if
status
==
self
.
STATUS_CLOSED
:
action
=
_
(
"Close"
)
action
=
_
(
"Close"
)
...
@@ -59,6 +76,19 @@ class Ticket(CommonModelMixin):
...
@@ -59,6 +76,19 @@ class Ticket(CommonModelMixin):
self
.
status
=
status
self
.
status
=
status
self
.
save
()
self
.
save
()
def
create_action_comment
(
self
,
action
,
user
):
action_display
=
dict
(
self
.
ACTION_CHOICES
)
.
get
(
action
)
body
=
'{} {} {}'
.
format
(
user
,
action_display
,
_
(
"this order"
))
self
.
comments
.
create
(
body
=
body
,
user
=
user
,
user_display
=
str
(
user
))
def
perform_action
(
self
,
action
,
user
):
self
.
create_action_comment
(
action
,
user
)
self
.
action
=
action
self
.
status
=
self
.
STATUS_CLOSED
self
.
assignee
=
user
self
.
assignees_display
=
str
(
user
)
self
.
save
()
class
Meta
:
class
Meta
:
ordering
=
(
'-date_created'
,)
ordering
=
(
'-date_created'
,)
...
...
apps/tickets/serializers/__init__.py
View file @
ebe129b3
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
.base
import
*
from
.ticket
import
*
from
.login_confirm
import
*
apps/tickets/serializers/login_confirm.py
deleted
100644 → 0
View file @
18f88647
# -*- coding: utf-8 -*-
#
from
rest_framework
import
serializers
from
common.serializers
import
AdaptedBulkListSerializer
from
common.mixins.serializers
import
BulkSerializerMixin
from
.base
import
TicketSerializer
from
..models
import
LoginConfirmTicket
__all__
=
[
'LoginConfirmTicketSerializer'
,
'LoginConfirmTicketActionSerializer'
]
class
LoginConfirmTicketSerializer
(
BulkSerializerMixin
,
serializers
.
ModelSerializer
):
class
Meta
:
list_serializer_class
=
AdaptedBulkListSerializer
model
=
LoginConfirmTicket
fields
=
TicketSerializer
.
Meta
.
fields
+
[
'ip'
,
'city'
,
'action'
]
read_only_fields
=
TicketSerializer
.
Meta
.
read_only_fields
def
create
(
self
,
validated_data
):
validated_data
.
pop
(
'action'
)
return
super
()
.
create
(
validated_data
)
def
update
(
self
,
instance
,
validated_data
):
action
=
validated_data
.
get
(
"action"
)
user
=
self
.
context
[
"request"
]
.
user
if
action
and
user
not
in
instance
.
assignees
.
all
():
error
=
{
"action"
:
"Only assignees can update"
}
raise
serializers
.
ValidationError
(
error
)
if
instance
.
status
==
instance
.
STATUS_CLOSED
:
validated_data
.
pop
(
'action'
)
instance
=
super
()
.
update
(
instance
,
validated_data
)
if
not
instance
.
status
==
instance
.
STATUS_CLOSED
:
instance
.
perform_action
(
action
,
user
)
return
instance
class
LoginConfirmTicketActionSerializer
(
serializers
.
ModelSerializer
):
comment
=
serializers
.
CharField
(
allow_blank
=
True
)
class
Meta
:
model
=
LoginConfirmTicket
fields
=
[
'action'
]
def
update
(
self
,
instance
,
validated_data
):
pass
def
create
(
self
,
validated_data
):
pass
apps/tickets/serializers/
base
.py
→
apps/tickets/serializers/
ticket
.py
View file @
ebe129b3
...
@@ -14,12 +14,31 @@ class TicketSerializer(serializers.ModelSerializer):
...
@@ -14,12 +14,31 @@ class TicketSerializer(serializers.ModelSerializer):
'id'
,
'user'
,
'user_display'
,
'title'
,
'body'
,
'id'
,
'user'
,
'user_display'
,
'title'
,
'body'
,
'assignees'
,
'assignees_display'
,
'assignees'
,
'assignees_display'
,
'status'
,
'date_created'
,
'date_updated'
,
'status'
,
'date_created'
,
'date_updated'
,
'type_display'
,
'action_display'
,
]
]
read_only_fields
=
[
read_only_fields
=
[
'user_display'
,
'assignees_display'
,
'user_display'
,
'assignees_display'
,
'date_created'
,
'date_updated'
,
'date_created'
,
'date_updated'
,
]
]
def
create
(
self
,
validated_data
):
validated_data
.
pop
(
'action'
)
return
super
()
.
create
(
validated_data
)
def
update
(
self
,
instance
,
validated_data
):
action
=
validated_data
.
get
(
"action"
)
user
=
self
.
context
[
"request"
]
.
user
if
action
and
user
not
in
instance
.
assignees
.
all
():
error
=
{
"action"
:
"Only assignees can update"
}
raise
serializers
.
ValidationError
(
error
)
if
instance
.
status
==
instance
.
STATUS_CLOSED
:
validated_data
.
pop
(
'action'
)
instance
=
super
()
.
update
(
instance
,
validated_data
)
if
not
instance
.
status
==
instance
.
STATUS_CLOSED
and
action
:
instance
.
perform_action
(
action
,
user
)
return
instance
class
CurrentTicket
(
object
):
class
CurrentTicket
(
object
):
ticket
=
None
ticket
=
None
...
...
apps/tickets/signals_handler.py
View file @
ebe129b3
...
@@ -4,24 +4,24 @@ from django.dispatch import receiver
...
@@ -4,24 +4,24 @@ from django.dispatch import receiver
from
django.db.models.signals
import
m2m_changed
,
post_save
,
pre_save
from
django.db.models.signals
import
m2m_changed
,
post_save
,
pre_save
from
common.utils
import
get_logger
from
common.utils
import
get_logger
from
.models
import
LoginConfirmTicket
,
Ticket
,
Comment
from
.models
import
Ticket
,
Comment
from
.utils
import
(
from
.utils
import
(
send_
login_confirm
_ticket_mail_to_assignees
,
send_
new
_ticket_mail_to_assignees
,
send_
login_confirm
_action_mail_to_user
send_
ticket
_action_mail_to_user
)
)
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
@receiver
(
m2m_changed
,
sender
=
LoginConfirm
Ticket
.
assignees
.
through
)
@receiver
(
m2m_changed
,
sender
=
Ticket
.
assignees
.
through
)
def
on_login_confirm_ticket_assignees_set
(
sender
,
instance
=
None
,
action
=
None
,
def
on_login_confirm_ticket_assignees_set
(
sender
,
instance
=
None
,
action
=
None
,
reverse
=
False
,
model
=
None
,
reverse
=
False
,
model
=
None
,
pk_set
=
None
,
**
kwargs
):
pk_set
=
None
,
**
kwargs
):
if
action
==
'post_add'
:
if
action
==
'post_add'
:
logger
.
debug
(
'New ticket create, send mail: {}'
.
format
(
instance
.
id
))
logger
.
debug
(
'New ticket create, send mail: {}'
.
format
(
instance
.
id
))
assignees
=
model
.
objects
.
filter
(
pk__in
=
pk_set
)
assignees
=
model
.
objects
.
filter
(
pk__in
=
pk_set
)
send_
login_confirm
_ticket_mail_to_assignees
(
instance
,
assignees
)
send_
new
_ticket_mail_to_assignees
(
instance
,
assignees
)
if
action
.
startswith
(
'post'
)
and
not
reverse
:
if
action
.
startswith
(
'post'
)
and
not
reverse
:
instance
.
assignees_display
=
', '
.
join
([
instance
.
assignees_display
=
', '
.
join
([
str
(
u
)
for
u
in
instance
.
assignees
.
all
()
str
(
u
)
for
u
in
instance
.
assignees
.
all
()
...
@@ -29,15 +29,15 @@ def on_login_confirm_ticket_assignees_set(sender, instance=None, action=None,
...
@@ -29,15 +29,15 @@ def on_login_confirm_ticket_assignees_set(sender, instance=None, action=None,
instance
.
save
()
instance
.
save
()
@receiver
(
post_save
,
sender
=
LoginConfirm
Ticket
)
@receiver
(
post_save
,
sender
=
Ticket
)
def
on_login_confirm_ticket_status_change
(
sender
,
instance
=
None
,
created
=
False
,
**
kwargs
):
def
on_login_confirm_ticket_status_change
(
sender
,
instance
=
None
,
created
=
False
,
**
kwargs
):
if
created
or
instance
.
status
==
"open"
:
if
created
or
instance
.
status
==
"open"
:
return
return
logger
.
debug
(
'Ticket changed, send mail: {}'
.
format
(
instance
.
id
))
logger
.
debug
(
'Ticket changed, send mail: {}'
.
format
(
instance
.
id
))
send_
login_confirm
_action_mail_to_user
(
instance
)
send_
ticket
_action_mail_to_user
(
instance
)
@receiver
(
pre_save
,
sender
=
LoginConfirm
Ticket
)
@receiver
(
pre_save
,
sender
=
Ticket
)
def
on_ticket_create
(
sender
,
instance
=
None
,
**
kwargs
):
def
on_ticket_create
(
sender
,
instance
=
None
,
**
kwargs
):
instance
.
user_display
=
str
(
instance
.
user
)
instance
.
user_display
=
str
(
instance
.
user
)
if
instance
.
assignee
:
if
instance
.
assignee
:
...
...
apps/tickets/templates/tickets/login_confirm_ticket_detail.html
deleted
100644 → 0
View file @
18f88647
{% extends 'tickets/ticket_detail.html' %}
{% load static %}
{% load i18n %}
{% block status %}
{% endblock %}
{% block action %}
<a
class=
"btn btn-sm btn-primary btn-update btn-action"
data-action=
"approve"
><i
class=
"fa fa-check"
></i>
{% trans 'Approve' %}
</a>
<a
class=
"btn btn-sm btn-danger btn-update btn-action"
data-action=
"reject"
><i
class=
"fa fa-times"
></i>
{% trans 'Reject' %}
</a>
{% endblock %}
{% block custom_foot_js %}
{{ block.super }}
<script>
var
ticketDetailUrl
=
"{% url 'api-tickets:login-confirm-ticket-detail' pk=object.id %}"
;
$
(
document
).
ready
(
function
()
{
}).
on
(
'click'
,
'.btn-action'
,
function
()
{
createComment
(
function
()
{
});
var
action
=
$
(
this
).
data
(
'action'
);
var
data
=
{
url
:
ticketDetailUrl
,
body
:
JSON
.
stringify
({
action
:
action
}),
method
:
"PATCH"
,
success
:
reloadPage
};
requestApi
(
data
);
})
</script>
{% endblock %}
apps/tickets/templates/tickets/login_confirm_ticket_list.html
deleted
100644 → 0
View file @
18f88647
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block custom_head_css_js %}
{% endblock %}
{% block table_container %}
<table
class=
"table table-striped table-bordered table-hover "
id=
"login_confirm_ticket_list_table"
>
<thead>
<tr>
<th
class=
"text-center"
>
<input
id=
""
type=
"checkbox"
class=
"ipt_check_all"
>
</th>
<th
class=
"text-center"
>
{% trans 'Title' %}
</th>
<th
class=
"text-center"
>
{% trans 'User' %}
</th>
<th
class=
"text-center"
>
{% trans 'Status' %}
</th>
<th
class=
"text-center"
>
{% trans 'Datetime' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div
id=
"actions"
class=
"hide"
>
<div
class=
"input-group"
>
<select
class=
"form-control m-b"
style=
"width: auto"
id=
"slct_bulk_update"
>
<option
value=
"approve"
>
{% trans 'Approve selected' %}
</option>
<option
value=
"reject"
>
{% trans 'Reject selected' %}
</option>
</select>
<div
class=
"input-group-btn pull-left"
style=
"padding-left: 5px;"
>
<button
id=
'btn_bulk_update'
style=
"height: 32px;"
class=
"btn btn-sm btn-primary"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% include '_filter_dropdown.html' %}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
var
ticketTable
=
0
;
function
initTable
()
{
var
options
=
{
ele
:
$
(
'#login_confirm_ticket_list_table'
),
oSearch
:
{
sSearch
:
"status:open"
},
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
cellData
=
htmlEscape
(
cellData
);
var
detailBtn
=
'<a href="{% url "tickets:login-confirm-ticket-detail" pk=DEFAULT_PK %}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detailBtn
.
replace
(
"{{ DEFAULT_PK }}"
,
rowData
.
id
));
}},
{
targets
:
3
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
if
(
cellData
===
"approve"
)
{
$
(
td
).
html
(
'<i class="fa fa-check text-navy"></i>'
)
}
else
if
(
cellData
===
"reject"
)
{
$
(
td
).
html
(
'<i class="fa fa-times text-danger"></i>'
)
}
else
if
(
cellData
===
"open"
)
{
$
(
td
).
html
(
'<i class="fa fa-spinner text-info"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-circle text-info"></i>'
)
}
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
var
d
=
toSafeLocalDateStr
(
cellData
);
$
(
td
).
html
(
d
)
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
acceptBtn
=
'<a class="btn btn-xs btn-info btn-action" data-action="approve" data-uid="{{ DEFAULT_PK }}" >{% trans "Approve" %}</a> '
;
var
rejectBtn
=
'<a class="btn btn-xs btn-danger btn-action" data-action="reject" data-uid="{{ DEFAULT_PK }}" >{% trans "Reject" %}</a>'
;
acceptBtn
=
acceptBtn
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
rejectBtn
=
rejectBtn
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
var
acceptBtnRef
=
$
(
acceptBtn
);
var
rejectBtnRef
=
$
(
rejectBtn
);
if
(
rowData
.
action
!==
""
)
{
acceptBtnRef
.
attr
(
'disabled'
,
'disabled'
);
rejectBtnRef
.
attr
(
'disabled'
,
'disabled'
);
}
var
btn
=
acceptBtnRef
.
prop
(
'outerHTML'
)
+
' '
+
rejectBtnRef
.
prop
(
'outerHTML'
);
$
(
td
).
html
(
btn
)
}}],
ajax_url
:
'{% url "api-tickets:login-confirm-ticket-list" %}'
,
columns
:
[
{
data
:
"id"
},
{
data
:
"title"
},
{
data
:
"user_display"
},
{
data
:
"action"
,
width
:
"40px"
},
{
data
:
"date_created"
,
width
:
"120px"
},
{
data
:
"id"
,
orderable
:
false
}
],
op_html
:
$
(
'#actions'
).
html
()
};
ticketTable
=
jumpserver
.
initServerSideDataTable
(
options
);
return
ticketTable
}
$
(
document
).
ready
(
function
(){
initTable
();
var
menu
=
[
{
title
:
"IP"
,
value
:
"ip"
},
{
title
:
"{% trans 'Title' %}"
,
value
:
"title"
},
{
title
:
"{% trans 'User' %}"
,
value
:
"user_display"
},
{
title
:
"{% trans 'Status' %}"
,
value
:
"status"
,
submenu
:
[
{
title
:
"{% trans 'Open' %}"
,
value
:
"open"
},
{
title
:
"{% trans 'Closed' %}"
,
value
:
"closed"
},
]},
{
title
:
"{% trans 'Action' %}"
,
value
:
"action"
,
submenu
:
[
{
title
:
"{% trans 'Approve' %}"
,
value
:
"approve"
},
{
title
:
"{% trans 'Reject' %}"
,
value
:
"reject"
},
]},
];
initTableFilterDropdown
(
'#login_confirm_ticket_list_table_filter input'
,
menu
)
}).
on
(
'click'
,
'.btn-action'
,
function
()
{
var
ticketId
=
$
(
this
).
data
(
"uid"
);
var
action
=
$
(
this
).
data
(
'action'
);
var
ticketDetailUrl
=
"{% url 'api-tickets:login-confirm-ticket-detail' pk=DEFAULT_PK %}"
;
ticketDetailUrl
=
ticketDetailUrl
.
replace
(
"{{ DEFAULT_PK }}"
,
ticketId
);
var
data
=
{
url
:
ticketDetailUrl
,
body
:
JSON
.
stringify
({
action
:
action
}),
method
:
"PATCH"
,
success
:
reloadPage
};
requestApi
(
data
);
}).
on
(
'click'
,
'#btn_bulk_update'
,
function
()
{
var
action
=
$
(
'#slct_bulk_update'
).
val
();
var
idList
=
ticketTable
.
selected
;
if
(
idList
.
length
===
0
)
{
return
false
;
}
var
theUrl
=
"{% url 'api-tickets:login-confirm-ticket-list' %}"
;
function
doAction
(
action
)
{
var
data
=
[];
$
.
each
(
idList
,
function
(
index
,
object_id
)
{
var
obj
=
{
"pk"
:
object_id
,
"action"
:
action
};
data
.
push
(
obj
);
});
requestApi
({
url
:
theUrl
,
method
:
'PATCH'
,
body
:
JSON
.
stringify
(
data
),
success
:
function
(){
$
(
".ipt_check_all"
).
prop
(
"checked"
,
false
)
ticketTable
.
ajax
.
reload
();
}
});
}
doAction
(
action
)
})
</script>
{% endblock %}
apps/tickets/templates/tickets/ticket_detail.html
View file @
ebe129b3
...
@@ -72,7 +72,6 @@
...
@@ -72,7 +72,6 @@
</div>
</div>
</div>
</div>
{% for comment in object.comments.all %}
{% for comment in object.comments.all %}
<div
class=
"feed-element"
>
<div
class=
"feed-element"
>
<a
href=
"#"
class=
"pull-left"
>
<a
href=
"#"
class=
"pull-left"
>
<img
alt=
"image"
class=
"img-circle"
src=
"{% static 'img/avatar/user.png'%}"
>
<img
alt=
"image"
class=
"img-circle"
src=
"{% static 'img/avatar/user.png'%}"
>
...
@@ -97,14 +96,12 @@
...
@@ -97,14 +96,12 @@
</div>
</div>
</div>
</div>
<div
class=
"text-right"
>
<div
class=
"text-right"
>
{% block action %}
{% if object.type == object.TYPE_LOGIN_CONFIRM %}
{% endblock %}
<a
class=
"btn btn-sm btn-primary btn-update btn-action"
data-action=
"approve"
><i
class=
"fa fa-check"
></i>
{% trans 'Approve' %}
</a>
{% block status %}
<a
class=
"btn btn-sm btn-warning btn-update btn-action"
data-action=
"reject"
><i
class=
"fa fa-ban"
></i>
{% trans 'Reject' %}
</a>
<a
class=
"btn btn-sm btn-danger btn-update btn-status"
data-uid=
"close"
><i
class=
"fa fa-times"
></i>
{% trans 'Close' %}
</a>
{% endif %}
{% endblock %}
<a
class=
"btn btn-sm btn-danger btn-update btn-status"
data-uid=
"closed"
><i
class=
"fa fa-times"
></i>
{% trans 'Close' %}
</a>
{% block comment %}
<a
class=
"btn btn-sm btn-info btn-update btn-comment"
data-uid=
"comment"
><i
class=
"fa fa-pencil"
></i>
{% trans 'Comment' %}
</a>
<a
class=
"btn btn-sm btn-info btn-update btn-comment"
data-uid=
"comment"
><i
class=
"fa fa-pencil"
></i>
{% trans 'Comment' %}
</a>
{% endblock %}
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -127,6 +124,7 @@ var ticketId = "{{ object.id }}";
...
@@ -127,6 +124,7 @@ var ticketId = "{{ object.id }}";
var
status
=
"{{ object.status }}"
;
var
status
=
"{{ object.status }}"
;
var
commentUrl
=
"{% url 'api-tickets:ticket-comment-list' ticket_id=object.id %}"
;
var
commentUrl
=
"{% url 'api-tickets:ticket-comment-list' ticket_id=object.id %}"
;
var
ticketDetailUrl
=
"{% url 'api-tickets:ticket-detail' pk=object.id %}"
;
function
createComment
(
successCallback
)
{
function
createComment
(
successCallback
)
{
var
commentText
=
$
(
"#comment"
).
val
();
var
commentText
=
$
(
"#comment"
).
val
();
...
@@ -158,5 +156,26 @@ $(document).ready(function () {
...
@@ -158,5 +156,26 @@ $(document).ready(function () {
.
on
(
'click'
,
'.btn-comment'
,
function
()
{
.
on
(
'click'
,
'.btn-comment'
,
function
()
{
createComment
();
createComment
();
})
})
.
on
(
'click'
,
'.btn-action'
,
function
()
{
createComment
(
function
()
{});
var
action
=
$
(
this
).
data
(
'action'
);
var
data
=
{
url
:
ticketDetailUrl
,
body
:
JSON
.
stringify
({
action
:
action
}),
method
:
"PATCH"
,
success
:
reloadPage
};
requestApi
(
data
);
})
.
on
(
'click'
,
'.btn-status'
,
function
()
{
var
status
=
$
(
this
).
data
(
'uid'
);
var
data
=
{
url
:
ticketDetailUrl
,
body
:
JSON
.
stringify
({
status
:
status
}),
method
:
"PATCH"
,
success
:
reloadPage
};
requestApi
(
data
);
})
</script>
</script>
{% endblock %}
{% endblock %}
apps/tickets/templates/tickets/ticket_list.html
0 → 100644
View file @
ebe129b3
{% extends 'base.html' %}
{% load i18n static %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeIn"
>
<div
class=
"col-lg-12"
>
<div
class=
"tabs-container"
>
<ul
class=
"nav nav-tabs"
>
<li
{%
if
not
assign
%}
class=
"active"
{%
endif
%}
><a
href=
"{% url 'tickets:ticket-list' %}"
>
{% trans 'My tickets' %}
</a></li>
<li
{%
if
assign
%}
class=
"active"
{%
endif
%}
><a
href=
"{% url 'tickets:ticket-list' %}?assign=1"
>
{% trans 'Assigned me' %}
</a></li>
</ul>
<div
class=
"tab-content"
>
<div
id=
"my-tickets"
class=
"tab-pane active"
>
<div
class=
"panel-body"
>
<table
class=
"table table-striped table-bordered table-hover"
id=
"ticket-list-table"
>
<thead>
<tr>
<th
class=
"text-center"
>
<input
id=
""
type=
"checkbox"
class=
"ipt_check_all"
>
</th>
<th
class=
"text-center"
>
{% trans 'Title' %}
</th>
<th
class=
"text-center"
>
{% trans 'User' %}
</th>
<th
class=
"text-center"
>
{% trans 'Type' %}
</th>
<th
class=
"text-center"
>
{% trans 'Status' %}
</th>
<th
class=
"text-center"
>
{% trans 'Datetime' %}
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% include '_filter_dropdown.html' %}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
var
assignedTable
,
myTable
,
listUrl
;
{
%
if
assign
%
}
listUrl
=
'{% url "api-tickets:ticket-list" %}?assign=1'
;
{
%
else
%
}
listUrl
=
'{% url "api-tickets:ticket-list" %}?assign=0'
;
{
%
endif
%
}
function
initTable
()
{
var
options
=
{
ele
:
$
(
'#ticket-list-table'
),
oSearch
:
{
sSearch
:
"status:open"
},
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
cellData
=
htmlEscape
(
cellData
);
var
detailBtn
=
'<a href="{% url "tickets:ticket-detail" pk=DEFAULT_PK %}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detailBtn
.
replace
(
"{{ DEFAULT_PK }}"
,
rowData
.
id
));
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
cellData
===
"open"
)
{
$
(
td
).
html
(
'<i class="fa fa-check-circle-o text-navy"></i>'
);
}
else
{
$
(
td
).
html
(
'<i class="fa fa-times-circle-o text-danger"></i>'
)
}
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
var
d
=
toSafeLocalDateStr
(
cellData
);
$
(
td
).
html
(
d
)
}},
],
ajax_url
:
listUrl
,
columns
:
[
{
data
:
"id"
},
{
data
:
"title"
},
{
data
:
"user_display"
},
{
data
:
"type_display"
},
{
data
:
"status"
,
width
:
"40px"
},
{
data
:
"date_created"
},
],
op_html
:
$
(
'#actions'
).
html
()
};
myTable
=
jumpserver
.
initServerSideDataTable
(
options
);
return
myTable
}
$
(
document
).
ready
(
function
(){
initTable
();
var
menu
=
[
{
title
:
"IP"
,
value
:
"ip"
},
{
title
:
"{% trans 'Title' %}"
,
value
:
"title"
},
{
title
:
"{% trans 'User' %}"
,
value
:
"user_display"
},
{
title
:
"{% trans 'Status' %}"
,
value
:
"status"
,
submenu
:
[
{
title
:
"{% trans 'Open' %}"
,
value
:
"open"
},
{
title
:
"{% trans 'Closed' %}"
,
value
:
"closed"
},
]},
{
title
:
"{% trans 'Action' %}"
,
value
:
"action"
,
submenu
:
[
{
title
:
"{% trans 'Approve' %}"
,
value
:
"approve"
},
{
title
:
"{% trans 'Reject' %}"
,
value
:
"reject"
},
]},
];
initTableFilterDropdown
(
'#assigned-ticket-list-table input'
,
menu
)
}).
on
(
'click'
,
'.btn-action'
,
function
()
{
var
ticketId
=
$
(
this
).
data
(
"uid"
);
var
action
=
$
(
this
).
data
(
'action'
);
var
ticketDetailUrl
=
"{% url 'api-tickets:ticket-detail' pk=DEFAULT_PK %}"
;
ticketDetailUrl
=
ticketDetailUrl
.
replace
(
"{{ DEFAULT_PK }}"
,
ticketId
);
var
data
=
{
url
:
ticketDetailUrl
,
body
:
JSON
.
stringify
({
action
:
action
}),
method
:
"PATCH"
,
success
:
reloadPage
};
requestApi
(
data
);
})
</script>
{% endblock %}
apps/tickets/urls/api_urls.py
View file @
ebe129b3
...
@@ -9,7 +9,6 @@ router = BulkRouter()
...
@@ -9,7 +9,6 @@ router = BulkRouter()
router
.
register
(
'tickets'
,
api
.
TicketViewSet
,
'ticket'
)
router
.
register
(
'tickets'
,
api
.
TicketViewSet
,
'ticket'
)
router
.
register
(
'tickets/(?P<ticket_id>[0-9a-zA-Z
\
-]{36})/comments'
,
api
.
TicketCommentViewSet
,
'ticket-comment'
)
router
.
register
(
'tickets/(?P<ticket_id>[0-9a-zA-Z
\
-]{36})/comments'
,
api
.
TicketCommentViewSet
,
'ticket-comment'
)
router
.
register
(
'login-confirm-tickets'
,
api
.
LoginConfirmTicketViewSet
,
'login-confirm-ticket'
)
urlpatterns
=
[
urlpatterns
=
[
...
...
apps/tickets/urls/views_urls.py
View file @
ebe129b3
...
@@ -6,6 +6,6 @@ from .. import views
...
@@ -6,6 +6,6 @@ from .. import views
app_name
=
'tickets'
app_name
=
'tickets'
urlpatterns
=
[
urlpatterns
=
[
path
(
'
login-confirm-tickets/'
,
views
.
LoginConfirmTicketListView
.
as_view
(),
name
=
'login-confirm-
ticket-list'
),
path
(
'
tickets/'
,
views
.
TicketListView
.
as_view
(),
name
=
'
ticket-list'
),
path
(
'
login-confirm-tickets/<uuid:pk>/'
,
views
.
LoginConfirmTicketDetailView
.
as_view
(),
name
=
'login-confirm-ticket-detail'
)
path
(
'
tickets/<uuid:pk>/'
,
views
.
TicketDetailView
.
as_view
(),
name
=
'ticket-detail'
),
]
]
apps/tickets/utils.py
View file @
ebe129b3
...
@@ -9,37 +9,29 @@ from common.tasks import send_mail_async
...
@@ -9,37 +9,29 @@ from common.tasks import send_mail_async
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
def
send_
login_confirm
_ticket_mail_to_assignees
(
ticket
,
assignees
):
def
send_
new
_ticket_mail_to_assignees
(
ticket
,
assignees
):
recipient_list
=
[
user
.
email
for
user
in
assignees
]
recipient_list
=
[
user
.
email
for
user
in
assignees
]
user
=
ticket
.
user
user
=
ticket
.
user
if
not
recipient_list
:
if
not
recipient_list
:
logger
.
error
(
"Ticket not has assignees: {}"
.
format
(
ticket
.
id
))
logger
.
error
(
"Ticket not has assignees: {}"
.
format
(
ticket
.
id
))
return
return
subject
=
'{}: {}'
.
format
(
_
(
"New ticket"
),
ticket
.
title
)
subject
=
'{}: {}'
.
format
(
_
(
"New ticket"
),
ticket
.
title
)
detail_url
=
reverse
(
'tickets:
login-confirm-
ticket-detail'
,
detail_url
=
reverse
(
'tickets:ticket-detail'
,
kwargs
=
{
'pk'
:
ticket
.
id
},
external
=
True
)
kwargs
=
{
'pk'
:
ticket
.
id
},
external
=
True
)
message
=
_
(
"""
message
=
_
(
"""
<div>
<div>
<p>Your has a new ticket</p>
<p>Your has a new ticket</p>
<div>
<div>
<b>Title:</b> {ticket.title}
{body}
<br/>
<b>User:</b> {user}
<br/>
<b>Assignees:</b> {ticket.assignees_display}
<br/>
<b>City:</b> {ticket.city}
<br/>
<b>IP:</b> {ticket.ip}
<br/>
<br/>
<a href={url}>click here to review</a>
<a href={url}>click here to review</a>
</div>
</div>
</div>
</div>
"""
)
.
format
(
ticket
=
ticket
,
user
=
user
,
url
=
detail_url
)
"""
)
.
format
(
body
=
ticket
.
body
,
user
=
user
,
url
=
detail_url
)
send_mail_async
.
delay
(
subject
,
message
,
recipient_list
,
html_message
=
message
)
send_mail_async
.
delay
(
subject
,
message
,
recipient_list
,
html_message
=
message
)
def
send_
login_confirm
_action_mail_to_user
(
ticket
):
def
send_
ticket
_action_mail_to_user
(
ticket
):
if
not
ticket
.
user
:
if
not
ticket
.
user
:
logger
.
error
(
"Ticket not has user: {}"
.
format
(
ticket
.
id
))
logger
.
error
(
"Ticket not has user: {}"
.
format
(
ticket
.
id
))
return
return
...
...
apps/tickets/views.py
View file @
ebe129b3
...
@@ -2,32 +2,34 @@ from django.views.generic import TemplateView, DetailView
...
@@ -2,32 +2,34 @@ from django.views.generic import TemplateView, DetailView
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
common.permissions
import
PermissionsMixin
,
IsValidUser
from
common.permissions
import
PermissionsMixin
,
IsValidUser
from
.models
import
LoginConfirm
Ticket
from
.models
import
Ticket
from
.
import
mixins
from
.
import
mixins
class
LoginConfirm
TicketListView
(
PermissionsMixin
,
TemplateView
):
class
TicketListView
(
PermissionsMixin
,
TemplateView
):
template_name
=
'tickets/
login_confirm_
ticket_list.html'
template_name
=
'tickets/ticket_list.html'
permission_classes
=
(
IsValidUser
,)
permission_classes
=
(
IsValidUser
,)
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
assign
=
self
.
request
.
GET
.
get
(
'assign'
,
'0'
)
==
'1'
context
=
super
()
.
get_context_data
(
**
kwargs
)
context
=
super
()
.
get_context_data
(
**
kwargs
)
context
.
update
({
context
.
update
({
'app'
:
_
(
"Tickets"
),
'app'
:
_
(
"Tickets"
),
'action'
:
_
(
"Login confirm ticket list"
)
'action'
:
_
(
"Ticket list"
),
'assign'
:
assign
,
})
})
return
context
return
context
class
LoginConfirmTicketDetailView
(
PermissionsMixin
,
mixins
.
TicketMixin
,
DetailView
):
class
TicketDetailView
(
PermissionsMixin
,
mixins
.
TicketMixin
,
DetailView
):
template_name
=
'tickets/login_confirm_ticket_detail.html'
template_name
=
'tickets/ticket_detail.html'
queryset
=
LoginConfirmTicket
.
objects
.
all
()
permission_classes
=
(
IsValidUser
,)
permission_classes
=
(
IsValidUser
,)
queryset
=
Ticket
.
objects
.
all
()
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
()
.
get_context_data
(
**
kwargs
)
context
=
super
()
.
get_context_data
(
**
kwargs
)
context
.
update
({
context
.
update
({
'app'
:
_
(
"Tickets"
),
'app'
:
_
(
"Tickets"
),
'action'
:
_
(
"
Login confirm t
icket detail"
)
'action'
:
_
(
"
T
icket detail"
)
})
})
return
context
return
context
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment