mirror of https://github.com/jumpserver/jumpserver
				
				
				
			[Update] Rename app
							parent
							
								
									edce831e46
								
							
						
					
					
						commit
						08775551c2
					
				| 
						 | 
					@ -11,7 +11,7 @@ from ..models import LoginConfirmSetting
 | 
				
			||||||
from ..serializers import LoginConfirmSettingSerializer
 | 
					from ..serializers import LoginConfirmSettingSerializer
 | 
				
			||||||
from .. import errors
 | 
					from .. import errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ['LoginConfirmSettingUpdateApi', 'UserOrderAcceptAuthApi']
 | 
					__all__ = ['LoginConfirmSettingUpdateApi', 'UserTicketAcceptAuthApi']
 | 
				
			||||||
logger = get_logger(__name__)
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,27 +30,42 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
 | 
				
			||||||
        return s
 | 
					        return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserOrderAcceptAuthApi(APIView):
 | 
					class UserTicketAcceptAuthApi(APIView):
 | 
				
			||||||
    permission_classes = ()
 | 
					    permission_classes = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, request, *args, **kwargs):
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
        from orders.models import LoginConfirmOrder
 | 
					        from tickets.models import LoginConfirmTicket
 | 
				
			||||||
        order_id = self.request.session.get("auth_order_id")
 | 
					        ticket_id = self.request.session.get("auth_ticket_id")
 | 
				
			||||||
        logger.debug('Login confirm order id: {}'.format(order_id))
 | 
					        logger.debug('Login confirm ticket id: {}'.format(ticket_id))
 | 
				
			||||||
        if not order_id:
 | 
					        if not ticket_id:
 | 
				
			||||||
            order = None
 | 
					            ticket = None
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            order = get_object_or_none(LoginConfirmOrder, pk=order_id)
 | 
					            ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if not order:
 | 
					            if not ticket:
 | 
				
			||||||
                raise errors.LoginConfirmOrderNotFound(order_id)
 | 
					                raise errors.LoginConfirmTicketNotFound(ticket_id)
 | 
				
			||||||
            if order.status == order.STATUS_ACCEPTED:
 | 
					            if ticket.action == LoginConfirmTicket.ACTION_APPROVE:
 | 
				
			||||||
                self.request.session["auth_confirm"] = "1"
 | 
					                self.request.session["auth_confirm"] = "1"
 | 
				
			||||||
                return Response({"msg": "ok"})
 | 
					                return Response({"msg": "ok"})
 | 
				
			||||||
            elif order.status == order.STATUS_REJECTED:
 | 
					            elif ticket.action == LoginConfirmTicket.ACTION_REJECT:
 | 
				
			||||||
                raise errors.LoginConfirmRejectedError(order_id)
 | 
					                raise errors.LoginConfirmRejectedError(ticket_id)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                raise errors.LoginConfirmWaitError(order_id)
 | 
					                raise errors.LoginConfirmWaitError(ticket_id)
 | 
				
			||||||
        except errors.AuthFailedError as e:
 | 
					        except errors.AuthFailedError as e:
 | 
				
			||||||
            data = e.as_data()
 | 
					            data = e.as_data()
 | 
				
			||||||
            return Response(data, status=400)
 | 
					            return Response(data, status=400)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserTicketCancelAuthApi(APIView):
 | 
				
			||||||
 | 
					    permission_classes = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        from tickets.models import LoginConfirmTicket
 | 
				
			||||||
 | 
					        ticket_id = self.request.session.get("auth_ticket_id")
 | 
				
			||||||
 | 
					        logger.debug('Login confirm ticket id: {}'.format(ticket_id))
 | 
				
			||||||
 | 
					        if not ticket_id:
 | 
				
			||||||
 | 
					            ticket = None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
 | 
				
			||||||
 | 
					        if not ticket:
 | 
				
			||||||
 | 
					            ticket.status = "close"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,9 +47,9 @@ mfa_failed_msg = _("MFA code invalid, or ntp sync server time")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mfa_required_msg = _("MFA required")
 | 
					mfa_required_msg = _("MFA required")
 | 
				
			||||||
login_confirm_required_msg = _("Login confirm required")
 | 
					login_confirm_required_msg = _("Login confirm required")
 | 
				
			||||||
login_confirm_wait_msg = _("Wait login confirm order for accept")
 | 
					login_confirm_wait_msg = _("Wait login confirm ticket for accept")
 | 
				
			||||||
login_confirm_rejected_msg = _("Login confirm order was rejected")
 | 
					login_confirm_rejected_msg = _("Login confirm ticket was rejected")
 | 
				
			||||||
login_confirm_order_not_found_msg = _("Order not found")
 | 
					login_confirm_ticket_not_found_msg = _("Ticket not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AuthFailedNeedLogMixin:
 | 
					class AuthFailedNeedLogMixin:
 | 
				
			||||||
| 
						 | 
					@ -155,8 +155,8 @@ class LoginConfirmError(AuthFailedError):
 | 
				
			||||||
    msg = login_confirm_wait_msg
 | 
					    msg = login_confirm_wait_msg
 | 
				
			||||||
    error = 'login_confirm_wait'
 | 
					    error = 'login_confirm_wait'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, order_id, **kwargs):
 | 
					    def __init__(self, ticket_id, **kwargs):
 | 
				
			||||||
        self.order_id = order_id
 | 
					        self.ticket_id = ticket_id
 | 
				
			||||||
        super().__init__(**kwargs)
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def as_data(self):
 | 
					    def as_data(self):
 | 
				
			||||||
