mirror of https://github.com/jumpserver/jumpserver
feat: 支持企业微信、钉钉直接审批工单 (#8115)
parent
d23953932f
commit
c56179e9e4
|
@ -21,6 +21,7 @@ from authentication.mixins import AuthMixin
|
|||
from common.sdk.im.dingtalk import DingTalk
|
||||
from common.utils.common import get_request_ip
|
||||
from authentication.notifications import OAuthBindMessage
|
||||
from .mixins import METAMixin
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -200,14 +201,17 @@ class DingTalkEnableStartView(UserVerifyPasswordView):
|
|||
return success_url
|
||||
|
||||
|
||||
class DingTalkQRLoginView(DingTalkQRMixin, View):
|
||||
class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View):
|
||||
permission_classes = (AllowAny,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
redirect_url = request.GET.get('redirect_url')
|
||||
|
||||
redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True)
|
||||
redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
|
||||
redirect_uri += '?' + urlencode({
|
||||
'redirect_url': redirect_url,
|
||||
'next': self.get_next_url_from_meta()
|
||||
})
|
||||
|
||||
url = self.get_qr_url(redirect_uri)
|
||||
return HttpResponseRedirect(url)
|
||||
|
@ -305,4 +309,4 @@ class DingTalkOAuthLoginCallbackView(AuthMixin, DingTalkOAuthMixin, View):
|
|||
response = self.get_failed_response(login_url, title=msg, msg=msg)
|
||||
return response
|
||||
|
||||
return self.redirect_to_guard_view()
|
||||
return self.redirect_to_guard_view()
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
class METAMixin:
|
||||
def get_next_url_from_meta(self):
|
||||
request_meta = self.request.META or {}
|
||||
next_url = None
|
||||
referer = request_meta.get('HTTP_REFERER', '')
|
||||
next_url_item = referer.rsplit('next=', 1)
|
||||
if len(next_url_item) > 1:
|
||||
next_url = next_url_item[-1]
|
||||
return next_url
|
|
@ -21,6 +21,7 @@ from common.utils.common import get_request_ip
|
|||
from authentication import errors
|
||||
from authentication.mixins import AuthMixin
|
||||
from authentication.notifications import OAuthBindMessage
|
||||
from .mixins import METAMixin
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -196,14 +197,17 @@ class WeComEnableStartView(UserVerifyPasswordView):
|
|||
return success_url
|
||||
|
||||
|
||||
class WeComQRLoginView(WeComQRMixin, View):
|
||||
class WeComQRLoginView(WeComQRMixin, METAMixin, View):
|
||||
permission_classes = (AllowAny,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
redirect_url = request.GET.get('redirect_url')
|
||||
|
||||
redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True)
|
||||
redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
|
||||
redirect_uri += '?' + urlencode({
|
||||
'redirect_url': redirect_url,
|
||||
'next': self.get_next_url_from_meta()
|
||||
})
|
||||
|
||||
url = self.get_qr_url(redirect_uri)
|
||||
return HttpResponseRedirect(url)
|
||||
|
@ -301,4 +305,4 @@ class WeComOAuthLoginCallbackView(AuthMixin, WeComOAuthMixin, View):
|
|||
response = self.get_failed_response(login_url, title=msg, msg=msg)
|
||||
return response
|
||||
|
||||
return self.redirect_to_guard_view()
|
||||
return self.redirect_to_guard_view()
|
||||
|
|
|
@ -31,6 +31,7 @@ api_v1 = [
|
|||
app_view_patterns = [
|
||||
path('auth/', include('authentication.urls.view_urls'), name='auth'),
|
||||
path('ops/', include('ops.urls.view_urls'), name='ops'),
|
||||
path('tickets/', include('tickets.urls.view_urls'), name='tickets'),
|
||||
path('common/', include('common.urls.view_urls'), name='common'),
|
||||
re_path(r'flower/(?P<path>.*)', views.celery_flower_view, name='flower-view'),
|
||||
path('download/', views.ResourceDownload.as_view(), name='download'),
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e70a491494af861945bde8a0b03c9b6e78dde7016446236ead362362b76b09a8
|
||||
size 125713
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-29 12:49+0800\n"
|
||||
"POT-Creation-Date: 2022-05-05 13:03+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -1402,6 +1402,7 @@ msgid "Filename"
|
|||
msgstr "ファイル名"
|
||||
|
||||
#: audits/models.py:43 audits/models.py:115 terminal/models/sharing.py:90
|
||||
#: tickets/views/approve.py:93
|
||||
#: xpack/plugins/change_auth_plan/serializers/app.py:87
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:197
|
||||
msgid "Success"
|
||||
|
@ -1557,12 +1558,12 @@ msgid "Auth Token"
|
|||
msgstr "認証トークン"
|
||||
|
||||
#: audits/signal_handlers.py:71 authentication/notifications.py:73
|
||||
#: authentication/views/login.py:164 authentication/views/wecom.py:181
|
||||
#: authentication/views/login.py:164 authentication/views/wecom.py:182
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:720
|
||||
msgid "WeCom"
|
||||
msgstr "企業微信"
|
||||
|
||||
#: audits/signal_handlers.py:72 authentication/views/dingtalk.py:182
|
||||
#: audits/signal_handlers.py:72 authentication/views/dingtalk.py:183
|
||||
#: authentication/views/login.py:170 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:721
|
||||
msgid "DingTalk"
|
||||
|
@ -1740,7 +1741,7 @@ msgstr "{ApplicationPermission} 追加 {SystemUser}"
|
|||
msgid "{ApplicationPermission} REMOVE {SystemUser}"
|
||||
msgstr "{ApplicationPermission} 削除 {SystemUser}"
|
||||
|
||||
#: authentication/api/connection_token.py:328
|
||||
#: authentication/api/connection_token.py:326
|
||||
msgid "Invalid token"
|
||||
msgstr "無効なトークン"
|
||||
|
||||
|
@ -2184,6 +2185,7 @@ msgstr "コードエラー"
|
|||
#: jumpserver/conf.py:301 ops/tasks.py:145 ops/tasks.py:148
|
||||
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
||||
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
||||
#: tickets/templates/tickets/approve_check_password.html:23
|
||||
#: users/templates/users/_msg_account_expire_reminder.html:4
|
||||
#: users/templates/users/_msg_password_expire_reminder.html:4
|
||||
#: users/templates/users/_msg_reset_mfa.html:4
|
||||
|
@ -2323,54 +2325,54 @@ msgstr "返品"
|
|||
msgid "Copy success"
|
||||
msgstr "コピー成功"
|
||||
|
||||
#: authentication/views/dingtalk.py:39
|
||||
#: authentication/views/dingtalk.py:40
|
||||
msgid "DingTalk Error, Please contact your system administrator"
|
||||
msgstr "DingTalkエラー、システム管理者に連絡してください"
|
||||
|
||||
#: authentication/views/dingtalk.py:42
|
||||
#: authentication/views/dingtalk.py:43
|
||||
msgid "DingTalk Error"
|
||||
msgstr "DingTalkエラー"
|
||||
|
||||
#: authentication/views/dingtalk.py:54 authentication/views/feishu.py:50
|
||||
#: authentication/views/wecom.py:54
|
||||
#: authentication/views/dingtalk.py:55 authentication/views/feishu.py:50
|
||||
#: authentication/views/wecom.py:55
|
||||
msgid ""
|
||||
"The system configuration is incorrect. Please contact your administrator"
|
||||
msgstr "システム設定が正しくありません。管理者に連絡してください"
|
||||
|
||||
#: authentication/views/dingtalk.py:78
|
||||
#: authentication/views/dingtalk.py:79
|
||||
msgid "DingTalk is already bound"
|
||||
msgstr "DingTalkはすでにバインドされています"
|
||||
|
||||
#: authentication/views/dingtalk.py:127 authentication/views/feishu.py:99
|
||||
#: authentication/views/wecom.py:127
|
||||
#: authentication/views/dingtalk.py:128 authentication/views/feishu.py:99
|
||||
#: authentication/views/wecom.py:128
|
||||
msgid "Please verify your password first"
|
||||
msgstr "最初にパスワードを確認してください"
|
||||
|
||||
#: authentication/views/dingtalk.py:151 authentication/views/wecom.py:151
|
||||
#: authentication/views/dingtalk.py:152 authentication/views/wecom.py:152
|
||||
msgid "Invalid user_id"
|
||||
msgstr "無効なuser_id"
|
||||
|
||||
#: authentication/views/dingtalk.py:167
|
||||
#: authentication/views/dingtalk.py:168
|
||||
msgid "DingTalk query user failed"
|
||||
msgstr "DingTalkクエリユーザーが失敗しました"
|
||||
|
||||
#: authentication/views/dingtalk.py:176
|
||||
#: authentication/views/dingtalk.py:177
|
||||
msgid "The DingTalk is already bound to another user"
|
||||
msgstr "DingTalkはすでに別のユーザーにバインドされています"
|
||||
|
||||
#: authentication/views/dingtalk.py:183
|
||||
#: authentication/views/dingtalk.py:184
|
||||
msgid "Binding DingTalk successfully"
|
||||
msgstr "DingTalkのバインドに成功"
|
||||
|
||||
#: authentication/views/dingtalk.py:235 authentication/views/dingtalk.py:289
|
||||
#: authentication/views/dingtalk.py:239 authentication/views/dingtalk.py:293
|
||||
msgid "Failed to get user from DingTalk"
|
||||
msgstr "DingTalkからユーザーを取得できませんでした"
|
||||
|
||||
#: authentication/views/dingtalk.py:241 authentication/views/dingtalk.py:295
|
||||
#: authentication/views/dingtalk.py:245 authentication/views/dingtalk.py:299
|
||||
msgid "DingTalk is not bound"
|
||||
msgstr "DingTalkはバインドされていません"
|
||||
|
||||
#: authentication/views/dingtalk.py:242 authentication/views/dingtalk.py:296
|
||||
#: authentication/views/dingtalk.py:246 authentication/views/dingtalk.py:300
|
||||
msgid "Please login with a password and then bind the DingTalk"
|
||||
msgstr "パスワードでログインし、DingTalkをバインドしてください"
|
||||
|
||||
|
@ -2443,39 +2445,39 @@ msgstr "ログアウト成功"
|
|||
msgid "Logout success, return login page"
|
||||
msgstr "ログアウト成功、ログインページを返す"
|
||||
|
||||
#: authentication/views/wecom.py:39
|
||||
#: authentication/views/wecom.py:40
|
||||
msgid "WeCom Error, Please contact your system administrator"
|
||||
msgstr "企業微信エラー、システム管理者に連絡してください"
|
||||
|
||||
#: authentication/views/wecom.py:42
|
||||
#: authentication/views/wecom.py:43
|
||||
msgid "WeCom Error"
|
||||
msgstr "企業微信エラー"
|
||||
|
||||
#: authentication/views/wecom.py:78
|
||||
#: authentication/views/wecom.py:79
|
||||
msgid "WeCom is already bound"
|
||||
msgstr "企業の微信はすでにバインドされています"
|
||||
|
||||
#: authentication/views/wecom.py:166
|
||||
#: authentication/views/wecom.py:167
|
||||
msgid "WeCom query user failed"
|
||||
msgstr "企業微信ユーザーの問合せに失敗しました"
|
||||
|
||||
#: authentication/views/wecom.py:175
|
||||
#: authentication/views/wecom.py:176
|
||||
msgid "The WeCom is already bound to another user"
|
||||
msgstr "この企業の微信はすでに他のユーザーをバインドしている。"
|
||||
|
||||
#: authentication/views/wecom.py:182
|
||||
#: authentication/views/wecom.py:183
|
||||
msgid "Binding WeCom successfully"
|
||||
msgstr "企業の微信のバインドに成功"
|
||||
|
||||
#: authentication/views/wecom.py:231 authentication/views/wecom.py:285
|
||||
#: authentication/views/wecom.py:235 authentication/views/wecom.py:289
|
||||
msgid "Failed to get user from WeCom"
|
||||
msgstr "企業の微信からユーザーを取得できませんでした"
|
||||
|
||||
#: authentication/views/wecom.py:237 authentication/views/wecom.py:291
|
||||
#: authentication/views/wecom.py:241 authentication/views/wecom.py:295
|
||||
msgid "WeCom is not bound"
|
||||
msgstr "企業の微信をバインドしていません"
|
||||
|
||||
#: authentication/views/wecom.py:238 authentication/views/wecom.py:292
|
||||
#: authentication/views/wecom.py:242 authentication/views/wecom.py:296
|
||||
msgid "Please login with a password and then bind the WeCom"
|
||||
msgstr "パスワードでログインしてからWeComをバインドしてください"
|
||||
|
||||
|
@ -4564,7 +4566,7 @@ msgstr "フィルター"
|
|||
|
||||
#: terminal/api/endpoint.py:63
|
||||
msgid "Not found protocol query params"
|
||||
msgstr ""
|
||||
msgstr "プロトコルクエリパラメータが見つかりません"
|
||||
|
||||
#: terminal/api/session.py:210
|
||||
msgid "Session does not exist: {}"
|
||||
|
@ -5295,19 +5297,19 @@ msgstr "もう一度お試しください"
|
|||
msgid "Super ticket"
|
||||
msgstr "スーパーチケット"
|
||||
|
||||
#: tickets/notifications.py:63
|
||||
#: tickets/notifications.py:72
|
||||
msgid "Your has a new ticket, applicant - {}"
|
||||
msgstr "新しいチケットがあります- {}"
|
||||
|
||||
#: tickets/notifications.py:69
|
||||
#: tickets/notifications.py:78
|
||||
msgid "New Ticket - {} ({})"
|
||||
msgstr "新しいチケット- {} ({})"
|
||||
|
||||
#: tickets/notifications.py:91
|
||||
#: tickets/notifications.py:123
|
||||
msgid "Your ticket has been processed, processor - {}"
|
||||
msgstr "チケットが処理されました。プロセッサー- {}"
|
||||
|
||||
#: tickets/notifications.py:95
|
||||
#: tickets/notifications.py:127
|
||||
msgid "Ticket has processed - {} ({})"
|
||||
msgstr "チケットが処理済み- {} ({})"
|
||||
|
||||
|
@ -5444,10 +5446,55 @@ msgid "The current organization type already exists"
|
|||
msgstr "現在の組織タイプは既に存在します。"
|
||||
|
||||
#: tickets/templates/tickets/_base_ticket_body.html:17
|
||||
#: tickets/templates/tickets/_msg_ticket.html:12
|
||||
msgid "Click here to review"
|
||||
msgstr "こちらをクリックしてご覧ください"
|
||||
|
||||
#: tickets/templates/tickets/_msg_ticket.html:12
|
||||
msgid "View details"
|
||||
msgstr "詳細の表示"
|
||||
|
||||
#: tickets/templates/tickets/_msg_ticket.html:17
|
||||
msgid "Direct approval"
|
||||
msgstr "直接承認"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:11
|
||||
msgid "Ticket information"
|
||||
msgstr "作業指示情報"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:19
|
||||
#, fuzzy
|
||||
#| msgid "Ticket flow approval rule"
|
||||
msgid "Ticket approval"
|
||||
msgstr "作業指示の承認"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:35
|
||||
#: tickets/views/approve.py:25
|
||||
msgid "Ticket direct approval"
|
||||
msgstr "作業指示の直接承認"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:40
|
||||
msgid "Go Login"
|
||||
msgstr "ログイン"
|
||||
|
||||
#: tickets/views/approve.py:26
|
||||
msgid ""
|
||||
"This ticket does not exist, the process has ended, or this link has expired"
|
||||
msgstr ""
|
||||
"このワークシートが存在しないか、ワークシートが終了したか、このリンクが無効に"
|
||||
"なっています"
|
||||
|
||||
#: tickets/views/approve.py:55
|
||||
msgid "Click the button to approve directly"
|
||||
msgstr "ボタンをクリックすると、直接承認に成功します。"
|
||||
|
||||
#: tickets/views/approve.py:57
|
||||
msgid "After successful authentication, this ticket can be approved directly"
|
||||
msgstr "認証に成功した後、作業指示書は直接承認することができる。"
|
||||
|
||||
#: tickets/views/approve.py:84
|
||||
msgid "This user is not authorized to approve this ticket"
|
||||
msgstr "このユーザーはこの作業指示を承認する権限がありません"
|
||||
|
||||
#: users/api/user.py:183
|
||||
msgid "Could not reset self otp, use profile reset instead"
|
||||
msgstr "自己otpをリセットできませんでした、代わりにプロファイルリセットを使用"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:95e9f6addbdb6811647fd2bb5ae64bfc2572a80702c371eab0a1bb041a1e8476
|
||||
size 104032
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-29 12:49+0800\n"
|
||||
"POT-Creation-Date: 2022-05-05 13:03+0800\n"
|
||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -1390,6 +1390,7 @@ msgid "Filename"
|
|||
msgstr "文件名"
|
||||
|
||||
#: audits/models.py:43 audits/models.py:115 terminal/models/sharing.py:90
|
||||
#: tickets/views/approve.py:93
|
||||
#: xpack/plugins/change_auth_plan/serializers/app.py:87
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:197
|
||||
msgid "Success"
|
||||
|
@ -1545,12 +1546,12 @@ msgid "Auth Token"
|
|||
msgstr "认证令牌"
|
||||
|
||||
#: audits/signal_handlers.py:71 authentication/notifications.py:73
|
||||
#: authentication/views/login.py:164 authentication/views/wecom.py:181
|
||||
#: authentication/views/login.py:164 authentication/views/wecom.py:182
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:720
|
||||
msgid "WeCom"
|
||||
msgstr "企业微信"
|
||||
|
||||
#: audits/signal_handlers.py:72 authentication/views/dingtalk.py:182
|
||||
#: audits/signal_handlers.py:72 authentication/views/dingtalk.py:183
|
||||
#: authentication/views/login.py:170 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:721
|
||||
msgid "DingTalk"
|
||||
|
@ -1728,7 +1729,7 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}"
|
|||
msgid "{ApplicationPermission} REMOVE {SystemUser}"
|
||||
msgstr "{ApplicationPermission} 移除 {SystemUser}"
|
||||
|
||||
#: authentication/api/connection_token.py:328
|
||||
#: authentication/api/connection_token.py:326
|
||||
msgid "Invalid token"
|
||||
msgstr "无效的令牌"
|
||||
|
||||
|
@ -2163,6 +2164,7 @@ msgstr "代码错误"
|
|||
#: jumpserver/conf.py:301 ops/tasks.py:145 ops/tasks.py:148
|
||||
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
||||
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
||||
#: tickets/templates/tickets/approve_check_password.html:23
|
||||
#: users/templates/users/_msg_account_expire_reminder.html:4
|
||||
#: users/templates/users/_msg_password_expire_reminder.html:4
|
||||
#: users/templates/users/_msg_reset_mfa.html:4
|
||||
|
@ -2293,54 +2295,54 @@ msgstr "返回"
|
|||
msgid "Copy success"
|
||||
msgstr "复制成功"
|
||||
|
||||
#: authentication/views/dingtalk.py:39
|
||||
#: authentication/views/dingtalk.py:40
|
||||
msgid "DingTalk Error, Please contact your system administrator"
|
||||
msgstr "钉钉错误,请联系系统管理员"
|
||||
|
||||
#: authentication/views/dingtalk.py:42
|
||||
#: authentication/views/dingtalk.py:43
|
||||
msgid "DingTalk Error"
|
||||
msgstr "钉钉错误"
|
||||
|
||||
#: authentication/views/dingtalk.py:54 authentication/views/feishu.py:50
|
||||
#: authentication/views/wecom.py:54
|
||||
#: authentication/views/dingtalk.py:55 authentication/views/feishu.py:50
|
||||
#: authentication/views/wecom.py:55
|
||||
msgid ""
|
||||
"The system configuration is incorrect. Please contact your administrator"
|
||||
msgstr "企业配置错误,请联系系统管理员"
|
||||
|
||||
#: authentication/views/dingtalk.py:78
|
||||
#: authentication/views/dingtalk.py:79
|
||||
msgid "DingTalk is already bound"
|
||||
msgstr "钉钉已经绑定"
|
||||
|
||||
#: authentication/views/dingtalk.py:127 authentication/views/feishu.py:99
|
||||
#: authentication/views/wecom.py:127
|
||||
#: authentication/views/dingtalk.py:128 authentication/views/feishu.py:99
|
||||
#: authentication/views/wecom.py:128
|
||||
msgid "Please verify your password first"
|
||||
msgstr "请检查密码"
|
||||
|
||||
#: authentication/views/dingtalk.py:151 authentication/views/wecom.py:151
|
||||
#: authentication/views/dingtalk.py:152 authentication/views/wecom.py:152
|
||||
msgid "Invalid user_id"
|
||||
msgstr "无效的 user_id"
|
||||
|
||||
#: authentication/views/dingtalk.py:167
|
||||
#: authentication/views/dingtalk.py:168
|
||||
msgid "DingTalk query user failed"
|
||||
msgstr "钉钉查询用户失败"
|
||||
|
||||
#: authentication/views/dingtalk.py:176
|
||||
#: authentication/views/dingtalk.py:177
|
||||
msgid "The DingTalk is already bound to another user"
|
||||
msgstr "该钉钉已经绑定其他用户"
|
||||
|
||||
#: authentication/views/dingtalk.py:183
|
||||
#: authentication/views/dingtalk.py:184
|
||||
msgid "Binding DingTalk successfully"
|
||||
msgstr "绑定 钉钉 成功"
|
||||
|
||||
#: authentication/views/dingtalk.py:235 authentication/views/dingtalk.py:289
|
||||
#: authentication/views/dingtalk.py:239 authentication/views/dingtalk.py:293
|
||||
msgid "Failed to get user from DingTalk"
|
||||
msgstr "从钉钉获取用户失败"
|
||||
|
||||
#: authentication/views/dingtalk.py:241 authentication/views/dingtalk.py:295
|
||||
#: authentication/views/dingtalk.py:245 authentication/views/dingtalk.py:299
|
||||
msgid "DingTalk is not bound"
|
||||
msgstr "钉钉没有绑定"
|
||||
|
||||
#: authentication/views/dingtalk.py:242 authentication/views/dingtalk.py:296
|
||||
#: authentication/views/dingtalk.py:246 authentication/views/dingtalk.py:300
|
||||
msgid "Please login with a password and then bind the DingTalk"
|
||||
msgstr "请使用密码登录,然后绑定钉钉"
|
||||
|
||||
|
@ -2413,39 +2415,39 @@ msgstr "退出登录成功"
|
|||
msgid "Logout success, return login page"
|
||||
msgstr "退出登录成功,返回到登录页面"
|
||||
|
||||
#: authentication/views/wecom.py:39
|
||||
#: authentication/views/wecom.py:40
|
||||
msgid "WeCom Error, Please contact your system administrator"
|
||||
msgstr "企业微信错误,请联系系统管理员"
|
||||
|
||||
#: authentication/views/wecom.py:42
|
||||
#: authentication/views/wecom.py:43
|
||||
msgid "WeCom Error"
|
||||
msgstr "企业微信错误"
|
||||
|
||||
#: authentication/views/wecom.py:78
|
||||
#: authentication/views/wecom.py:79
|
||||
msgid "WeCom is already bound"
|
||||
msgstr "企业微信已经绑定"
|
||||
|
||||
#: authentication/views/wecom.py:166
|
||||
#: authentication/views/wecom.py:167
|
||||
msgid "WeCom query user failed"
|
||||
msgstr "企业微信查询用户失败"
|
||||
|
||||
#: authentication/views/wecom.py:175
|
||||
#: authentication/views/wecom.py:176
|
||||
msgid "The WeCom is already bound to another user"
|
||||
msgstr "该企业微信已经绑定其他用户"
|
||||
|
||||
#: authentication/views/wecom.py:182
|
||||
#: authentication/views/wecom.py:183
|
||||
msgid "Binding WeCom successfully"
|
||||
msgstr "绑定 企业微信 成功"
|
||||
|
||||
#: authentication/views/wecom.py:231 authentication/views/wecom.py:285
|
||||
#: authentication/views/wecom.py:235 authentication/views/wecom.py:289
|
||||
msgid "Failed to get user from WeCom"
|
||||
msgstr "从企业微信获取用户失败"
|
||||
|
||||
#: authentication/views/wecom.py:237 authentication/views/wecom.py:291
|
||||
#: authentication/views/wecom.py:241 authentication/views/wecom.py:295
|
||||
msgid "WeCom is not bound"
|
||||
msgstr "没有绑定企业微信"
|
||||
|
||||
#: authentication/views/wecom.py:238 authentication/views/wecom.py:292
|
||||
#: authentication/views/wecom.py:242 authentication/views/wecom.py:296
|
||||
msgid "Please login with a password and then bind the WeCom"
|
||||
msgstr "请使用密码登录,然后绑定企业微信"
|
||||
|
||||
|
@ -5221,19 +5223,19 @@ msgstr "请再次尝试"
|
|||
msgid "Super ticket"
|
||||
msgstr "超级工单"
|
||||
|
||||
#: tickets/notifications.py:63
|
||||
#: tickets/notifications.py:72
|
||||
msgid "Your has a new ticket, applicant - {}"
|
||||
msgstr "你有一个新的工单, 申请人 - {}"
|
||||
|
||||
#: tickets/notifications.py:69
|
||||
#: tickets/notifications.py:78
|
||||
msgid "New Ticket - {} ({})"
|
||||
msgstr "新工单 - {} ({})"
|
||||
|
||||
#: tickets/notifications.py:91
|
||||
#: tickets/notifications.py:123
|
||||
msgid "Your ticket has been processed, processor - {}"
|
||||
msgstr "你的工单已被处理, 处理人 - {}"
|
||||
|
||||
#: tickets/notifications.py:95
|
||||
#: tickets/notifications.py:127
|
||||
msgid "Ticket has processed - {} ({})"
|
||||
msgstr "你的工单已被处理, 处理人 - {} ({})"
|
||||
|
||||
|
@ -5368,10 +5370,51 @@ msgid "The current organization type already exists"
|
|||
msgstr "当前组织已存在该类型"
|
||||
|
||||
#: tickets/templates/tickets/_base_ticket_body.html:17
|
||||
#: tickets/templates/tickets/_msg_ticket.html:12
|
||||
msgid "Click here to review"
|
||||
msgstr "点击查看"
|
||||
|
||||
#: tickets/templates/tickets/_msg_ticket.html:12
|
||||
msgid "View details"
|
||||
msgstr "查看详情"
|
||||
|
||||
#: tickets/templates/tickets/_msg_ticket.html:17
|
||||
msgid "Direct approval"
|
||||
msgstr "直接批准"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:11
|
||||
msgid "Ticket information"
|
||||
msgstr "工单信息"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:19
|
||||
msgid "Ticket approval"
|
||||
msgstr "工单审批"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:35
|
||||
#: tickets/views/approve.py:25
|
||||
msgid "Ticket direct approval"
|
||||
msgstr "工单直接审批"
|
||||
|
||||
#: tickets/templates/tickets/approve_check_password.html:40
|
||||
msgid "Go Login"
|
||||
msgstr "去登录"
|
||||
|
||||
#: tickets/views/approve.py:26
|
||||
msgid ""
|
||||
"This ticket does not exist, the process has ended, or this link has expired"
|
||||
msgstr "工单不存在,或者工单流程已经结束,或者此链接已经过期"
|
||||
|
||||
#: tickets/views/approve.py:55
|
||||
msgid "Click the button to approve directly"
|
||||
msgstr "点击按钮,即可直接审批成功"
|
||||
|
||||
#: tickets/views/approve.py:57
|
||||
msgid "After successful authentication, this ticket can be approved directly"
|
||||
msgstr "认证成功后,工单可直接审批"
|
||||
|
||||
#: tickets/views/approve.py:84
|
||||
msgid "This user is not authorized to approve this ticket"
|
||||
msgstr "此用户无权审批此工单"
|
||||
|
||||
#: users/api/user.py:183
|
||||
msgid "Could not reset self otp, use profile reset instead"
|
||||
msgstr "不能在该页面重置 MFA 多因子认证, 请去个人信息页面重置"
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="{{ FAVICON_URL }}" type="image/x-icon">
|
||||
<title>{% block html_title %}{% endblock %}</title>
|
||||
|
||||
{% include '_head_css_js.html' %}
|
||||
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/jumpserver.js" %}"></script>
|
||||
<style>
|
||||
.outerBox {
|
||||
max-width: 80%;
|
||||
margin: 0 auto;
|
||||
padding: 100px 20px 20px 20px;
|
||||
}
|
||||
</style>
|
||||
{% block custom_head_css_js %} {% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="gray-bg">
|
||||
<div class="outerBox animated fadeInDown">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% block content %} {% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% include '_copyright.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
{% include '_foot_js.html' %}
|
||||
{% block custom_foot_js %} {% endblock %}
|
||||
</html>
|
|
@ -1,13 +1,15 @@
|
|||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.shortcuts import reverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from . import const
|
||||
from notifications.notifications import UserMessage
|
||||
from common.utils import get_logger
|
||||
from common.utils import get_logger, random_string
|
||||
from .models import Ticket
|
||||
from . import const
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -57,6 +59,13 @@ class TicketAppliedToAssignee(BaseTicketMessage):
|
|||
def __init__(self, user, ticket):
|
||||
self.ticket = ticket
|
||||
super().__init__(user)
|
||||
self._token = None
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
if self._token is None:
|
||||
self._token = random_string(32)
|
||||
return self._token
|
||||
|
||||
@property
|
||||
def content_title(self):
|
||||
|
@ -71,6 +80,29 @@ class TicketAppliedToAssignee(BaseTicketMessage):
|
|||
)
|
||||
return title
|
||||
|
||||
def get_ticket_approval_url(self):
|
||||
url = reverse('tickets:direct-approve', kwargs={'token': self.token})
|
||||
return urljoin(settings.SITE_URL, url)
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
body = self.ticket.body.replace('\n', '<br/>')
|
||||
context = dict(
|
||||
title=self.content_title,
|
||||
ticket_detail_url=self.ticket_detail_url,
|
||||
body=body,
|
||||
)
|
||||
|
||||
ticket_approval_url = self.get_ticket_approval_url()
|
||||
context.update({'ticket_approval_url': ticket_approval_url})
|
||||
message = render_to_string('tickets/_msg_ticket.html', context)
|
||||
cache.set(self.token, {
|
||||
'body': body, 'ticket_id': self.ticket.id
|
||||
}, 3600)
|
||||
return {
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def gen_test_msg(cls):
|
||||
from .models import Ticket
|
||||
|
|
|
@ -9,7 +9,13 @@
|
|||
<br>
|
||||
<div>
|
||||
<a href="{{ ticket_detail_url }}" target="_blank">
|
||||
{% trans 'Click here to review' %}
|
||||
{% trans 'View details' %}
|
||||
</a>
|
||||
<br>
|
||||
{% if ticket_approval_url %}
|
||||
<a href="{{ ticket_approval_url }}" target="_blank">
|
||||
{% trans 'Direct approval' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
{% extends '_base_double_screen.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<div class="col-lg-6">
|
||||
<div class="ibox-content">
|
||||
<h2 class="font-bold" style="display: inline">{% trans 'Ticket information' %}</h2>
|
||||
<h1></h1>
|
||||
<div style="word-break: break-all">{{ ticket_info | safe }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="ibox-content">
|
||||
<img src="{{ LOGO_URL }}" style="margin: auto" width="50" height="50">
|
||||
<h2 class="font-bold" style="display: inline">{% trans 'Ticket approval' %}</h2>
|
||||
<h1></h1>
|
||||
<div class="ibox-content">
|
||||
<p>
|
||||
{% trans 'Hello' %} {{ user.name }},
|
||||
</p>
|
||||
<p style="text-indent: 3em">
|
||||
{{ prompt_msg }}
|
||||
</p>
|
||||
<br>
|
||||
<form id="approve-form" action="" method="post" role="form" novalidate="novalidate">
|
||||
{% csrf_token %}
|
||||
<div class="form-group" style="">
|
||||
{% if user.is_authenticated %}
|
||||
<button id='approve_button' class="btn btn-primary block full-width m-b"
|
||||
type="submit">
|
||||
{% trans 'Ticket direct approval' %}
|
||||
</button>
|
||||
{% else %}
|
||||
<a id='login_button' class="btn btn-primary block full-width m-b"
|
||||
href="{{ login_url }}">
|
||||
{% trans 'Go Login' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
{% endblock %}
|
|
@ -0,0 +1,12 @@
|
|||
# coding:utf-8
|
||||
#
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from .. import views
|
||||
|
||||
app_name = 'tickets'
|
||||
|
||||
urlpatterns = [
|
||||
path('direct-approve/<str:token>/', views.TicketDirectApproveView.as_view(), name='direct-approve'),
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
from .approve import *
|
|
@ -0,0 +1,93 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.shortcuts import redirect, reverse
|
||||
from django.core.cache import cache
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from tickets.models import Ticket
|
||||
from tickets.errors import AlreadyClosed
|
||||
from common.utils import get_logger, FlashMessageUtil
|
||||
|
||||
logger = get_logger(__name__)
|
||||
__all__ = ['TicketDirectApproveView']
|
||||
|
||||
|
||||
class TicketDirectApproveView(TemplateView):
|
||||
template_name = 'tickets/approve_check_password.html'
|
||||
redirect_field_name = 'next'
|
||||
|
||||
@property
|
||||
def message_data(self):
|
||||
return {
|
||||
'title': _('Ticket direct approval'),
|
||||
'error': _("This ticket does not exist, "
|
||||
"the process has ended, or this link has expired"),
|
||||
'redirect_url': self.login_url,
|
||||
'auto_redirect': False
|
||||
}
|
||||
|
||||
@property
|
||||
def login_url(self):
|
||||
return reverse('authentication:login') + '?admin=1'
|
||||
|
||||
def redirect_message_response(self, **kwargs):
|
||||
message_data = self.message_data
|
||||
for key, value in kwargs.items():
|
||||
if isinstance(value, str):
|
||||
message_data[key] = value
|
||||
if message_data.get('message'):
|
||||
message_data.pop('error')
|
||||
redirect_url = FlashMessageUtil.gen_message_url(message_data)
|
||||
return redirect(redirect_url)
|
||||
|
||||
@staticmethod
|
||||
def clear(token):
|
||||
cache.delete(token)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# 放入工单信息
|
||||
token = kwargs.get('token')
|
||||
ticket_info = cache.get(token, {}).get('body', '')
|
||||
if self.request.user.is_authenticated:
|
||||
prompt_msg = _('Click the button to approve directly')
|
||||
else:
|
||||
prompt_msg = _('After successful authentication, this ticket can be approved directly')
|
||||
kwargs.update({
|
||||
'ticket_info': ticket_info, 'prompt_msg': prompt_msg,
|
||||
'login_url': '%s&next=%s' % (
|
||||
self.login_url,
|
||||
reverse('tickets:direct-approve', kwargs={'token': token})
|
||||
),
|
||||
})
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
token = kwargs.get('token')
|
||||
ticket_info = cache.get(token)
|
||||
if not ticket_info:
|
||||
return self.redirect_message_response(redirect_url=self.login_url)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, **kwargs):
|
||||
user = request.user
|
||||
token = kwargs.get('token')
|
||||
ticket_info = cache.get(token)
|
||||
if not ticket_info:
|
||||
return self.redirect_message_response(redirect_url=self.login_url)
|
||||
try:
|
||||
ticket_id = ticket_info.get('ticket_id')
|
||||
ticket = Ticket.all().get(id=ticket_id)
|
||||
if not ticket.has_current_assignee(user):
|
||||
raise Exception(_("This user is not authorized to approve this ticket"))
|
||||
ticket.approve(user)
|
||||
except AlreadyClosed as e:
|
||||
self.clear(token)
|
||||
return self.redirect_message_response(error=str(e), redirect_url=self.login_url)
|
||||
except Exception as e:
|
||||
return self.redirect_message_response(error=str(e), redirect_url=self.login_url)
|
||||
|
||||
self.clear(token)
|
||||
return self.redirect_message_response(message=_("Success"), redirect_url=self.login_url)
|
Loading…
Reference in New Issue