@@ -155,7 +155,7 @@ def authticate_with_otp_and_passsword(principal: IAmPrincipal, otp=None, passwor
|
||||
|
||||
if otp:
|
||||
otp_instance = IAmPrincipalOtp.objects.filter(
|
||||
principal=principal, otp_code=otp
|
||||
principal=principal, otp_code=otp, is_used=False
|
||||
).last()
|
||||
|
||||
if not otp_instance:
|
||||
|
||||
@@ -137,14 +137,14 @@ class OtpRequestView(APIView):
|
||||
# principal = auth_service.get_principal_by_email(request.data.get("email"))
|
||||
|
||||
otp_code = SMSService().create_otp(
|
||||
principal=principal, otp_purpose="Forget password"
|
||||
principal=principal, otp_purpose="Password Reset Request"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return ApiResponse.error(message=str(e), errors=str(e))
|
||||
|
||||
email_service = EmailService(
|
||||
subject="Forget Password",
|
||||
subject="Password Reset Request",
|
||||
to=principal.email,
|
||||
from_email=settings.EMAIL_HOST_USER,
|
||||
)
|
||||
@@ -197,24 +197,27 @@ class OTPVerificationView(APIView):
|
||||
|
||||
|
||||
class ForgetPasswordView(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = PasswordResetSerializer
|
||||
|
||||
def post(self, request):
|
||||
email = request.data.get("email")
|
||||
|
||||
print("email for password reset", email)
|
||||
principal = get_principal_by_email(email=email)
|
||||
|
||||
otp_instance = IAmPrincipalOtp.objects.filter(principal=principal).last()
|
||||
if isinstance(principal, Response):
|
||||
return principal
|
||||
|
||||
otp_instance = IAmPrincipalOtp.objects.filter(principal=principal, is_used=True).last()
|
||||
|
||||
if not otp_instance:
|
||||
return ApiResponse.error(message=constants.SESSION_EXPIRED)
|
||||
return ApiResponse.error(message=constants.PASSWORD_RESET_SESSION_EXPIRE)
|
||||
|
||||
if otp_instance.is_expired():
|
||||
return ApiResponse.error(message=constants.SESSION_EXPIRED)
|
||||
return ApiResponse.error(message=constants.PASSWORD_RESET_SESSION_EXPIRE)
|
||||
|
||||
serializer = self.serializer_class(request.user, data=request.data)
|
||||
serializer = self.serializer_class(principal, data=request.data)
|
||||
if not serializer.is_valid():
|
||||
error_response = {
|
||||
"status": status.HTTP_403_FORBIDDEN,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic import TemplateView, RedirectView
|
||||
|
||||
app_name = "module_auth"
|
||||
|
||||
urlpatterns = [
|
||||
# redirect to different url
|
||||
path('', RedirectView.as_view(url='login'), name='index'),
|
||||
path('login/', views.AdminLoginView.as_view(), name="login"),
|
||||
path('logout/', views.AdminLogoutView.as_view(), name="logout"),
|
||||
path('password-reset/', views.CustomPasswordResetView.as_view(), name='password_reset'),
|
||||
|
||||
@@ -55,13 +55,6 @@ class IAmPrincipalForm(forms.ModelForm):
|
||||
widget=forms.PasswordInput(attrs={"autocomplete": "off"})
|
||||
)
|
||||
|
||||
is_active = forms.BooleanField(
|
||||
label="Active",
|
||||
initial=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.IAmPrincipal
|
||||
fields = [
|
||||
@@ -71,26 +64,19 @@ class IAmPrincipalForm(forms.ModelForm):
|
||||
"email",
|
||||
"password",
|
||||
"confirm_password",
|
||||
"is_active",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instance = kwargs.get("instance")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["principal_type"].queryset = models.IAmPrincipalType.objects.filter(
|
||||
active=True, deleted=False
|
||||
active=True, deleted=False, name__in=(PRINCIPAL_TYPE_ADMIN, PRINCIPAL_TYPE_SUBADMIN)
|
||||
)
|
||||
# If it's a create action, exclude 'is_active' field
|
||||
if instance is None:
|
||||
self.fields.pop("is_active", None)
|
||||
else:
|
||||
# Exclude 'password' and 'confirm_password' fields for updates
|
||||
if instance is not None and instance.pk is not None:
|
||||
self.fields.pop("password", None)
|
||||
self.fields.pop("confirm_password", None)
|
||||
|
||||
# Make the 'email' field read-only
|
||||
self.fields["email"].widget.attrs["readonly"] = True
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get("email")
|
||||
# Skip uniqueness validation if it's an update action (instance exists)
|
||||
@@ -237,21 +223,20 @@ class ProfileEditForm(forms.ModelForm):
|
||||
]
|
||||
|
||||
|
||||
class IAmPrincipalGroupLinkForm(forms.ModelForm):
|
||||
class IAmPrincipalGroupLinkForm(IAmPrincipalForm):
|
||||
|
||||
class Meta:
|
||||
model = models.IAmPrincipal
|
||||
fields = [
|
||||
# "principal_type",
|
||||
"principal_type",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"password",
|
||||
"confirm_password",
|
||||
"principal_group",
|
||||
]
|
||||
|
||||
# principal_type = forms.ModelChoiceField(
|
||||
# label="Principal Type",
|
||||
# queryset=models.IAmPrincipalType.objects.filter(active=True, deleted=False),
|
||||
# widget=forms.widgets.TextInput(attrs={"readonly": True}),
|
||||
# )
|
||||
principal_group = forms.ModelMultipleChoiceField(
|
||||
label="Groups",
|
||||
queryset=models.IAmPrincipalGroup.objects.filter(active=True, deleted=False),
|
||||
@@ -261,13 +246,18 @@ class IAmPrincipalGroupLinkForm(forms.ModelForm):
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Make the 'email' field read-only
|
||||
# self.fields['principal_type'].widget.attrs['disabled'] = True
|
||||
self.fields['email'].widget.attrs['readonly'] = True
|
||||
|
||||
def save(self, commit=True):
|
||||
# First, save the instance of the IAmPrincipal model as usual
|
||||
principal = super().save(commit=False)
|
||||
# If the principal_group field has data
|
||||
if self.cleaned_data['principal_group']:
|
||||
# Get the principal_group data
|
||||
principal_group_data = self.cleaned_data['principal_group']
|
||||
# Update the many-to-many relationship
|
||||
principal.principal_group.set(principal_group_data)
|
||||
# Save the instance to the database
|
||||
if commit:
|
||||
principal.save()
|
||||
|
||||
class IAmPrincipalTypeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
||||
@@ -9,19 +9,20 @@ urlpatterns = [
|
||||
|
||||
# path('principal/', views.PrincipalListView.as_view(), name="principal_list"),
|
||||
# path('principal/add/', views.PrincipalCreateOrUpdateView.as_view(), name="principal_add"),
|
||||
# path('principal/edit/<int:pk>', views.PrincipalCreateOrUpdateView.as_view(), name="principal_edit"),
|
||||
path('principal/edit/<int:pk>', views.PrincipalCreateOrUpdateView.as_view(), name="principal_edit"),
|
||||
# path('principal/delete/<int:pk>', views.PrincipalDeleteView.as_view(), name="principal_delete"),
|
||||
|
||||
path('principal/group/link/', views.PrincipalGroupLinkView.as_view(), name="principal_group_link"),
|
||||
path('principal/group/link/', views.PrincipalGroupLinkAdminListJsonView.as_view(), name="principal_group_link_list"),
|
||||
# path('principal/group/link/edit/<int:pk>/', views.PrincipalGroupLinkEditView.as_view(), name="principal_group_link_edit"),
|
||||
|
||||
path('principal/group/link/list/admin/', views.PrincipalGroupLinkAdminListJsonView.as_view(), name="principal_group_link_list"),
|
||||
path('principal/group/link/list/subadmin/', views.PrincipalGroupLinkSubAdminListJsonView.as_view(), name="principal_group_link_list_sub"),
|
||||
path('principal/group/link/edit/<int:pk>/', views.PrincipalGroupLinkEditView.as_view(), name="principal_group_link_edit"),
|
||||
path('principal/group/link/action/', views.PrincipalGroupLinkActionView.as_view(), name="principal_group_link_action"),
|
||||
|
||||
path('principal/group/', views.PrincipalGroupView.as_view(), name="principal_group"),
|
||||
path('principal/group/list', views.PrincipalGroupListJsonView.as_view(), name="principal_group_list"),
|
||||
path('principal/group/add/', views.PrincipalGroupCreateOrUpdateView.as_view(), name="principal_group_add"),
|
||||
path('principal/group/edit/<int:pk>/', views.PrincipalGroupCreateOrUpdateView.as_view(), name="principal_group_edit"),
|
||||
path('principal/group/action//', views.PrincipalGroupActionView.as_view(), name="principal_group_action"),
|
||||
path('principal/group/action/', views.PrincipalGroupActionView.as_view(), name="principal_group_action"),
|
||||
|
||||
path('principal/role/', views.AppRoleView.as_view(), name="role"),
|
||||
path('principal/role/list/', views.AppRoleListJsonView.as_view(), name="role_list"),
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from typing import Any
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models.base import Model as Model
|
||||
from django.db.models.query import QuerySet
|
||||
from django.views import generic
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import Q
|
||||
@@ -14,13 +18,14 @@ from module_iam import iam_constant
|
||||
from module_project.mixins import DatatablesMixin
|
||||
from django_datatables_view.base_datatable_view import BaseDatatableView
|
||||
from module_project.mixins import ActionMixin
|
||||
from module_project.utils import JsonResponseUtil
|
||||
from .forms import (
|
||||
CustomAuthenticationForm,
|
||||
IAmPrincipalForm,
|
||||
IAmPrincipalGroupRoleLinkForm,
|
||||
IAmPrincipalRoleAppResourceActionLinkForm,
|
||||
IAmPrincipalGroupLinkForm,
|
||||
ProfileEditForm
|
||||
ProfileEditForm,
|
||||
)
|
||||
from .models import (
|
||||
IAmPrincipal,
|
||||
@@ -36,6 +41,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class DashboardView(generic.TemplateView):
|
||||
page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD
|
||||
template_name = "base_structure/layout/dashboard.html"
|
||||
@@ -51,11 +57,68 @@ class DashboardView(generic.TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
active_user_count, total_user_count = self.get_user_count()
|
||||
context['active_user_count'] = active_user_count
|
||||
context['total_user_count'] = total_user_count
|
||||
context['page_name'] = self.page_name
|
||||
context["active_user_count"] = active_user_count
|
||||
context["total_user_count"] = total_user_count
|
||||
context["page_name"] = self.page_name
|
||||
return context
|
||||
|
||||
|
||||
class PrincipalCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
page_name = iam_constant.RESOURCE_IAM_PRINCIPAL
|
||||
model = IAmPrincipal
|
||||
form_class = IAmPrincipalForm
|
||||
template_name = "module_iam/iam_principal_add.html"
|
||||
success_url = reverse_lazy("module_iam:principal_group_link")
|
||||
success_message = "Saved Successfully"
|
||||
error_message = "An error occurred while saving the data."
|
||||
|
||||
def get_object(self):
|
||||
pk = self.kwargs.get("pk")
|
||||
return get_object_or_404(self.model, pk=pk) if pk else None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
"page_name": self.page_name,
|
||||
"operation": "Edit" if self.object else "Add",
|
||||
}
|
||||
context.update(kwargs) # Include any additional context data passed to the view
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.form_class(instance=self.object)
|
||||
context = self.get_context_data(form=form)
|
||||
return render(request, self.template_name, context=context)
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
print(request.POST)
|
||||
self.object = self.get_object()
|
||||
form = self.form_class(request.POST, instance=self.object)
|
||||
try:
|
||||
if form.is_valid():
|
||||
principal = form.save(commit=False)
|
||||
|
||||
# Check if it's a new object (create action) or an existing one (update action)
|
||||
if not principal.pk: # pk is None for new objects
|
||||
principal.created_by = request.user
|
||||
principal.modified_by = request.user
|
||||
principal.modified_on = datetime.now()
|
||||
|
||||
# Save the object
|
||||
principal.save()
|
||||
|
||||
messages.success(request, "Form submitted successfully")
|
||||
return redirect(self.success_url)
|
||||
except Exception as e:
|
||||
self.error_message = constants.ERROR_OCCURR.format(str(e))
|
||||
print(self.error_message)
|
||||
messages.error(request, self.error_message)
|
||||
|
||||
context = self.get_context_data(form=form)
|
||||
return render(request, template_name=self.template_name, context=context)
|
||||
|
||||
|
||||
class PrincipalGroupLinkView(LoginRequiredMixin, generic.TemplateView):
|
||||
page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
|
||||
model = IAmPrincipal
|
||||
@@ -66,14 +129,121 @@ class PrincipalGroupLinkView(LoginRequiredMixin, generic.TemplateView):
|
||||
context["page_name"] = self.page_name
|
||||
return context
|
||||
|
||||
|
||||
class PrincipalGroupLinkAdminListJsonView(BaseDatatableView):
|
||||
model = IAmPrincipal
|
||||
columns = ["id", "first_name", "email", "principal_type__name", "is_active"],
|
||||
order_columns = ["id", "first_name", "email"]
|
||||
columns = ["id", "first_name", "email", "is_active"]
|
||||
order_columns = ["id", "first_name", "email", "is_active"]
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get('deleted_flag', False)
|
||||
return self.model.objects.filter(deleted=deleted_flag).exclude(principal_type__name=iam_constant.PRINCIPAL_TYPE_USER)
|
||||
deleted_flag = self.request.GET.get("deleted_flag", False)
|
||||
return self.model.objects.filter(deleted=deleted_flag, principal_type__name=iam_constant.PRINCIPAL_TYPE_ADMIN)
|
||||
|
||||
def render_column(self, row, column):
|
||||
if column == "principal_type_name":
|
||||
return row.principal_type.name if row.principal_type else None
|
||||
return super().render_column(row, column)
|
||||
|
||||
def filter_queryset(self, qs):
|
||||
search_value = self.request.GET.get("search[value]", None)
|
||||
if search_value:
|
||||
qs = qs.filter(
|
||||
Q(id__icontains=search_value) | Q(first_name__icontains=search_value) | Q(email__icontains=search_value)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
class PrincipalGroupLinkSubAdminListJsonView(BaseDatatableView):
|
||||
model = IAmPrincipal
|
||||
columns = ["id", "first_name", "email", "is_active"]
|
||||
order_columns = ["id", "first_name", "email", "is_active"]
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get("deleted_flag", False)
|
||||
return self.model.objects.filter(deleted=deleted_flag, principal_type__name=iam_constant.PRINCIPAL_TYPE_SUBADMIN)
|
||||
|
||||
def render_column(self, row, column):
|
||||
if column == "principal_type_name":
|
||||
return row.principal_type.name if row.principal_type else None
|
||||
if column == "groups":
|
||||
return [{"name": group.name} for group in row.principal_group.all()]
|
||||
return super().render_column(row, column)
|
||||
|
||||
def filter_queryset(self, qs):
|
||||
search_value = self.request.GET.get("search[value]", None)
|
||||
if search_value:
|
||||
qs = qs.filter(
|
||||
Q(id__icontains=search_value) | Q(first_name__icontains=search_value) | Q(email__icontains=search_value)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
class PrincipalGroupLinkEditView(LoginRequiredMixin, generic.View):
|
||||
page_name = iam_constant.RESOURCE_IAM_PRINCIPAL_GROUP
|
||||
model = IAmPrincipal
|
||||
template_name = "module_iam/iam_principal_group_link_edit.html"
|
||||
form_class = IAmPrincipalGroupLinkForm
|
||||
success_url = reverse_lazy("module_iam:principal_group_link")
|
||||
success_message = "Record Updated Successfully"
|
||||
error_message = "An error occurred while saving the data"
|
||||
|
||||
def get_object(self):
|
||||
pk = self.kwargs.get("pk")
|
||||
return get_object_or_404(self.model, pk=pk) if pk else None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
"page_name": self.page_name,
|
||||
"operation": "Edit",
|
||||
}
|
||||
context.update(kwargs) # Include any additional context data passed to the view
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.form_class(instance=self.object)
|
||||
context = self.get_context_data(form=form)
|
||||
return render(request, self.template_name, context=context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.form_class(request.POST, instance=self.object)
|
||||
if not form.is_valid():
|
||||
context = self.get_context_data(form=form)
|
||||
return render(request, self.template_name, context=context)
|
||||
form.save()
|
||||
messages.success(request, self.success_message)
|
||||
return redirect(self.success_url)
|
||||
|
||||
|
||||
class PrincipalGroupLinkActionView(generic.View):
|
||||
model = IAmPrincipal
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
if self.model is None:
|
||||
raise NotImplementedError("Subclasses of BaseActionView must define a 'model' attribute.")
|
||||
|
||||
action = request.POST.get('action') # 'archive', 'active', or 'unarchive'
|
||||
ids = request.POST.getlist('ids[]') # List of IDs to perform action on
|
||||
active = request.POST.get('active')
|
||||
print(f"arhive action {action} and id is {ids} and active data is {active}")
|
||||
if action == 'archive':
|
||||
# Update 'deleted' field to True for the selected users
|
||||
self.model.objects.filter(id__in=ids).update(deleted=True, is_active=False)
|
||||
message = 'Record archived successfully.'
|
||||
elif action == 'active':
|
||||
# Update 'active' field to True for the selected users
|
||||
self.model.objects.filter(id__in=ids).update(is_active=active.capitalize())
|
||||
message = 'Record updated successfully.'
|
||||
elif action == 'unarchive':
|
||||
# Update 'deleted' field to False for the selected users
|
||||
self.model.objects.filter(id__in=ids).update(deleted=False)
|
||||
message = 'Record unarchived successfully.'
|
||||
else:
|
||||
return JsonResponseUtil.error(message="Invalid Action")
|
||||
|
||||
return JsonResponseUtil.success(message=message)
|
||||
|
||||
|
||||
class PrincipalGroupView(LoginRequiredMixin, generic.TemplateView):
|
||||
@@ -86,15 +256,6 @@ class PrincipalGroupView(LoginRequiredMixin, generic.TemplateView):
|
||||
context["page_name"] = self.page_name
|
||||
return context
|
||||
|
||||
def filter_queryset(self, qs):
|
||||
search_value = self.request.GET.get("search[value]", None)
|
||||
if search_value:
|
||||
qs = qs.filter(
|
||||
Q(id__icontains=search_value)
|
||||
| Q(name__icontains=search_value)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
class PrincipalGroupListJsonView(BaseDatatableView):
|
||||
model = IAmPrincipalGroup
|
||||
@@ -102,41 +263,43 @@ class PrincipalGroupListJsonView(BaseDatatableView):
|
||||
order_columns = ["id", "name", "active"]
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get('deleted_flag', False)
|
||||
deleted_flag = self.request.GET.get("deleted_flag", False)
|
||||
return self.model.objects.filter(deleted=deleted_flag)
|
||||
|
||||
def filter_queryset(self, qs):
|
||||
search_value = self.request.GET.get("search[value]", None)
|
||||
if search_value:
|
||||
qs = qs.filter(
|
||||
Q(id__icontains=search_value)
|
||||
| Q(name__icontains=search_value)
|
||||
Q(id__icontains=search_value) | Q(name__icontains=search_value)
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
def generate_role_data(self, queryset):
|
||||
roles_data = []
|
||||
for obj in queryset:
|
||||
roles = [{'name': role.name} for role in obj.role.all()]
|
||||
roles = [{"name": role.name} for role in obj.role.all()]
|
||||
print(f"role data is this {roles}")
|
||||
roles_data.append({
|
||||
'id': obj.id,
|
||||
'name': obj.name,
|
||||
'active': str(obj.active),
|
||||
'roles': roles
|
||||
})
|
||||
roles_data.append(
|
||||
{
|
||||
"id": obj.id,
|
||||
"name": obj.name,
|
||||
"active": str(obj.active),
|
||||
"roles": roles,
|
||||
}
|
||||
)
|
||||
return roles_data
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
roles = self.filter_queryset(self.get_initial_queryset())
|
||||
role_data = self.generate_role_data(roles)
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['recordsTotal'] = len(role_data)
|
||||
context['recordsFiltered'] = len(role_data)
|
||||
context['data'] = role_data
|
||||
context['result'] = 'ok'
|
||||
context["recordsTotal"] = len(role_data)
|
||||
context["recordsFiltered"] = len(role_data)
|
||||
context["data"] = role_data
|
||||
context["result"] = "ok"
|
||||
return context
|
||||
|
||||
|
||||
class PrincipalGroupCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
page_name = iam_constant.RESOURCE_IAM_GROUP
|
||||
page_title = "Principal Group"
|
||||
@@ -147,7 +310,9 @@ class PrincipalGroupCreateOrUpdateView(LoginRequiredMixin, generic.View):
|
||||
error_message = "An error occurred while saving the data."
|
||||
|
||||
def get_success_message(self):
|
||||
self.success_message = constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
|
||||
self.success_message = (
|
||||
constants.RECORD_CREATED if not self.object else constants.RECORD_UPDATED
|
||||
)
|
||||
return self.success_message
|
||||
|
||||
def get_object(self):
|
||||
@@ -194,13 +359,14 @@ class AppRoleView(LoginRequiredMixin, generic.TemplateView):
|
||||
context["page_name"] = self.page_name
|
||||
return context
|
||||
|
||||
|
||||
class AppRoleListJsonView(BaseDatatableView):
|
||||
model = IAmRole
|
||||
columns = ["id", "name", "active", "resources"]
|
||||
order_columns = ["id", "name"]
|
||||
|
||||
def get_initial_queryset(self):
|
||||
deleted_flag = self.request.GET.get('deleted_flag', False)
|
||||
deleted_flag = self.request.GET.get("deleted_flag", False)
|
||||
return (
|
||||
super(AppRoleListJsonView, self)
|
||||
.get_initial_queryset()
|
||||
@@ -247,10 +413,10 @@ class AppRoleListJsonView(BaseDatatableView):
|
||||
roles = self.filter_queryset(self.get_initial_queryset())
|
||||
role_data = self.generate_resource_data(roles)
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['recordsTotal'] = len(role_data)
|
||||
context['recordsFiltered'] = len(role_data)
|
||||
context['data'] = role_data
|
||||
context['result'] = 'ok'
|
||||
context["recordsTotal"] = len(role_data)
|
||||
context["recordsFiltered"] = len(role_data)
|
||||
context["data"] = role_data
|
||||
context["result"] = "ok"
|
||||
return context
|
||||
|
||||
|
||||
@@ -319,7 +485,10 @@ class PrincipalProfileView(LoginRequiredMixin, generic.TemplateView):
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
user = self.request.user.id
|
||||
return get_object_or_404(self.model.objects.select_related("principal_type", "principal_source"), pk=user)
|
||||
return get_object_or_404(
|
||||
self.model.objects.select_related("principal_type", "principal_source"),
|
||||
pk=user,
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -327,6 +496,7 @@ class PrincipalProfileView(LoginRequiredMixin, generic.TemplateView):
|
||||
context["data_obj"] = self.get_object()
|
||||
return context
|
||||
|
||||
|
||||
class PrincipalProfileEditView(generic.View):
|
||||
page_name = iam_constant.RESOURCE_MANAGE_DASHBOARD
|
||||
model = IAmPrincipal
|
||||
@@ -349,7 +519,7 @@ class PrincipalProfileEditView(generic.View):
|
||||
context = {
|
||||
# "page_name": self.page_name,
|
||||
"operation": "Edit",
|
||||
"page_name": self.page_name
|
||||
"page_name": self.page_name,
|
||||
}
|
||||
context.update(kwargs) # Include any additional context data passed to the view
|
||||
return context
|
||||
@@ -368,7 +538,7 @@ class PrincipalProfileEditView(generic.View):
|
||||
print(form.errors)
|
||||
context = self.get_context_data(form=form)
|
||||
return render(request, self.template_name, context=context)
|
||||
|
||||
|
||||
form.save()
|
||||
messages.success(self.request, self.get_success_message())
|
||||
return redirect(self.success_url)
|
||||
return redirect(self.success_url)
|
||||
|
||||
@@ -129,6 +129,12 @@ class NotificationActionView(ActionMixin):
|
||||
class NotificationSendView(generic.View):
|
||||
model = PushNotification
|
||||
|
||||
def get_image_url(self, obj, field_name, request):
|
||||
image_field = getattr(obj, field_name)
|
||||
if image_field:
|
||||
return request.build_absolute_uri(image_field.url)
|
||||
return ""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
id = request.POST.get("id")
|
||||
obj = self.model.objects.filter(pk=int(id)).first()
|
||||
@@ -141,8 +147,6 @@ class NotificationSendView(generic.View):
|
||||
if not obj:
|
||||
return JsonResponseUtil.error(message="No notification with such ID exists.")
|
||||
|
||||
print(f"data type is ============ {type(player_ids)}")
|
||||
print(f"player id aare {player_ids}")
|
||||
try:
|
||||
notification = OneSignalService()
|
||||
response = notification.send_notification(
|
||||
@@ -151,9 +155,7 @@ class NotificationSendView(generic.View):
|
||||
# include_player_ids=["5643e132-5266-4dc2-9131-1b4a81f0cbd0"], # single player id
|
||||
include_player_ids=player_ids,
|
||||
)
|
||||
print("pussh dtaa ===========", response)
|
||||
except Exception as e:
|
||||
print(f"Error is {e}")
|
||||
error_response = {
|
||||
"status": 400,
|
||||
"message": constants.INTERNAL_SERVER_ERROR,
|
||||
|
||||
@@ -31,6 +31,7 @@ LOGIN_REQUIRED = "Login required to perform this action."
|
||||
LOGIN_SUCCESS = "Login successful."
|
||||
LOGOUT_SUCCESS = "Logout successful."
|
||||
SESSION_EXPIRED = "Your session has expired. Please log in again."
|
||||
PASSWORD_RESET_SESSION_EXPIRE = "Password reset session has expired"
|
||||
ACCOUNT_DEACTIVATED = "Your account is inactive. Please contact support."
|
||||
EMAIL_EXISTS = "This email address is already in use. Please use a different email."
|
||||
INVALID_EMAIL_PASSWORD = "Invalid email or password."
|
||||
|
||||
@@ -28,7 +28,7 @@ if READ_DOT_ENV_FILE:
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# SECRET_KEY = 'django-insecure-#7rdu=fr58ba9_!n3$l5pm!xs8l%6%8xt@vb8$&o@hqhd@rtd%'
|
||||
|
||||
SECRET_KEY = env.str("SECRET_KEY")
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
@@ -108,7 +108,7 @@ WSGI_APPLICATION = 'module_project.wsgi.application'
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"NAME": "digest_db",
|
||||
"NAME": env.str("DB_DATABASE"),
|
||||
"HOST": env.str("DB_HOST"),
|
||||
"USER": env.str("DB_USERNAME"),
|
||||
"PASSWORD": env.str("DB_PASSWORD"),
|
||||
@@ -154,12 +154,12 @@ SHORT_DATE_FORMAT = "d-m-Y"
|
||||
TIME_FORMAT = "H:i p"
|
||||
|
||||
# otp expire time limit
|
||||
OTP_EXPIRE_TIME = 5 # mins
|
||||
OTP_EXPIRE_TIME = 2 # mins
|
||||
|
||||
APPEND_SLASH = True
|
||||
LOGIN_REDIRECT_URL = "/iam/dashboard/"
|
||||
LOGIN_URL = "/auth/login/"
|
||||
LOGOUT_REDIRECT_URL = "/auth/login/"
|
||||
LOGIN_URL = "/login/"
|
||||
LOGOUT_REDIRECT_URL = "/login/"
|
||||
|
||||
|
||||
# https://docs.djangoproject.com/en/4.2/topics/auth/customizing/#substituting-a-custom-user-model
|
||||
@@ -219,8 +219,6 @@ ONESIGNAL_APP_ID = env.str("ONESIGNAL_APP_ID")
|
||||
ONESIGNAL_REST_API_KEY = env.str("ONESIGNAL_REST_API_KEY")
|
||||
ONESIGNAL_USER_AUTH_KEY = env.str("ONESIGNAL_USER_AUTH_KEY")
|
||||
|
||||
|
||||
|
||||
# LOGGING
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/4.2/topics/logging/#logging
|
||||
@@ -260,7 +258,7 @@ LOGGING = {
|
||||
# jwt configuration
|
||||
# https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html#settings
|
||||
SIMPLE_JWT = {
|
||||
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(days=20),
|
||||
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(minutes=5),
|
||||
"REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=30),
|
||||
"ROTATE_REFRESH_TOKENS": False,
|
||||
"BLACKLIST_AFTER_ROTATION": False,
|
||||
@@ -283,8 +281,3 @@ SIMPLE_JWT = {
|
||||
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
|
||||
"JTI_CLAIM": "jti",
|
||||
}
|
||||
|
||||
|
||||
SOCIAL_AUTH_APPLE_CLIENT_ID = '<YOUR_APPLE_CLIENT_ID>'
|
||||
SOCIAL_AUTH_APPLE_CLIENT_SECRET = '<YOUR_APPLE_CLIENT_SECRET>'
|
||||
SOCIAL_AUTH_APPLE_REDIRECT_URI = '<YOUR_APPLE_REDIRECT_URI>'
|
||||
@@ -5,14 +5,14 @@ import colorlog
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ["staging.eatwithdigest.com"]
|
||||
|
||||
# CORS_ALLOWED_ORIGINS = [
|
||||
# "http://127.0.0.1:3000",
|
||||
# ]
|
||||
|
||||
|
||||
# CORS_ORIGIN_ALLOW_ALL = True
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
|
||||
|
||||
# CORS_ORIGIN_WHITELIST = ("http://localhost:3000",)
|
||||
@@ -69,7 +69,7 @@ LOGGING = {
|
||||
},
|
||||
}
|
||||
|
||||
BASE_DOMAIN = ""
|
||||
BASE_DOMAIN = "https://staging.eatwithdigest.com"
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
"""
|
||||
Django settings for module_project project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.2.5.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-#7rdu=fr58ba9_!n3$l5pm!xs8l%6%8xt@vb8$&o@hqhd@rtd%'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'module_project.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'module_project.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
@@ -23,8 +23,8 @@ urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
path('iam/', include('module_iam.urls')),
|
||||
|
||||
path('auth/', include('module_auth.urls')),
|
||||
|
||||
path('', include('module_auth.urls')),
|
||||
path('api/auth/', include('module_auth.api.urls')),
|
||||
|
||||
path('cms/', include('module_cms.urls')),
|
||||
|
||||
@@ -8,6 +8,11 @@ https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append('/var/www/testing_django/testing')
|
||||
sys.path.append('/var/www/testing_django/testing/testing')
|
||||
sys.path.append('/var/www/testing_django/testing/venv/lib/python3.11/site-packages')
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<small class="form-text text-muted">{{ field.help_text }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% elif field.field.widget.input_type == 'checkbox' %}
|
||||
<div class="form-group mb-3">
|
||||
<div class="">
|
||||
@@ -67,4 +68,4 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
|
||||
<title>Nifty11 </title>
|
||||
<title>Digest</title>
|
||||
{% load static %}
|
||||
<link rel="icon" type="image/x-icon" href="../src/assets/img/favicon.ico"/>
|
||||
<link href="../layouts/collapsible-menu/css/light/loader.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<p><strong>{{ code }}</strong></p>
|
||||
<p>If you didn't request a password reset, you can safely ignore this email.</p>
|
||||
<p>Thank you,</p>
|
||||
<p>The Support Team</p>
|
||||
<p>Team Digest</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -173,8 +173,8 @@ function initializeDataTable(tableName, mainUrl) {
|
||||
function renderRole(data, type, row) {
|
||||
if (type === 'display' && row.roles) {
|
||||
let html = '<ul>';
|
||||
for (const [name] of Object.entries(row.roles)) {
|
||||
html += `<li class="mb-1"><span class="badge badge-primary">${name}</span></li>`;
|
||||
for (const [index, role] of Object.entries(row.roles)) {
|
||||
html += `<li class="mb-1"><span class="badge badge-primary">${role.name}</span></li>`;
|
||||
}
|
||||
html += '</ul>';
|
||||
return html;
|
||||
|
||||
48
templates/module_iam/iam_principal_add.html
Normal file
48
templates/module_iam/iam_principal_add.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends 'base_structure/layout/base_template.html' %}
|
||||
{% load static %}
|
||||
{% block stylesheet %}
|
||||
<!-- include required css cdn link through html here -->
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row layout-top-spacing">
|
||||
<div class="col-lg-12">
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<h3>{{operation}} Principal</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<button class="btn btn-dark mb-2 me-4" onclick="history.back()">
|
||||
<i class="fa fa-arrow-left"></i>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row layout-spacing">
|
||||
<div class="col-lg-12">
|
||||
<div class="statbox widget box box-shadow">
|
||||
<div class="widget-content widget-content-area">
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% include 'base_structure/includes/dynamic_template_form.html' with form=form %}
|
||||
<div class="mt-4 mb-0">
|
||||
<div class="d-grid"><button class="btn btn-primary btn-block" type="submit">Submit</button></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block javascript %}
|
||||
<!-- include required js cdn link through html here -->
|
||||
|
||||
{% endblock %}
|
||||
@@ -56,21 +56,33 @@
|
||||
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th class="sorting_asc text-center" tabindex="0"
|
||||
aria-controls="style-3" aria-sort="ascending"
|
||||
style="width: 50.2656px;">#</th>
|
||||
<th class="sorting text-center" tabindex="1" aria-controls="style-3"
|
||||
colspan="1"
|
||||
style="width: 44.2344px;">Time</th>
|
||||
<th class="sorting text-center" tabindex="2" aria-controls="style-3"
|
||||
colspan="1"
|
||||
style="width: 44.2344px;">Meal</th>
|
||||
<th class="sorting text-center" tabindex="3" aria-controls="style-3"
|
||||
style="width: 79.7969px;">Medication</th>
|
||||
<th class="sorting text-center" tabindex="4" aria-controls="style-3"
|
||||
style="width: 79.7969px;">Bowel Movement</th>
|
||||
<th class="sorting text-center" tabindex="5" aria-controls="style-3"
|
||||
style="width: 79.7969px;">Symptoms</th>
|
||||
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
||||
aria-label=" Record Id : activate to sort column descending"
|
||||
style="width: 69.2656px;"> Id </th>
|
||||
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
||||
aria-label=" Record Id : activate to sort column descending"
|
||||
style="width: 69.2656px;"> Id </th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
|
||||
colspan="1" aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 44.2344px;">Name</th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
|
||||
colspan="1" aria-label="Email: activate to sort column ascending"
|
||||
style="width: 44.2344px;">Email</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="Permission: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Permission</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Pricipal_type</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Active</th>
|
||||
<th class="text-center dt-no-sorting sorting" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="Action: activate to sort column ascending"
|
||||
style="width: 51.625px;">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -100,17 +112,30 @@
|
||||
<tr role="row">
|
||||
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
||||
style="width: 69.2656px;"> Record Id </th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
style="width: 79.7969px;">Name</th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
style="width: 143.516px;">Email</th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
style="width: 143.516px;">Permission</th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
style="width: 98.875px;">Principal Type</th>
|
||||
<th class="text-center dt-no-sorting" tabindex="0"
|
||||
aria-label=" Record Id : activate to sort column descending"
|
||||
style="width: 69.2656px;"> Id </th>
|
||||
<th class="checkbox-column text-center sorting_asc" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1" aria-sort="ascending"
|
||||
aria-label=" Record Id : activate to sort column descending"
|
||||
style="width: 69.2656px;"> Id </th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
|
||||
colspan="1" aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 44.2344px;">Name</th>
|
||||
<th class="text-center sorting" tabindex="0" aria-controls="style-3" rowspan="1"
|
||||
colspan="1" aria-label="Email: activate to sort column ascending"
|
||||
style="width: 44.2344px;">Email</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="Permission: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Groups</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Pricipal_type</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="First Name: activate to sort column ascending"
|
||||
style="width: 79.7969px;">Active</th>
|
||||
<th class="text-center dt-no-sorting sorting" tabindex="0"
|
||||
aria-controls="style-3" rowspan="1" colspan="1"
|
||||
aria-label="Action: activate to sort column ascending"
|
||||
style="width: 51.625px;">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -153,26 +178,42 @@
|
||||
|
||||
// Define DataTable instance
|
||||
var dataTableInstance;
|
||||
var mainUrl = "{% url 'module_iam:role_list' %}?deleted_flag=False"
|
||||
var editUrl = "{% url 'module_iam:role_edit' pk=0 %}"
|
||||
var actionUrl = "{% url 'module_iam:role_action' %}"
|
||||
var adminMainUrl = "{% url 'module_iam:principal_group_link_list' %}?deleted_flag=False"
|
||||
var editUrl = "{% url 'module_iam:principal_edit' pk=0 %}"
|
||||
var actionUrl = "{% url 'module_iam:principal_group_link_action' %}"
|
||||
|
||||
|
||||
// Entry point
|
||||
$(document).ready(function() {
|
||||
|
||||
const table1Settings = {
|
||||
tableId: '#table',
|
||||
MainUrl: "{% url 'module_iam:principal_group_link_list' %}?deleted_flag=False",
|
||||
actionUrl: "{% url 'module_iam:principal_group_link_action' %}",
|
||||
editUrl: "{% url 'module_iam:principal_edit' pk=0 %}"
|
||||
};
|
||||
|
||||
tableName = $('#table');
|
||||
dataTableInstance = initializeDataTable(tableName, mainUrl);
|
||||
viewClickEvent(dataTableInstance)
|
||||
activeSwitchEventListener()
|
||||
const table2Settings = {
|
||||
tableId: '#table2',
|
||||
MainUrl: "{% url 'module_iam:principal_group_link_list_sub' %}?deleted_flag=False",
|
||||
actionUrl: "{% url 'module_iam:principal_group_link_action' %}",
|
||||
editUrl: "{% url 'module_iam:principal_group_link_edit' pk=0 %}"
|
||||
};
|
||||
|
||||
dataTableInstance = initializeDataTable(table1Settings);
|
||||
activeSwitchEventListener(dataTableInstance);
|
||||
|
||||
dataTable2Instance = initialize2DataTable(table2Settings);
|
||||
activeSwitchEventListener(dataTable2Instance);
|
||||
});
|
||||
|
||||
// Function to initialize DataTable
|
||||
function initializeDataTable(tableName, mainUrl) {
|
||||
return tableName.DataTable({
|
||||
function initializeDataTable(tableSettings) {
|
||||
return $(tableSettings.tableId).DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
url: mainUrl,
|
||||
url: tableSettings.MainUrl,
|
||||
type: "GET",
|
||||
},
|
||||
columns: [
|
||||
@@ -186,9 +227,13 @@ function initializeDataTable(tableName, mainUrl) {
|
||||
return `<ul><span class="badge badge-success">All Access Permission</span></ul>`
|
||||
}
|
||||
},
|
||||
{ data: "principal_type__name", className: "text-center"},
|
||||
{ data: "principal_type_name", className: "text-center" },
|
||||
{ data: "is_active", className: "text-center", render: renderSwitch },
|
||||
{ data: null, className: "text-center", render: renderActions }
|
||||
{ data: null, className: "text-center",
|
||||
render: (data, type, row) => {
|
||||
return renderActions(data, type, row, tableSettings.editUrl);
|
||||
}
|
||||
}
|
||||
],
|
||||
debug: true,
|
||||
columnDefs: [
|
||||
@@ -215,7 +260,9 @@ function initializeDataTable(tableName, mainUrl) {
|
||||
{
|
||||
text: 'Archive',
|
||||
className: "btn btn-dark buttons-archive",
|
||||
action: archiveAction,
|
||||
action: function (e, dt, node, config) {
|
||||
archiveAction(e, dt, node, config, tableSettings);
|
||||
},
|
||||
init: function(api, node, config){
|
||||
$(node).hide();
|
||||
}
|
||||
@@ -243,28 +290,106 @@ function initializeDataTable(tableName, mainUrl) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderResources(data, type, row) {
|
||||
if (type === 'display' && row.resources) {
|
||||
let html = '<ul>';
|
||||
for (const [resource, actions] of Object.entries(row.resources)) {
|
||||
html += `<li class="mb-1"><span class="badge badge-primary">${resource}</span>`;
|
||||
for (const action of actions) {
|
||||
html += `<span class="badge badge-secondary">${action}</span>`;
|
||||
|
||||
// Function to initialize DataTable
|
||||
function initialize2DataTable(tableSettings) {
|
||||
return $(tableSettings.tableId).DataTable({
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
ajax: {
|
||||
url: tableSettings.MainUrl,
|
||||
type: "GET",
|
||||
},
|
||||
columns: [
|
||||
{ data: null, className: "text-center", render: renderCheckbox },
|
||||
{ data: "id", className: "text-center" },
|
||||
{ data: "first_name" },
|
||||
{ data: "email" },
|
||||
{
|
||||
data: "groups",
|
||||
render: renderGroups
|
||||
},
|
||||
{ data: "principal_type_name", className: "text-center" },
|
||||
{ data: "is_active", className: "text-center", render: renderSwitch },
|
||||
{ data: null, className: "text-center",
|
||||
render: (data, type, row) => {
|
||||
return renderActions(data, type, row, tableSettings.editUrl);
|
||||
}
|
||||
}
|
||||
html += '</li>';
|
||||
],
|
||||
debug: true,
|
||||
columnDefs: [
|
||||
{
|
||||
targets: [1, 2],
|
||||
searchable: true,
|
||||
orderable: true
|
||||
},
|
||||
{
|
||||
targets: [3],
|
||||
searchable: true,
|
||||
orderable: false
|
||||
},
|
||||
{
|
||||
targets: [0,-1], // Targeting the last column (action column)
|
||||
searchable: false,
|
||||
orderable: false
|
||||
},
|
||||
],
|
||||
dom: "<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'Bf>>>" +
|
||||
"<'table-responsive'tr>" +
|
||||
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
|
||||
buttons: [
|
||||
{
|
||||
text: 'Archive',
|
||||
className: "btn btn-dark buttons-archive",
|
||||
action: function (e, dt, node, config) {
|
||||
archiveAction(e, dt, node, config, tableSettings);
|
||||
},
|
||||
init: function(api, node, config){
|
||||
$(node).hide();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'View Archive List',
|
||||
className: "btn btn-dark ",
|
||||
action: function () {
|
||||
// Add your action here, e.g., redirect to archive page
|
||||
window.location.href = '/archive';
|
||||
}
|
||||
}
|
||||
],
|
||||
oLanguage: {
|
||||
oPaginate: { "sPrevious": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>', "sNext": '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>' },
|
||||
sInfo: "Showing page _PAGE_ of _PAGES_",
|
||||
sSearch: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>',
|
||||
sSearchPlaceholder: "Search...",
|
||||
sLengthMenu: " _MENU_",
|
||||
},
|
||||
stripeClasses: [],
|
||||
lengthMenu: [5, 10, 20, 50],
|
||||
pageLength: 10,
|
||||
initComplete: initCompleteCallback
|
||||
});
|
||||
}
|
||||
|
||||
function renderGroups(data, type, row) {
|
||||
if (type === 'display' && row.groups) {
|
||||
let html = '<ul>';
|
||||
for (const [index, group] of Object.entries(row.groups)) {
|
||||
html += `<li class="mb-1"><span class="badge badge-warning">${group.name}</span></li>`;
|
||||
}
|
||||
html += '</ul>';
|
||||
return html;
|
||||
} else if (type === 'display' && !row.resources) {
|
||||
return '<span class="badge badge-danger">No Permission assigned</span>';
|
||||
} else if (type === 'display' && !row.groups) {
|
||||
return '<span class="badge badge-danger">No Group assigned</span>';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Function to reload the DataTable
|
||||
function reloadDataTable() {
|
||||
dataTableInstance.ajax.reload();
|
||||
function reloadDataTable(tableInstance) {
|
||||
tableInstance.ajax.reload();
|
||||
}
|
||||
|
||||
// Render checkbox
|
||||
@@ -286,7 +411,7 @@ function renderSwitch(data, type, row) {
|
||||
}
|
||||
|
||||
// Render actions
|
||||
function renderActions(data, type, row) {
|
||||
function renderActions(data, type, row, editUrl) {
|
||||
return `
|
||||
<ul class="table-controls">
|
||||
<li>
|
||||
@@ -299,15 +424,30 @@ function renderActions(data, type, row) {
|
||||
</ul>`;
|
||||
}
|
||||
|
||||
// Callback function for DataTable initialization complete event
|
||||
function initCompleteCallback(settings) {
|
||||
var tableId = '#' + settings.sTableId; // Get the ID of the current table
|
||||
var api = this.api();
|
||||
|
||||
// Add event listener for checkbox change
|
||||
$(document).on('change', tableId + ' input[type="checkbox"]', function () {
|
||||
var checkedCount = $(tableId + ' tbody input.archive-checkbox:checked').length;
|
||||
var archiveButton = $(' .buttons-archive');
|
||||
console.log("checkbox is checked", checkedCount);
|
||||
archiveButton.toggle(checkedCount > 0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Function to handle archive action
|
||||
function archiveAction() {
|
||||
function archiveAction(e, dt, node, config, tableSettings) {
|
||||
// Get all the checked checkboxes
|
||||
var checkedCheckboxes = $('.archive-checkbox:checked');
|
||||
var checkedCheckboxes = dt.$('.archive-checkbox:checked');
|
||||
// If no checkboxes are checked, show an error message
|
||||
if (checkedCheckboxes.length === 0) {
|
||||
Swal.fire({
|
||||
title: 'No users selected',
|
||||
text: 'Please select at least one user to archive.',
|
||||
title: 'No Record selected',
|
||||
text: 'Please select at least one Record to archive.',
|
||||
icon: 'error',
|
||||
showConfirmButton: true
|
||||
});
|
||||
@@ -330,7 +470,7 @@ function archiveAction() {
|
||||
if (result.isConfirmed) {
|
||||
// Perform archive action
|
||||
$.ajax({
|
||||
url: actionUrl, // Replace with your archive endpoint
|
||||
url: tableSettings.actionUrl, // Replace with your archive endpoint
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: "archive",
|
||||
@@ -346,7 +486,7 @@ function archiveAction() {
|
||||
showConfirmButton: true
|
||||
});
|
||||
// Optionally, you can reload the DataTable after successful archive
|
||||
reloadDataTable();
|
||||
reloadDataTable(dt);
|
||||
},
|
||||
error: function(response) {
|
||||
// Show error message
|
||||
@@ -362,33 +502,12 @@ function archiveAction() {
|
||||
});
|
||||
}
|
||||
|
||||
// Callback function for DataTable initialization complete event
|
||||
function initCompleteCallback() {
|
||||
var api = this.api();
|
||||
|
||||
// Add event listener for checkbox change
|
||||
$('body').on('change', 'input[type="checkbox"]', function () {
|
||||
var checkedCount = $('tbody input.archive-checkbox:checked').length;
|
||||
var archiveButton = $('.buttons-archive');
|
||||
console.log("checkbox is checked", + checkedCount)
|
||||
archiveButton.toggle(checkedCount > 0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Function to handle click event for view button
|
||||
function viewClickEvent(dataTableInstance) {
|
||||
$('body').on('click', '.view', function(){
|
||||
var id =$(this).data('id');
|
||||
var rowData = dataTableInstance.row($(this).closest('tr')).data();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Function to add event listener for switch
|
||||
function activeSwitchEventListener() {
|
||||
function activeSwitchEventListener(tableInstance) {
|
||||
// Add event listener for switch change event
|
||||
$('body').on('change', '.switch-input', function() {
|
||||
tableInstance.on('change', '.switch-input', function() {
|
||||
var rowId = $(this).closest('tr').find('.switch-input').data('id');
|
||||
var isActive = $(this).prop('checked');
|
||||
console.log(rowId, isActive)
|
||||
|
||||
64
templates/module_iam/iam_principal_group_link_edit.html
Normal file
64
templates/module_iam/iam_principal_group_link_edit.html
Normal file
@@ -0,0 +1,64 @@
|
||||
{% extends 'base_structure/layout/base_template.html' %}
|
||||
{% load static %}
|
||||
{% block stylesheet %}
|
||||
<!-- include required css cdn link through html here -->
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row layout-top-spacing">
|
||||
<div class="col-lg-12">
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<h3>{{operation}} Principal Group Link</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<button class="btn btn-dark mb-2 me-4" onclick="history.back()">
|
||||
<i class="fa fa-arrow-left"></i>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row layout-spacing">
|
||||
<div class="col-lg-12">
|
||||
<div class="statbox widget box box-shadow">
|
||||
<div class="widget-content widget-content-area">
|
||||
|
||||
<form method="post" autocomplete="off">
|
||||
{% csrf_token %}
|
||||
{% include 'base_structure/includes/dynamic_template_form.html' with form=form %}
|
||||
<div class="mt-4 mb-0">
|
||||
<div class="d-grid"><button class="btn btn-primary btn-block" type="submit">Submit</button></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block javascript %}
|
||||
<!-- include required js cdn link through html here -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.js-example-basic-multiple').select2({
|
||||
placeholder: 'Select options',
|
||||
allowClear: true,
|
||||
tags: true, // Allow the user to enter custom tags
|
||||
tokenSeparators: [',', ' '], // Customize token separators
|
||||
closeOnSelect: false // Keep the dropdown open after selection
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user