| 
						 | 
					@ -164,7 +164,7 @@ class LoginConfirmError(AuthFailedError):
 | 
				
			||||||
            "error": self.error,
 | 
					            "error": self.error,
 | 
				
			||||||
            "msg": self.msg,
 | 
					            "msg": self.msg,
 | 
				
			||||||
            "data": {
 | 
					            "data": {
 | 
				
			||||||
                "order_id": self.order_id
 | 
					                "ticket_id": self.ticket_id
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -179,6 +179,6 @@ class LoginConfirmRejectedError(LoginConfirmError):
 | 
				
			||||||
    error = 'login_confirm_rejected'
 | 
					    error = 'login_confirm_rejected'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginConfirmOrderNotFound(LoginConfirmError):
 | 
					class LoginConfirmTicketNotFound(LoginConfirmError):
 | 
				
			||||||
    msg = login_confirm_order_not_found_msg
 | 
					    msg = login_confirm_ticket_not_found_msg
 | 
				
			||||||
    error = 'login_confirm_order_not_found'
 | 
					    error = 'login_confirm_ticket_not_found'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,30 +91,30 @@ class AuthMixin:
 | 
				
			||||||
        raise errors.MFAFailedError(username=user.username, request=self.request)
 | 
					        raise errors.MFAFailedError(username=user.username, request=self.request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def check_user_login_confirm_if_need(self, user):
 | 
					    def check_user_login_confirm_if_need(self, user):
 | 
				
			||||||
        from orders.models import LoginConfirmOrder
 | 
					        from tickets.models import LoginConfirmTicket
 | 
				
			||||||
        confirm_setting = user.get_login_confirm_setting()
 | 
					        confirm_setting = user.get_login_confirm_setting()
 | 
				
			||||||
        if self.request.session.get('auth_confirm') or not confirm_setting:
 | 
					        if self.request.session.get('auth_confirm') or not confirm_setting:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        order = None
 | 
					        ticket = None
 | 
				
			||||||
        if self.request.session.get('auth_order_id'):
 | 
					        if self.request.session.get('auth_ticket_id'):
 | 
				
			||||||
            order_id = self.request.session['auth_order_id']
 | 
					            ticket_id = self.request.session['auth_ticket_id']
 | 
				
			||||||
            order = get_object_or_none(LoginConfirmOrder, pk=order_id)
 | 
					            ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
 | 
				
			||||||
        if not order:
 | 
					        if not ticket:
 | 
				
			||||||
            order = confirm_setting.create_confirm_order(self.request)
 | 
					            ticket = confirm_setting.create_confirm_ticket(self.request)
 | 
				
			||||||
            self.request.session['auth_order_id'] = str(order.id)
 | 
					            self.request.session['auth_ticket_id'] = str(ticket.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if order.status == "accepted":
 | 
					        if ticket.status == "accepted":
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        elif order.status == "rejected":
 | 
					        elif ticket.status == "rejected":
 | 
				
			||||||
            raise errors.LoginConfirmRejectedError(order.id)
 | 
					            raise errors.LoginConfirmRejectedError(ticket.id)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise errors.LoginConfirmWaitError(order.id)
 | 
					            raise errors.LoginConfirmWaitError(ticket.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def clear_auth_mark(self):
 | 
					    def clear_auth_mark(self):
 | 
				
			||||||
        self.request.session['auth_password'] = ''
 | 
					        self.request.session['auth_password'] = ''
 | 
				
			||||||
        self.request.session['auth_mfa'] = ''
 | 
					        self.request.session['auth_mfa'] = ''
 | 
				
			||||||
        self.request.session['auth_confirm'] = ''
 | 
					        self.request.session['auth_confirm'] = ''
 | 
				
			||||||
        self.request.session['auth_order_id'] = ''
 | 
					        self.request.session['auth_ticket_id'] = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def send_auth_signal(self, success=True, user=None, username='', reason=''):
 | 
					    def send_auth_signal(self, success=True, user=None, username='', reason=''):
 | 
				
			||||||
        if success:
 | 
					        if success:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,8 +48,8 @@ class LoginConfirmSetting(CommonModelMixin):
 | 
				
			||||||
    def get_user_confirm_setting(cls, user):
 | 
					    def get_user_confirm_setting(cls, user):
 | 
				
			||||||
        return get_object_or_none(cls, user=user)
 | 
					        return get_object_or_none(cls, user=user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_confirm_order(self, request=None):
 | 
					    def create_confirm_ticket(self, request=None):
 | 
				
			||||||
        from orders.models import LoginConfirmOrder
 | 
					        from tickets.models import LoginConfirmTicket
 | 
				
			||||||
        title = _('User login confirm: {}').format(self.user)
 | 
					        title = _('User login confirm: {}').format(self.user)
 | 
				
			||||||
        if request:
 | 
					        if request:
 | 
				
			||||||
            remote_addr = get_request_ip(request)
 | 
					            remote_addr = get_request_ip(request)
 | 
				
			||||||
| 
						 | 
					@ -58,20 +58,20 @@ class LoginConfirmSetting(CommonModelMixin):
 | 
				
			||||||
                self.user, remote_addr, city, timezone.now()
 | 
					                self.user, remote_addr, city, timezone.now()
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            city = ''
 | 
					            city = 'Localhost'
 | 
				
			||||||
            remote_addr = ''
 | 
					            remote_addr = '127.0.0.1'
 | 
				
			||||||
            body = ''
 | 
					            body = ''
 | 
				
			||||||
        reviewer = self.reviewers.all()
 | 
					        reviewer = self.reviewers.all()
 | 
				
			||||||
        reviewer_names = ','.join([u.name for u in reviewer])
 | 
					        reviewer_names = ','.join([u.name for u in reviewer])
 | 
				
			||||||
        order = LoginConfirmOrder.objects.create(
 | 
					        ticket = LoginConfirmTicket.objects.create(
 | 
				
			||||||
            user=self.user, user_display=str(self.user),
 | 
					            user=self.user, user_display=str(self.user),
 | 
				
			||||||
            title=title, body=body,
 | 
					            title=title, body=body,
 | 
				
			||||||
            city=city, ip=remote_addr,
 | 
					            city=city, ip=remote_addr,
 | 
				
			||||||
            assignees_display=reviewer_names,
 | 
					            assignees_display=reviewer_names,
 | 
				
			||||||
            type=LoginConfirmOrder.TYPE_LOGIN_CONFIRM,
 | 
					            type=LoginConfirmTicket.TYPE_LOGIN_CONFIRM,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        order.assignees.set(reviewer)
 | 
					        ticket.assignees.set(reviewer)
 | 
				
			||||||
        return order
 | 
					        return ticket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return '{} confirm'.format(self.user.username)
 | 
					        return '{} confirm'.format(self.user.username)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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('order/auth/', api.UserOrderAcceptAuthApi.as_view(), name='user-order-auth'),
 | 
					    path('order/auth/', api.UserTicketAcceptAuthApi.as_view(), name='user-order-auth'),
 | 
				
			||||||
    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')
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,8 +126,8 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView):
 | 
				
			||||||
            return self.format_redirect_url(self.login_otp_url)
 | 
					            return self.format_redirect_url(self.login_otp_url)
 | 
				
			||||||
        confirm_setting = user.get_login_confirm_setting()
 | 
					        confirm_setting = user.get_login_confirm_setting()
 | 
				
			||||||
        if confirm_setting and not self.request.session.get('auth_confirm'):
 | 
					        if confirm_setting and not self.request.session.get('auth_confirm'):
 | 
				
			||||||
            order = confirm_setting.create_confirm_order(self.request)
 | 
					            ticket = confirm_setting.create_confirm_ticket(self.request)
 | 
				
			||||||
            self.request.session['auth_order_id'] = str(order.id)
 | 
					            self.request.session['auth_ticket_id'] = str(ticket.id)
 | 
				
			||||||
            url = self.format_redirect_url(self.login_confirm_url)
 | 
					            url = self.format_redirect_url(self.login_confirm_url)
 | 
				
			||||||
            return url
 | 
					            return url
 | 
				
			||||||
        self.login_success(user)
 | 
					        self.login_success(user)
 | 
				
			||||||
| 
						 | 
					@ -159,26 +159,26 @@ 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 orders.models import LoginConfirmOrder
 | 
					        from tickets.models import LoginConfirmTicket
 | 
				
			||||||
        order_id = self.request.session.get("auth_order_id")
 | 
					        ticket_id = self.request.session.get("auth_ticket_id")
 | 
				
			||||||
        if not order_id:
 | 
					        if not ticket_id:
 | 
				
			||||||
            order = None
 | 
					            ticket = None
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            order = get_object_or_none(LoginConfirmOrder, pk=order_id)
 | 
					            ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
 | 
				
			||||||
        context = super().get_context_data(**kwargs)
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
        if order:
 | 
					        if ticket:
 | 
				
			||||||
            order_detail_url = reverse('orders:login-confirm-order-detail', kwargs={'pk': order_id})
 | 
					            ticket_detail_url = reverse('tickets:login-confirm-ticket-detail', kwargs={'pk': ticket_id})
 | 
				
			||||||
            timestamp_created = datetime.datetime.timestamp(order.date_created)
 | 
					            timestamp_created = datetime.datetime.timestamp(ticket.date_created)
 | 
				
			||||||
            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(order.assignees_display)
 | 
					                  Don't close this page""").format(ticket.assignees_display)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            timestamp_created = 0
 | 
					            timestamp_created = 0
 | 
				
			||||||
            order_detail_url = ''
 | 
					            ticket_detail_url = ''
 | 
				
			||||||
            msg = _("No order found")
 | 
					            msg = _("No ticket found")
 | 
				
			||||||
        context.update({
 | 
					        context.update({
 | 
				
			||||||
            "msg": msg,
 | 
					            "msg": msg,
 | 
				
			||||||
            "timestamp": timestamp_created,
 | 
					            "timestamp": timestamp_created,
 | 
				
			||||||
            "order_detail_url": order_detail_url
 | 
					            "ticket_detail_url": ticket_detail_url
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ INSTALLED_APPS = [
 | 
				
			||||||
    'audits.apps.AuditsConfig',
 | 
					    'audits.apps.AuditsConfig',
 | 
				
			||||||
    'authentication.apps.AuthenticationConfig',  # authentication
 | 
					    'authentication.apps.AuthenticationConfig',  # authentication
 | 
				
			||||||
    'applications.apps.ApplicationsConfig',
 | 
					    'applications.apps.ApplicationsConfig',
 | 
				
			||||||
    'orders.apps.OrdersConfig',
 | 
					    'tickets.apps.TicketsConfig',
 | 
				
			||||||
    'rest_framework',
 | 
					    'rest_framework',
 | 
				
			||||||
    'rest_framework_swagger',
 | 
					    'rest_framework_swagger',
 | 
				
			||||||
    'drf_yasg',
 | 
					    'drf_yasg',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@ api_v1 = [
 | 
				
			||||||
    path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')),
 | 
					    path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')),
 | 
				
			||||||
    path('common/', include('common.urls.api_urls', namespace='api-common')),
 | 
					    path('common/', include('common.urls.api_urls', namespace='api-common')),
 | 
				
			||||||
    path('applications/', include('applications.urls.api_urls', namespace='api-applications')),
 | 
					    path('applications/', include('applications.urls.api_urls', namespace='api-applications')),
 | 
				
			||||||
    path('orders/', include('orders.urls.api_urls', namespace='api-orders')),
 | 
					    path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api_v2 = [
 | 
					api_v2 = [
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@ app_view_patterns = [
 | 
				
			||||||
    path('orgs/', include('orgs.urls.views_urls', namespace='orgs')),
 | 
					    path('orgs/', include('orgs.urls.views_urls', namespace='orgs')),
 | 
				
			||||||
    path('auth/', include('authentication.urls.view_urls'), name='auth'),
 | 
					    path('auth/', include('authentication.urls.view_urls'), name='auth'),
 | 
				
			||||||
    path('applications/', include('applications.urls.views_urls', namespace='applications')),
 | 
					    path('applications/', include('applications.urls.views_urls', namespace='applications')),
 | 
				
			||||||
    path('orders/', include('orders.urls.views_urls', namespace='orders')),
 | 
					    path('tickets/', include('tickets.urls.views_urls', namespace='tickets')),
 | 
				
			||||||
    re_path(r'flower/(?P<path>.*)', celery_flower_view, name='flower-view'),
 | 
					    re_path(r'flower/(?P<path>.*)', celery_flower_view, name='flower-view'),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from rest_framework import viewsets, generics
 | 
					 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from common.permissions import IsValidUser
 | 
					 | 
				
			||||||
from common.mixins import CommonApiMixin
 | 
					 | 
				
			||||||
from . import serializers
 | 
					 | 
				
			||||||
from .models import LoginConfirmOrder
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LoginConfirmOrderViewSet(CommonApiMixin, viewsets.ModelViewSet):
 | 
					 | 
				
			||||||
    serializer_class = serializers.LoginConfirmOrderSerializer
 | 
					 | 
				
			||||||
    permission_classes = (IsValidUser,)
 | 
					 | 
				
			||||||
    filter_fields = ['status', 'title']
 | 
					 | 
				
			||||||
    search_fields = ['user_display', 'title', 'ip', 'city']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_queryset(self):
 | 
					 | 
				
			||||||
        queryset = LoginConfirmOrder.objects.all()\
 | 
					 | 
				
			||||||
            .filter(assignees=self.request.user)
 | 
					 | 
				
			||||||
        return queryset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LoginConfirmOrderCreateActionApi(generics.CreateAPIView):
 | 
					 | 
				
			||||||
    permission_classes = (IsValidUser,)
 | 
					 | 
				
			||||||
    serializer_class = serializers.LoginConfirmOrderActionSerializer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_order(self):
 | 
					 | 
				
			||||||
        order_id = self.kwargs.get('pk')
 | 
					 | 
				
			||||||
        queryset = LoginConfirmOrder.objects.all()\
 | 
					 | 
				
			||||||
            .filter(assignees=self.request.user)
 | 
					 | 
				
			||||||
        order = get_object_or_404(queryset, id=order_id)
 | 
					 | 
				
			||||||
        return order
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_serializer_context(self):
 | 
					 | 
				
			||||||
        context = super().get_serializer_context()
 | 
					 | 
				
			||||||
        order = self.get_order()
 | 
					 | 
				
			||||||
        context['order'] = order
 | 
					 | 
				
			||||||
        return context
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,9 +0,0 @@
 | 
				
			||||||
from django.apps import AppConfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OrdersConfig(AppConfig):
 | 
					 | 
				
			||||||
    name = 'orders'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def ready(self):
 | 
					 | 
				
			||||||
        from . import signals_handler
 | 
					 | 
				
			||||||
        return super().ready()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,72 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from rest_framework import serializers
 | 
					 | 
				
			||||||
from django.utils.translation import ugettext_lazy as _
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .models import LoginConfirmOrder, Comment
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LoginConfirmOrderSerializer(serializers.ModelSerializer):
 | 
					 | 
				
			||||||
    class Meta:
 | 
					 | 
				
			||||||
        model = LoginConfirmOrder
 | 
					 | 
				
			||||||
        fields = [
 | 
					 | 
				
			||||||
            'id', 'user', 'user_display', 'title', 'body',
 | 
					 | 
				
			||||||
            'ip', 'city', 'assignees', 'assignees_display',
 | 
					 | 
				
			||||||
            'type', 'status', 'date_created', 'date_updated',
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LoginConfirmOrderActionSerializer(serializers.Serializer):
 | 
					 | 
				
			||||||
    ACTION_CHOICES = (
 | 
					 | 
				
			||||||
        ('accept', _('Accept')),
 | 
					 | 
				
			||||||
        ('reject', _('Reject')),
 | 
					 | 
				
			||||||
        ('comment', _('Comment'))
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    action = serializers.ChoiceField(choices=ACTION_CHOICES)
 | 
					 | 
				
			||||||
    comment = serializers.CharField(allow_blank=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def update(self, instance, validated_data):
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create_comments(self, order, user, validated_data):
 | 
					 | 
				
			||||||
        comment_data = validated_data.get('comment')
 | 
					 | 
				
			||||||
        action = validated_data.get('action')
 | 
					 | 
				
			||||||
        comments_data = []
 | 
					 | 
				
			||||||
        if comment_data:
 | 
					 | 
				
			||||||
            comments_data.append(comment_data)
 | 
					 | 
				
			||||||
            Comment.objects.create(
 | 
					 | 
				
			||||||
                order_id=order.id, body=comment_data, user=user,
 | 
					 | 
				
			||||||
                user_display=str(user)
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        if action != "comment":
 | 
					 | 
				
			||||||
            action_display = dict(self.ACTION_CHOICES).get(action)
 | 
					 | 
				
			||||||
            comment_data = '{} {} {}'.format(user, action_display, _("this order"))
 | 
					 | 
				
			||||||
            comments_data.append(comment_data)
 | 
					 | 
				
			||||||
        comments = [
 | 
					 | 
				
			||||||
            Comment(order_id=order.id, body=data, user=user, user_display=str(user))
 | 
					 | 
				
			||||||
            for data in comments_data
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        Comment.objects.bulk_create(comments)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def perform_action(order, user, validated_data):
 | 
					 | 
				
			||||||
        action = validated_data.get('action')
 | 
					 | 
				
			||||||
        if action == "accept":
 | 
					 | 
				
			||||||
            status = "accepted"
 | 
					 | 
				
			||||||
        elif action == "reject":
 | 
					 | 
				
			||||||
            status = "rejected"
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            status = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if status:
 | 
					 | 
				
			||||||
            order.status = status
 | 
					 | 
				
			||||||
            order.assignee = user
 | 
					 | 
				
			||||||
            order.assignee_display = str(user)
 | 
					 | 
				
			||||||
            order.save()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create(self, validated_data):
 | 
					 | 
				
			||||||
        order = self.context['order']
 | 
					 | 
				
			||||||
        user = self.context['request'].user
 | 
					 | 
				
			||||||
        self.create_comments(order, user, validated_data)
 | 
					 | 
				
			||||||
        self.perform_action(order, user, validated_data)
 | 
					 | 
				
			||||||
        return validated_data
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,31 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from django.dispatch import receiver
 | 
					 | 
				
			||||||
from django.db.models.signals import m2m_changed, post_save
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from common.utils import get_logger
 | 
					 | 
				
			||||||
from .models import LoginConfirmOrder
 | 
					 | 
				
			||||||
from .utils import (
 | 
					 | 
				
			||||||
    send_login_confirm_order_mail_to_assignees,
 | 
					 | 
				
			||||||
    send_login_confirm_action_mail_to_user
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
logger = get_logger(__name__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@receiver(m2m_changed, sender=LoginConfirmOrder.assignees.through)
 | 
					 | 
				
			||||||
def on_login_confirm_order_assignees_set(sender, instance=None, action=None,
 | 
					 | 
				
			||||||
                                        model=None, pk_set=None, **kwargs):
 | 
					 | 
				
			||||||
    if action == 'post_add':
 | 
					 | 
				
			||||||
        logger.debug('New order create, send mail: {}'.format(instance.id))
 | 
					 | 
				
			||||||
        assignees = model.objects.filter(pk__in=pk_set)
 | 
					 | 
				
			||||||
        send_login_confirm_order_mail_to_assignees(instance, assignees)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@receiver(post_save, sender=LoginConfirmOrder)
 | 
					 | 
				
			||||||
def on_login_confirm_order_status_change(sender, instance=None, created=False, **kwargs):
 | 
					 | 
				
			||||||
    if created or instance.status == "pending":
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    logger.debug('Order changed, send mail: {}'.format(instance.id))
 | 
					 | 
				
			||||||
    send_login_confirm_action_mail_to_user(instance)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,20 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from django.urls import path
 | 
					 | 
				
			||||||
from rest_framework.routers import DefaultRouter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .. import api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app_name = 'orders'
 | 
					 | 
				
			||||||
router = DefaultRouter()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
router.register('login-confirm-orders', api.LoginConfirmOrderViewSet, 'login-confirm-order')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
urlpatterns = [
 | 
					 | 
				
			||||||
    path('login-confirm-order/<uuid:pk>/actions/',
 | 
					 | 
				
			||||||
         api.LoginConfirmOrderCreateActionApi.as_view(),
 | 
					 | 
				
			||||||
         name='login-confirm-order-create-action'
 | 
					 | 
				
			||||||
         ),
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
urlpatterns += router.urls
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,11 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from django.urls import path
 | 
					 | 
				
			||||||
from .. import views
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app_name = 'orders'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
urlpatterns = [
 | 
					 | 
				
			||||||
    path('login-confirm-orders/', views.LoginConfirmOrderListView.as_view(), name='login-confirm-order-list'),
 | 
					 | 
				
			||||||
    path('login-confirm-orders/<uuid:pk>/', views.LoginConfirmOrderDetailView.as_view(), name='login-confirm-order-detail')
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,62 +0,0 @@
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					 | 
				
			||||||
from django.utils.translation import ugettext as _
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from common.utils import get_logger, reverse
 | 
					 | 
				
			||||||
from common.tasks import send_mail_async
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
logger = get_logger(__name__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def send_login_confirm_order_mail_to_assignees(order, assignees):
 | 
					 | 
				
			||||||
    recipient_list = [user.email for user in assignees]
 | 
					 | 
				
			||||||
    user = order.user
 | 
					 | 
				
			||||||
    if not recipient_list:
 | 
					 | 
				
			||||||
        logger.error("Order not has assignees: {}".format(order.id))
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    subject = '{}: {}'.format(_("New order"), order.title)
 | 
					 | 
				
			||||||
    detail_url = reverse('orders:login-confirm-order-detail',
 | 
					 | 
				
			||||||
                         kwargs={'pk': order.id}, external=True)
 | 
					 | 
				
			||||||
    message = _("""
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <p>Your has a new order</p>
 | 
					 | 
				
			||||||
            <div>
 | 
					 | 
				
			||||||
                <b>Title:</b> {order.title}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>User:</b> {user}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>Assignees:</b> {order.assignees_display}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>City:</b> {order.city}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>IP:</b> {order.ip}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <a href={url}>click here to review</a> 
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    """).format(order=order, user=user, url=detail_url)
 | 
					 | 
				
			||||||
    send_mail_async.delay(subject, message, recipient_list, html_message=message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def send_login_confirm_action_mail_to_user(order):
 | 
					 | 
				
			||||||
    if not order.user:
 | 
					 | 
				
			||||||
        logger.error("Order not has user: {}".format(order.id))
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    user = order.user
 | 
					 | 
				
			||||||
    recipient_list = [user.email]
 | 
					 | 
				
			||||||
    subject = '{}: {}'.format(_("Order has been reply"), order.title)
 | 
					 | 
				
			||||||
    message = _("""
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
            <p>Your order has been replay</p>
 | 
					 | 
				
			||||||
            <div>
 | 
					 | 
				
			||||||
                <b>Title:</b> {order.title}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>Assignee:</b> {order.assignee_display}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
                <b>Status:</b> {order.status_display}
 | 
					 | 
				
			||||||
                <br/>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
     """).format(order=order)
 | 
					 | 
				
			||||||
    send_mail_async.delay(subject, message, recipient_list, html_message=message)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -554,3 +554,7 @@ span.select2-selection__placeholder {
 | 
				
			||||||
    height: 22px;
 | 
					    height: 22px;
 | 
				
			||||||
    max-width: inherit;
 | 
					    max-width: inherit;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					table.table-striped.table-bordered {
 | 
				
			||||||
 | 
					    width: 100% !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,8 @@
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					    li.dropdown-submenu {
 | 
				
			||||||
 | 
					        width: 100%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
<ul class="dropdown-menu multi-level search-help" role="menu" aria-labelledby="dropdownMenu">
 | 
					<ul class="dropdown-menu multi-level search-help" role="menu" aria-labelledby="dropdownMenu">
 | 
				
			||||||
</ul>
 | 
					</ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -122,12 +122,12 @@
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% if request.user.can_admin_current_org and LOGIN_CONFIRM_ENABLE %}
 | 
					{% if request.user.can_admin_current_org and LOGIN_CONFIRM_ENABLE %}
 | 
				
			||||||
    <li id="orders">
 | 
					    <li id="tickets">
 | 
				
			||||||
        <a>
 | 
					        <a>
 | 
				
			||||||
            <i class="fa fa-check-square-o" style="width: 14px"></i> <span class="nav-label">{% trans 'Orders' %}</span><span class="fa arrow"></span>
 | 
					            <i class="fa fa-check-square-o" style="width: 14px"></i> <span class="nav-label">{% trans 'Tickets' %}</span><span class="fa arrow"></span>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
        <ul class="nav nav-second-level">
 | 
					        <ul class="nav nav-second-level">
 | 
				
			||||||
            <li id="login-confirm-orders"><a href="{% url 'orders:login-confirm-order-list' %}">{% trans 'Login confirm' %}</a></li>
 | 
					            <li id="login-confirm-orders"><a href="{% url 'tickets:login-confirm-ticket-list' %}">{% trans 'Login confirm' %}</a></li>
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
    </li>
 | 
					    </li>
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from .base import *
 | 
				
			||||||
 | 
					from .login_confirm import *
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from rest_framework import viewsets, generics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .. import serializers, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TicketViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    serializer_class = serializers.TicketSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        queryset = models.Ticket.objects.all().none()
 | 
				
			||||||
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommentViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    serializer_class = serializers.CommentSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        queryset = models.Comment.objects.none()
 | 
				
			||||||
 | 
					        return queryset
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from rest_framework import viewsets, generics
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from common.permissions import IsValidUser
 | 
				
			||||||
 | 
					from common.mixins import CommonApiMixin
 | 
				
			||||||
 | 
					from .. import serializers
 | 
				
			||||||
 | 
					from ..models import LoginConfirmTicket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginConfirmTicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    serializer_class = serializers.LoginConfirmTicketSerializer
 | 
				
			||||||
 | 
					    permission_classes = (IsValidUser,)
 | 
				
			||||||
 | 
					    filter_fields = ['status', 'title']
 | 
				
			||||||
 | 
					    search_fields = ['user_display', 'title', 'ip', 'city']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        queryset = LoginConfirmTicket.objects.all()\
 | 
				
			||||||
 | 
					            .filter(assignees=self.request.user)
 | 
				
			||||||
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginConfirmTicketsCreateActionApi(generics.CreateAPIView):
 | 
				
			||||||
 | 
					    permission_classes = (IsValidUser,)
 | 
				
			||||||
 | 
					    serializer_class = serializers.LoginConfirmTicketActionSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_ticket(self):
 | 
				
			||||||
 | 
					        ticket_id = self.kwargs.get('pk')
 | 
				
			||||||
 | 
					        queryset = LoginConfirmTicket.objects.all()\
 | 
				
			||||||
 | 
					            .filter(assignees=self.request.user)
 | 
				
			||||||
 | 
					        ticket = get_object_or_404(queryset, id=ticket_id)
 | 
				
			||||||
 | 
					        return ticket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer_context(self):
 | 
				
			||||||
 | 
					        context = super().get_serializer_context()
 | 
				
			||||||
 | 
					        ticket = self.get_ticket()
 | 
				
			||||||
 | 
					        context['ticket'] = ticket
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TicketsConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'tickets'
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# Generated by Django 2.2.5 on 2019-10-31 10:23
 | 
					# Generated by Django 2.2.5 on 2019-11-07 08:02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.db import migrations, models
 | 
					from django.db import migrations, models
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
        migrations.CreateModel(
 | 
					        migrations.CreateModel(
 | 
				
			||||||
            name='LoginConfirmOrder',
 | 
					            name='Ticket',
 | 
				
			||||||
            fields=[
 | 
					            fields=[
 | 
				
			||||||
                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
 | 
					                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
 | 
				
			||||||
                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
 | 
					                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
 | 
				
			||||||
| 
						 | 
					@ -27,18 +27,28 @@ class Migration(migrations.Migration):
 | 
				
			||||||
                ('body', models.TextField(verbose_name='Body')),
 | 
					                ('body', models.TextField(verbose_name='Body')),
 | 
				
			||||||
                ('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(choices=[('login_confirm', 'Login confirm')], max_length=16, verbose_name='Type')),
 | 
					                ('type', models.CharField(default='general', max_length=16, verbose_name='Type')),
 | 
				
			||||||
                ('status', models.CharField(choices=[('accepted', 'Accepted'), ('rejected', 'Rejected'), ('pending', 'Pending')], default='pending', max_length=16)),
 | 
					                ('status', models.CharField(choices=[('open', 'Open'), ('closed', 'Closed')], default='open', max_length=16)),
 | 
				
			||||||
                ('ip', models.GenericIPAddressField(blank=True, null=True)),
 | 
					                ('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')),
 | 
				
			||||||
                ('city', models.CharField(blank=True, default='', max_length=16)),
 | 
					                ('assignees', models.ManyToManyField(related_name='ticket_assigned', to=settings.AUTH_USER_MODEL, verbose_name='Assignees')),
 | 
				
			||||||
                ('assignee', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='loginconfirmorder_handled', to=settings.AUTH_USER_MODEL, verbose_name='Assignee')),
 | 
					                ('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')),
 | 
				
			||||||
                ('assignees', models.ManyToManyField(related_name='loginconfirmorder_assigned', to=settings.AUTH_USER_MODEL, verbose_name='Assignees')),
 | 
					 | 
				
			||||||
                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='loginconfirmorder_requested', to=settings.AUTH_USER_MODEL, verbose_name='User')),
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            options={
 | 
					            options={
 | 
				
			||||||
                '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,
 | 
					                'abstract': False,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            bases=('tickets.ticket',),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        migrations.CreateModel(
 | 
					        migrations.CreateModel(
 | 
				
			||||||
            name='Comment',
 | 
					            name='Comment',
 | 
				
			||||||
| 
						 | 
					@ -47,9 +57,9 @@ class Migration(migrations.Migration):
 | 
				
			||||||
                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
 | 
					                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
 | 
				
			||||||
                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
 | 
					                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
 | 
				
			||||||
                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
 | 
					                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
 | 
				
			||||||
                ('order_id', models.UUIDField()),
 | 
					 | 
				
			||||||
                ('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')),
 | 
				
			||||||
                ('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={
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from .base import *
 | 
				
			||||||
 | 
					from .login_confirm import *
 | 
				
			||||||
| 
						 | 
					@ -1,33 +1,26 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ['LoginConfirmOrder', 'Comment']
 | 
					__all__ = ['Ticket', 'Comment']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Comment(CommonModelMixin):
 | 
					class Ticket(CommonModelMixin):
 | 
				
			||||||
    order_id = models.UUIDField()
 | 
					    STATUS_OPEN = 'open'
 | 
				
			||||||
    user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, verbose_name=_("User"), related_name='comments')
 | 
					    STATUS_CLOSED = 'closed'
 | 
				
			||||||
    user_display = models.CharField(max_length=128, verbose_name=_("User display name"))
 | 
					 | 
				
			||||||
    body = models.TextField(verbose_name=_("Body"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					 | 
				
			||||||
        ordering = ('date_created', )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class BaseOrder(CommonModelMixin):
 | 
					 | 
				
			||||||
    STATUS_ACCEPTED = 'accepted'
 | 
					 | 
				
			||||||
    STATUS_REJECTED = 'rejected'
 | 
					 | 
				
			||||||
    STATUS_PENDING = 'pending'
 | 
					 | 
				
			||||||
    STATUS_CHOICES = (
 | 
					    STATUS_CHOICES = (
 | 
				
			||||||
        (STATUS_ACCEPTED, _("Accepted")),
 | 
					        (STATUS_OPEN, _("Open")),
 | 
				
			||||||
        (STATUS_REJECTED, _("Rejected")),
 | 
					        (STATUS_CLOSED, _("Closed"))
 | 
				
			||||||
        (STATUS_PENDING, _("Pending"))
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    TYPE_GENERAL = 'general'
 | 
				
			||||||
    TYPE_LOGIN_CONFIRM = 'login_confirm'
 | 
					    TYPE_LOGIN_CONFIRM = 'login_confirm'
 | 
				
			||||||
    TYPE_CHOICES = (
 | 
					    TYPE_CHOICES = (
 | 
				
			||||||
        (TYPE_LOGIN_CONFIRM, 'Login confirm'),
 | 
					        (TYPE_GENERAL, _("General")),
 | 
				
			||||||
 | 
					        (TYPE_LOGIN_CONFIRM, _("Login confirm"))
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    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"))
 | 
				
			||||||
| 
						 | 
					@ -38,8 +31,8 @@ class BaseOrder(CommonModelMixin):
 | 
				
			||||||
    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(choices=TYPE_CHOICES, max_length=16, verbose_name=_('Type'))
 | 
					    type = models.CharField(max_length=16, default='general', verbose_name=_("Type"))
 | 
				
			||||||
    status = models.CharField(choices=STATUS_CHOICES, max_length=16, default='pending')
 | 
					    status = models.CharField(choices=STATUS_CHOICES, max_length=16, default='open')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return '{}: {}'.format(self.user_display, self.title)
 | 
					        return '{}: {}'.format(self.user_display, self.title)
 | 
				
			||||||
| 
						 | 
					@ -57,10 +50,16 @@ class BaseOrder(CommonModelMixin):
 | 
				
			||||||
        return self.get_status_display()
 | 
					        return self.get_status_display()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        abstract = True
 | 
					 | 
				
			||||||
        ordering = ('-date_created',)
 | 
					        ordering = ('-date_created',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginConfirmOrder(BaseOrder):
 | 
					class Comment(CommonModelMixin):
 | 
				
			||||||
    ip = models.GenericIPAddressField(blank=True, null=True)
 | 
					    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
 | 
				
			||||||
    city = models.CharField(max_length=16, blank=True, default='')
 | 
					    user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, verbose_name=_("User"), related_name='comments')
 | 
				
			||||||
 | 
					    user_display = models.CharField(max_length=128, verbose_name=_("User display name"))
 | 
				
			||||||
 | 
					    body = models.TextField(verbose_name=_("Body"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        ordering = ('date_created', )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					# -*- 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)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from .base import *
 | 
				
			||||||
 | 
					from .login_confirm import *
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .. import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ['TicketSerializer', 'CommentSerializer']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TicketSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = models.Ticket
 | 
				
			||||||
 | 
					        fields = [
 | 
				
			||||||
 | 
					            'id', 'user', 'user_display', 'title', 'body',
 | 
				
			||||||
 | 
					            'assignees', 'assignees_display',
 | 
				
			||||||
 | 
					            'status', 'date_created', 'date_updated',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        read_only_fields = [
 | 
				
			||||||
 | 
					            'user_display', 'assignees_display',
 | 
				
			||||||
 | 
					            'date_created', 'date_updated',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommentSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = models.Comment
 | 
				
			||||||
 | 
					        fields = [
 | 
				
			||||||
 | 
					            'id', 'ticket', 'body', 'user', 'user_display',
 | 
				
			||||||
 | 
					            'date_created', 'date_updated'
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        read_only_fields = [
 | 
				
			||||||
 | 
					            'user_display', 'date_created', 'date_updated'
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .base import TicketSerializer
 | 
				
			||||||
 | 
					from ..models import LoginConfirmTicket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ['LoginConfirmTicketSerializer', 'LoginConfirmTicketActionSerializer']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginConfirmTicketSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = LoginConfirmTicket
 | 
				
			||||||
 | 
					        fields = TicketSerializer.Meta.fields + [
 | 
				
			||||||
 | 
					            'ip', 'city', 'action'
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        read_only_fields = TicketSerializer.Meta.read_only_fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginConfirmTicketActionSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    comment = serializers.CharField(allow_blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = LoginConfirmTicket
 | 
				
			||||||
 | 
					        fields = ['action', 'comment']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance, validated_data):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from django.dispatch import receiver
 | 
				
			||||||
 | 
					from django.db.models.signals import m2m_changed, post_save
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from common.utils import get_logger
 | 
				
			||||||
 | 
					from .models import LoginConfirmTicket
 | 
				
			||||||
 | 
					from .utils import (
 | 
				
			||||||
 | 
					    send_login_confirm_ticket_mail_to_assignees,
 | 
				
			||||||
 | 
					    send_login_confirm_action_mail_to_user
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@receiver(m2m_changed, sender=LoginConfirmTicket.assignees.through)
 | 
				
			||||||
 | 
					def on_login_confirm_ticket_assignees_set(sender, instance=None, action=None,
 | 
				
			||||||
 | 
					                                        model=None, pk_set=None, **kwargs):
 | 
				
			||||||
 | 
					    if action == 'post_add':
 | 
				
			||||||
 | 
					        logger.debug('New ticket create, send mail: {}'.format(instance.id))
 | 
				
			||||||
 | 
					        assignees = model.objects.filter(pk__in=pk_set)
 | 
				
			||||||
 | 
					        send_login_confirm_ticket_mail_to_assignees(instance, assignees)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@receiver(post_save, sender=LoginConfirmTicket)
 | 
				
			||||||
 | 
					def on_login_confirm_ticket_status_change(sender, instance=None, created=False, **kwargs):
 | 
				
			||||||
 | 
					    if created or instance.status == "pending":
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    logger.debug('Ticket changed, send mail: {}'.format(instance.id))
 | 
				
			||||||
 | 
					    send_login_confirm_action_mail_to_user(instance)
 | 
				
			||||||
| 
						 | 
					@ -112,9 +112,9 @@
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
{% block custom_foot_js %}
 | 
					{% block custom_foot_js %}
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
var orderId = "{{ object.id }}";
 | 
					var ticketId = "{{ object.id }}";
 | 
				
			||||||
var status = "{{ object.status }}";
 | 
					var status = "{{ object.status }}";
 | 
				
			||||||
var actionCreateUrl = "{% url 'api-orders:login-confirm-order-create-action' pk=object.id %}";
 | 
					var actionCreateUrl = "{% url 'api-tickets:login-confirm-ticket-create-action' pk=object.id %}";
 | 
				
			||||||
$(document).ready(function () {
 | 
					$(document).ready(function () {
 | 
				
			||||||
    if (status !== "pending") {
 | 
					    if (status !== "pending") {
 | 
				
			||||||
        $('.btn-update').attr('disabled', '1')
 | 
					        $('.btn-update').attr('disabled', '1')
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
{% block custom_head_css_js %}
 | 
					{% block custom_head_css_js %}
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
{% block table_container %}
 | 
					{% block table_container %}
 | 
				
			||||||
<table class="table table-striped table-bordered table-hover " id="login_confirm_order_list_table" >
 | 
					<table class="table table-striped table-bordered table-hover " id="login_confirm_ticket_list_table" >
 | 
				
			||||||
    <thead>
 | 
					    <thead>
 | 
				
			||||||
        <tr>
 | 
					        <tr>
 | 
				
			||||||
            <th class="text-center">
 | 
					            <th class="text-center">
 | 
				
			||||||
| 
						 | 
					@ -27,15 +27,15 @@
 | 
				
			||||||
{% block content_bottom_left %}{% endblock %}
 | 
					{% block content_bottom_left %}{% endblock %}
 | 
				
			||||||
{% block custom_foot_js %}
 | 
					{% block custom_foot_js %}
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
var orderTable = 0;
 | 
					var ticketTable = 0;
 | 
				
			||||||
function initTable() {
 | 
					function initTable() {
 | 
				
			||||||
     var options = {
 | 
					     var options = {
 | 
				
			||||||
        ele: $('#login_confirm_order_list_table'),
 | 
					        ele: $('#login_confirm_ticket_list_table'),
 | 
				
			||||||
        oSearch: {sSearch: "status:pending"},
 | 
					        oSearch: {sSearch: "status:open"},
 | 
				
			||||||
        columnDefs: [
 | 
					        columnDefs: [
 | 
				
			||||||
            {targets: 1, createdCell: function (td, cellData, rowData) {
 | 
					            {targets: 1, createdCell: function (td, cellData, rowData) {
 | 
				
			||||||
                cellData = htmlEscape(cellData);
 | 
					                cellData = htmlEscape(cellData);
 | 
				
			||||||
                var detailBtn = '<a href="{% url "orders:login-confirm-order-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
 | 
					                var detailBtn = '<a href="{% url "tickets:login-confirm-ticket-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
 | 
				
			||||||
                $(td).html(detailBtn.replace("{{ DEFAULT_PK }}", rowData.id));
 | 
					                $(td).html(detailBtn.replace("{{ DEFAULT_PK }}", rowData.id));
 | 
				
			||||||
            }},
 | 
					            }},
 | 
				
			||||||
            {targets: 3, createdCell: function (td, cellData, rowData) {
 | 
					            {targets: 3, createdCell: function (td, cellData, rowData) {
 | 
				
			||||||
| 
						 | 
					@ -43,12 +43,14 @@ function initTable() {
 | 
				
			||||||
                $(td).html(d)
 | 
					                $(td).html(d)
 | 
				
			||||||
            }},
 | 
					            }},
 | 
				
			||||||
            {targets: 4, createdCell: function (td, cellData, rowData) {
 | 
					            {targets: 4, createdCell: function (td, cellData, rowData) {
 | 
				
			||||||
                if (cellData === "accepted") {
 | 
					                if (cellData === "approval") {
 | 
				
			||||||
                    $(td).html('<i class="fa fa-check text-navy"></i>')
 | 
					                    $(td).html('<i class="fa fa-check text-navy"></i>')
 | 
				
			||||||
                } else if (cellData === "rejected") {
 | 
					                } else if (cellData === "rejected") {
 | 
				
			||||||
                    $(td).html('<i class="fa fa-times text-danger"></i>')
 | 
					                    $(td).html('<i class="fa fa-times text-danger"></i>')
 | 
				
			||||||
                } else if (cellData === "pending") {
 | 
					                } else if (cellData === "open") {
 | 
				
			||||||
                    $(td).html('<i class="fa fa-spinner text-info"></i>')
 | 
					                    $(td).html('<i class="fa fa-spinner text-info"></i>')
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $(td).html('<i class="fa fa-circle text-info"></i>')
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
             }},
 | 
					             }},
 | 
				
			||||||
            {targets: 5, createdCell: function (td, cellData) {
 | 
					            {targets: 5, createdCell: function (td, cellData) {
 | 
				
			||||||
| 
						 | 
					@ -62,25 +64,25 @@ function initTable() {
 | 
				
			||||||
                    rejectBtn = rejectBtn.replace('{{ DEFAULT_PK }}', cellData);
 | 
					                    rejectBtn = rejectBtn.replace('{{ DEFAULT_PK }}', cellData);
 | 
				
			||||||
                var acceptBtnRef = $(acceptBtn);
 | 
					                var acceptBtnRef = $(acceptBtn);
 | 
				
			||||||
                var rejectBtnRef = $(rejectBtn);
 | 
					                var rejectBtnRef = $(rejectBtn);
 | 
				
			||||||
                if (rowData.status !== "pending") {
 | 
					                if (rowData.action !== "") {
 | 
				
			||||||
                    acceptBtnRef.attr('disabled', 'disabled');
 | 
					                    acceptBtnRef.attr('disabled', 'disabled');
 | 
				
			||||||
                    rejectBtnRef.attr('disabled', 'disabled');
 | 
					                    rejectBtnRef.attr('disabled', 'disabled');
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                var btn = acceptBtnRef.prop('outerHTML') + ' ' + rejectBtnRef.prop('outerHTML');
 | 
					                var btn = acceptBtnRef.prop('outerHTML') + ' ' + rejectBtnRef.prop('outerHTML');
 | 
				
			||||||
                $(td).html(btn)
 | 
					                $(td).html(btn)
 | 
				
			||||||
             }}],
 | 
					             }}],
 | 
				
			||||||
        ajax_url: '{% url "api-orders:login-confirm-order-list" %}',
 | 
					        ajax_url: '{% url "api-tickets:login-confirm-ticket-list" %}',
 | 
				
			||||||
        columns: [
 | 
					        columns: [
 | 
				
			||||||
            {data: "id"}, {data: "title", className: "text-left"},
 | 
					            {data: "id"}, {data: "title"},
 | 
				
			||||||
            {data: "user_display"}, {data: "ip"},
 | 
					            {data: "user_display"}, {data: "ip"},
 | 
				
			||||||
            {data: "status", orderable: false, width: "30px"},
 | 
					            {data: "status", ticketable: false},
 | 
				
			||||||
            {data: "date_created", width: "120px"},
 | 
					            {data: "date_created", width: "120px"},
 | 
				
			||||||
            {data: "id", orderable: false, width: "100px"}
 | 
					            {data: "id", ticketable: false}
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        op_html: $('#actions').html()
 | 
					        op_html: $('#actions').html()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    orderTable = jumpserver.initServerSideDataTable(options);
 | 
					    ticketTable = jumpserver.initServerSideDataTable(options);
 | 
				
			||||||
    return orderTable
 | 
					    return ticketTable
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(document).ready(function(){
 | 
					$(document).ready(function(){
 | 
				
			||||||
| 
						 | 
					@ -89,16 +91,19 @@ $(document).ready(function(){
 | 
				
			||||||
        {title: "IP", value: "ip"},
 | 
					        {title: "IP", value: "ip"},
 | 
				
			||||||
        {title: "{% trans 'Title' %}", value: "title"},
 | 
					        {title: "{% trans 'Title' %}", value: "title"},
 | 
				
			||||||
        {title: "{% trans 'Status' %}", value: "status", submenu: [
 | 
					        {title: "{% trans 'Status' %}", value: "status", submenu: [
 | 
				
			||||||
                {title: "{% trans 'Pending' %}", value: "pending"},
 | 
					                {title: "{% trans 'Open' %}", value: "open"},
 | 
				
			||||||
                {title: "{% trans 'Accepted' %}", value: "accepted"},
 | 
					                {title: "{% trans 'Closed' %}", value: "closed"},
 | 
				
			||||||
                {title: "{% trans 'Rejected' %}", value: "rejected"}
 | 
					        ]},
 | 
				
			||||||
        ]}
 | 
					        {title: "{% trans 'Action' %}", value: "action", submenu: [
 | 
				
			||||||
 | 
					                {title: "{% trans 'Approve' %}", value: "approve"},
 | 
				
			||||||
 | 
					                {title: "{% trans 'Reject' %}", value: "reject"},
 | 
				
			||||||
 | 
					        ]},
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    initTableFilterDropdown('#login_confirm_order_list_table_filter input', menu)
 | 
					    initTableFilterDropdown('#login_confirm_ticket_list_table_filter input', menu)
 | 
				
			||||||
}).on('click', '.btn-action', function () {
 | 
					}).on('click', '.btn-action', function () {
 | 
				
			||||||
    var actionCreateUrl = "{% url 'api-orders:login-confirm-order-create-action' pk=DEFAULT_PK %}";
 | 
					    var actionCreateUrl = "{% url 'api-tickets:login-confirm-ticket-create-action' pk=DEFAULT_PK %}";
 | 
				
			||||||
    var orderId = $(this).data('uid');
 | 
					    var ticketId = $(this).data('uid');
 | 
				
			||||||
    actionCreateUrl = actionCreateUrl.replace("{{ DEFAULT_PK }}", orderId);
 | 
					    actionCreateUrl = actionCreateUrl.replace("{{ DEFAULT_PK }}", ticketId);
 | 
				
			||||||
    var action = $(this).data('action');
 | 
					    var action = $(this).data('action');
 | 
				
			||||||
    var comment = '';
 | 
					    var comment = '';
 | 
				
			||||||
    var data = {
 | 
					    var data = {
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					from rest_framework.routers import DefaultRouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .. import api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = 'tickets'
 | 
				
			||||||
 | 
					router = DefaultRouter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.register('tickets', api.TicketViewSet, 'ticket')
 | 
				
			||||||
 | 
					router.register('login-confirm-tickets', api.LoginConfirmTicketViewSet, 'login-confirm-ticket')
 | 
				
			||||||
 | 
					router.register('tickets/<uuid:ticket_id>/comments/', api.CommentViewSet, 'ticket-comment')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path('login-confirm-tickets/<uuid:pk>/actions/',
 | 
				
			||||||
 | 
					         api.LoginConfirmTicketsCreateActionApi.as_view(),
 | 
				
			||||||
 | 
					         name='login-confirm-ticket-create-action'
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns += router.urls
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					from .. import views
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = 'tickets'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path('login-confirm-tickets/', views.LoginConfirmTicketListView.as_view(), name='login-confirm-ticket-list'),
 | 
				
			||||||
 | 
					    path('login-confirm-tickets/<uuid:pk>/', views.LoginConfirmTicketDetailView.as_view(), name='login-confirm-ticket-detail')
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.utils.translation import ugettext as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from common.utils import get_logger, reverse
 | 
				
			||||||
 | 
					from common.tasks import send_mail_async
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def send_login_confirm_ticket_mail_to_assignees(ticket, assignees):
 | 
				
			||||||
 | 
					    recipient_list = [user.email for user in assignees]
 | 
				
			||||||
 | 
					    user = ticket.user
 | 
				
			||||||
 | 
					    if not recipient_list:
 | 
				
			||||||
 | 
					        logger.error("Ticket not has assignees: {}".format(ticket.id))
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    subject = '{}: {}'.format(_("New ticket"), ticket.title)
 | 
				
			||||||
 | 
					    detail_url = reverse('tickets:login-confirm-ticket-detail',
 | 
				
			||||||
 | 
					                         kwargs={'pk': ticket.id}, external=True)
 | 
				
			||||||
 | 
					    message = _("""
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <p>Your has a new ticket</p>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					                <b>Title:</b> {ticket.title}
 | 
				
			||||||
 | 
					                <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/>
 | 
				
			||||||
 | 
					                <a href={url}>click here to review</a> 
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    """).format(ticket=ticket, user=user, url=detail_url)
 | 
				
			||||||
 | 
					    send_mail_async.delay(subject, message, recipient_list, html_message=message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def send_login_confirm_action_mail_to_user(ticket):
 | 
				
			||||||
 | 
					    if not ticket.user:
 | 
				
			||||||
 | 
					        logger.error("Ticket not has user: {}".format(ticket.id))
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    user = ticket.user
 | 
				
			||||||
 | 
					    recipient_list = [user.email]
 | 
				
			||||||
 | 
					    subject = '{}: {}'.format(_("Ticket has been reply"), ticket.title)
 | 
				
			||||||
 | 
					    message = _("""
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <p>Your ticket has been replay</p>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					                <b>Title:</b> {ticket.title}
 | 
				
			||||||
 | 
					                <br/>
 | 
				
			||||||
 | 
					                <b>Assignee:</b> {ticket.assignee_display}
 | 
				
			||||||
 | 
					                <br/>
 | 
				
			||||||
 | 
					                <b>Status:</b> {ticket.status_display}
 | 
				
			||||||
 | 
					                <br/>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					     """).format(ticket=ticket)
 | 
				
			||||||
 | 
					    send_mail_async.delay(subject, message, recipient_list, html_message=message)
 | 
				
			||||||
| 
						 | 
					@ -2,33 +2,33 @@ 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, IsOrgAdmin
 | 
					from common.permissions import PermissionsMixin, IsOrgAdmin
 | 
				
			||||||
from .models import LoginConfirmOrder
 | 
					from .models import LoginConfirmTicket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginConfirmOrderListView(PermissionsMixin, TemplateView):
 | 
					class LoginConfirmTicketListView(PermissionsMixin, TemplateView):
 | 
				
			||||||
    template_name = 'orders/login_confirm_order_list.html'
 | 
					    template_name = 'tickets/login_confirm_ticket_list.html'
 | 
				
			||||||
    permission_classes = (IsOrgAdmin,)
 | 
					    permission_classes = (IsOrgAdmin,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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': _("Orders"),
 | 
					            'app': _("Tickets"),
 | 
				
			||||||
            'action': _("Login confirm order list")
 | 
					            'action': _("Login confirm ticket list")
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginConfirmOrderDetailView(PermissionsMixin, DetailView):
 | 
					class LoginConfirmTicketDetailView(PermissionsMixin, DetailView):
 | 
				
			||||||
    template_name = 'orders/login_confirm_order_detail.html'
 | 
					    template_name = 'tickets/login_confirm_ticket_detail.html'
 | 
				
			||||||
    permission_classes = (IsOrgAdmin,)
 | 
					    permission_classes = (IsOrgAdmin,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return LoginConfirmOrder.objects.filter(assignees=self.request.user)
 | 
					        return LoginConfirmTicket.objects.filter(assignees=self.request.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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': _("Orders"),
 | 
					            'app': _("Tickets"),
 | 
				
			||||||
            'action': _("Login confirm order detail")
 | 
					            'action': _("Login confirm ticket detail")
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
{% block table_container %}
 | 
					{% block table_container %}
 | 
				
			||||||
<div class="uc pull-left m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
 | 
					<div class="uc pull-left m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
 | 
				
			||||||
<table class="table table-striped table-bordered table-hover " id="user_list_table" >
 | 
					<table class="table table-striped table-bordered table-hover " id="user_list_table">
 | 
				
			||||||
    <thead>
 | 
					    <thead>
 | 
				
			||||||
        <tr>
 | 
					        <tr>
 | 
				
			||||||
            <th class="text-center">
 | 
					            <th class="text-center">
 | 
				
			||||||
| 
						 | 
					@ -125,7 +125,7 @@ function initTable() {
 | 
				
			||||||
            {data: "groups_display", orderable: false},
 | 
					            {data: "groups_display", orderable: false},
 | 
				
			||||||
            {data: "source"},
 | 
					            {data: "source"},
 | 
				
			||||||
            {data: "is_valid", orderable: false},
 | 
					            {data: "is_valid", orderable: false},
 | 
				
			||||||
            {data: "id", orderable: false, width: "100px"}
 | 
					            {data: "id", orderable: false}
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        op_html: $('#actions').html()
 | 
					        op_html: $('#actions').html()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue