I am principal module completed and added website and tags

This commit is contained in:
rizwanisready
2024-04-08 18:49:26 +05:30
parent 6050b4f7d1
commit 7d760361da
21 changed files with 477 additions and 71 deletions

View File

@@ -1,27 +1,27 @@
from accounts import resource_action
from django.core.cache import cache
CACHE_KEY_RESOURCE_ACTION_CONSTANTS = 'resource_action_constants'
# CACHE_KEY_RESOURCE_ACTION_CONSTANTS = 'resource_action_constants'
def resource_action_constants(request):
# Add debugging output
print("Resource and action constants context processor is executing.")
# Try to retrieve the constants from the cache
resource_action_constants = cache.get(CACHE_KEY_RESOURCE_ACTION_CONSTANTS)
# print(f"The value of '{CACHE_KEY_RESOURCE_ACTION_CONSTANTS}' in the cache is: {resource_action_constants}")
if resource_action_constants is None:
# Compute the constants if not found in the cache
resource_action_constants = compute_resource_action_constants()
print()
# def resource_action_constants(request):
# # Add debugging output
# print("Resource and action constants context processor is executing.")
# # Try to retrieve the constants from the cache
# resource_action_constants = cache.get(CACHE_KEY_RESOURCE_ACTION_CONSTANTS)
# # print(f"The value of '{CACHE_KEY_RESOURCE_ACTION_CONSTANTS}' in the cache is: {resource_action_constants}")
# if resource_action_constants is None:
# # Compute the constants if not found in the cache
# resource_action_constants = compute_resource_action_constants()
# print()
# Store the constants in the cache with a timeout (e.g., 3600 seconds for 1 hour)
cache.set(CACHE_KEY_RESOURCE_ACTION_CONSTANTS, resource_action_constants, 3600)
# # Store the constants in the cache with a timeout (e.g., 3600 seconds for 1 hour)
# cache.set(CACHE_KEY_RESOURCE_ACTION_CONSTANTS, resource_action_constants, 3600)
return {
'resource_context': resource_action_constants,
}
# return {
# 'resource_context': resource_action_constants,
# }
def compute_resource_action_constants():
def compute_resource_action_constants(request):
constants_dict = {
'ACTION_CREATE': resource_action.ACTION_CREATE,
'ACTION_READ': resource_action.ACTION_READ,
@@ -49,4 +49,6 @@ def compute_resource_action_constants():
'RESOURCE_IAM_GROUP': resource_action.RESOURCE_IAM_GROUP,
'RESOURCE_IAM_ROLE': resource_action.RESOURCE_IAM_ROLE,
}
return constants_dict
return {
'resource_context': constants_dict,
}

View File

@@ -319,3 +319,40 @@ class IAmPrincipalRoleAppResourceActionLinkForm(forms.ModelForm):
if instance is None:
self.fields.pop("active")
class IAmPrincipalResourceLinkForm(IAmPrincipalForm):
class Meta:
model = models.IAmPrincipal
fields = [
"principal_type",
# "first_name",
# "last_name",
"email",
# "password",
# "confirm_password",
"principal_resource",
]
principal_resource = forms.ModelMultipleChoiceField(
label="Module Permission",
queryset=models.IAmAppResource.objects.filter(active=True, deleted=False),
required=False,
widget=forms.widgets.SelectMultiple(
attrs={"class": "form_select js-example-basic-multiple"}
),
)
def save(self, commit=True):
# First, save the instance of the IAmPrincipal model as usual
principal = super().save(commit=False)
# If the principal_resource field has data
if self.cleaned_data['principal_resource']:
# Get the principal_resource data
principal_resource_data = self.cleaned_data['principal_resource']
# Update the many-to-many relationship
principal.principal_resource.set(principal_resource_data)
# Save the instance to the database
if commit:
principal.save()

View File

@@ -0,0 +1,57 @@
# Generated by Django 5.0.2 on 2024-04-08 10:13
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("accounts", "0006_iamprincipal_facebook_profile_and_more"),
]
operations = [
migrations.CreateModel(
name="IAmPrincipalResourceLink",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"principal",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="principal_resource_link_principal",
to=settings.AUTH_USER_MODEL,
),
),
(
"principal_resource",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="principal_resource_link_resource",
to="accounts.iamappresource",
),
),
],
options={
"db_table": "iam_principal_resource_group_link",
},
),
migrations.AddField(
model_name="iamprincipal",
name="principal_resource",
field=models.ManyToManyField(
related_name="principal_resources",
through="accounts.IAmPrincipalResourceLink",
to="accounts.iamappresource",
),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-04-08 11:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("accounts", "0007_iamprincipalresourcelink_and_more"),
]
operations = [
migrations.AddField(
model_name="iamprincipal",
name="website",
field=models.URLField(blank=True, null=True, verbose_name="Website"),
),
]

View File

@@ -239,6 +239,11 @@ class IAmPrincipal(AbstractUser):
on_delete=models.CASCADE,
null=True,
)
principal_resource = models.ManyToManyField(
IAmAppResource,
through="IAmPrincipalResourceLink",
related_name="principal_resources",
)
email = models.EmailField(unique=True)
gender = models.CharField(max_length=5, blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
@@ -309,6 +314,12 @@ class IAmPrincipal(AbstractUser):
blank=True,
null=True,
)
website = models.URLField(
verbose_name="Website",
max_length=200,
blank=True,
null=True,
)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
@@ -322,6 +333,22 @@ class IAmPrincipal(AbstractUser):
return f"{self.email}"
class IAmPrincipalResourceLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,
related_name="principal_resource_link_principal",
on_delete=models.CASCADE,
)
principal_resource = models.ForeignKey(
IAmAppResource,
related_name="principal_resource_link_resource",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_principal_resource_group_link"
class IAmPrincipalGroupLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,

View File

@@ -3,18 +3,74 @@ from django.core.exceptions import PermissionDenied
from . import models
from django.db.models import Q
from rest_framework import permissions
# import logging
# logger = logging.getLogger(__name__)
class CustomPermissionRequiredMixin:
resource = None
action = None
# class CustomPermissionRequiredMixin:
# resource = None
# action = None
def has_custom_permission(self, user, resource, action):
if not self.resource or not self.action:
raise AttributeError("Resource and action attributes must be defined in the view")
# def has_custom_permission(self, user, resource, action):
# if not self.resource or not self.action:
# raise AttributeError("Resource and action attributes must be defined in the view")
# # if not request.user.is_authenticated:
# # return self.handle_no_permission()
# if user.is_superuser: # will chagne to principal type for admin
# return True
# permission_query = Q(
# principal_group__role__app_resource_action__app_resource__name=resource,
# principal_group__role__app_resource_action__app_action__name=action
# )
# return models.IAmPrincipal.objects.filter(permission_query, id=user.id).exists()
# def dispatch(self, request, *args, **kwargs):
# if not self.has_custom_permission(request.user, self.resource, self.action):
# # logger.warning(f"Permission denied for user {request.user} accessing {self.resource}:{self.action}")
# raise PermissionDenied("You do not have permission to access this resource.")
# return super().dispatch(request, *args, **kwargs)
# @classmethod
# def as_decorator(cls, resource, action):
# def decorator(view_func):
# @wraps(view_func)
# def _wrapped_view(request, *args, **kwargs):
# instance = cls()
# instance.resource = resource
# instance.action = action
# if not instance.has_custom_permission(request.user, instance.resource, instance.action):
# raise PermissionDenied("You do not have permission to access this resource.")
# return view_func(request, *args, **kwargs)
# return _wrapped_view
# return decorator
# class IsOwnerOrReadOnly(permissions.BasePermission):
# """
# Custom permission to only allow owners of an object to edit it.
# """
# def has_object_permission(self, request, view, obj):
# # Read permissions are allowed to any request,
# # so we'll always allow GET, HEAD or OPTIONS requests.
# if request.method in permissions.SAFE_METHODS:
# return True
# # Write permissions are only allowed to the owner of the object.
# return obj.created_by == request.user
class ResourcePermissionRequiredMixin:
resource = None
def has_resource_permission(self, user, resource):
# if not self.resource or resource:
# raise AttributeError("Resource attributes must be defined in the view")
# if not request.user.is_authenticated:
# return self.handle_no_permission()
@@ -23,42 +79,33 @@ class CustomPermissionRequiredMixin:
return True
permission_query = Q(
principal_group__role__app_resource_action__app_resource__name=resource,
principal_group__role__app_resource_action__app_action__name=action
principal_resource__name=resource,
)
return models.IAmPrincipal.objects.filter(permission_query, id=user.id).exists()
def dispatch(self, request, *args, **kwargs):
if not self.has_custom_permission(request.user, self.resource, self.action):
if not self.has_resource_permission(request.user, self.resource):
# logger.warning(f"Permission denied for user {request.user} accessing {self.resource}:{self.action}")
raise PermissionDenied("You do not have permission to access this resource.")
raise PermissionDenied(
"You do not have permission to access this resource."
)
return super().dispatch(request, *args, **kwargs)
@classmethod
def as_decorator(cls, resource, action):
def as_decorator(cls, resource):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
instance = cls()
instance.resource = resource
instance.action = action
if not instance.has_custom_permission(request.user, instance.resource, instance.action):
raise PermissionDenied("You do not have permission to access this resource.")
if not instance.has_resource_permission(
request.user, instance.resource
):
raise PermissionDenied(
"You do not have permission to access this resource."
)
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the object.
return obj.created_by == request.user

View File

@@ -1,11 +1,35 @@
from django import template
from accounts.permission import CustomPermissionRequiredMixin
from accounts.permission import ResourcePermissionRequiredMixin
register = template.Library()
# @register.filter(name='has_resource_permission')
# def has_resource_permission(user, resource_action):
# """
# Check if a user has a specific resource and action permission.
# Args:
# user (User): The user to check for permission.
# resource_action (str): The resource and action string (e.g., "resource_name.action_name").
# Returns:
# bool: True if the user has the specified permission, False otherwise.
# Example usage in a template:
# {% if user|has_resource_permission:"article.add" %}
# <!-- Render content for users with permission -->
# {% else %}
# <!-- Render content for users without permission -->
# {% endif %}
# """
# resource, action = resource_action.split(".")
# return CustomPermissionRequiredMixin().has_custom_permission(user, resource, action)
@register.filter(name='has_resource_permission')
def has_resource_permission(user, resource_action):
def has_resource_permission(user, resource):
"""
Check if a user has a specific resource and action permission.
@@ -17,11 +41,11 @@ def has_resource_permission(user, resource_action):
bool: True if the user has the specified permission, False otherwise.
Example usage in a template:
{% if user|has_resource_permission:"article.add" %}
{% if user|has_resource_permission:"article" %}
<!-- Render content for users with permission -->
{% else %}
<!-- Render content for users without permission -->
{% endif %}
"""
resource, action = resource_action.split(".")
return CustomPermissionRequiredMixin().has_custom_permission(user, resource, action)
# resource, action = resource_action.split(".")
return ResourcePermissionRequiredMixin().has_resource_permission(user, resource)

View File

@@ -24,6 +24,8 @@ urlpatterns = [
path('principal/add/', views.PrincipalCreateOrUpdateView.as_view(), name="principal_add"),
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/resource/permission/edit/<int:pk>/', views.PrincipalResourcePermissionEditView.as_view(),
name="principal_resource_permission_edit"),
path('principal/group/link/', views.PrincipalGroupLinkListView.as_view(), name="principal_group_link_list"),
path('principal/group/link/edit/<int:pk>/', views.PrincipalGroupLinkEditView.as_view(), name="principal_group_link_edit"),

View File

@@ -18,6 +18,7 @@ from django.urls import reverse_lazy
from django.views import generic
from django.db import models, transaction, IntegrityError
from accounts import permission
from goodtimes import constants
from . import resource_action
@@ -25,6 +26,7 @@ from .forms import (
CustomAuthenticationForm,
IAmPrincipalForm,
IAmPrincipalGroupRoleLinkForm,
IAmPrincipalResourceLinkForm,
IAmPrincipalRoleAppResourceActionLinkForm,
IAmPrincipalGroupLinkForm,
ProfileEditForm,
@@ -229,6 +231,45 @@ class PrincipalCreateOrUpdateView(LoginRequiredMixin, generic.View):
return render(request, template_name=self.template_name, context=context)
class PrincipalResourcePermissionEditView(permission.ResourcePermissionRequiredMixin, LoginRequiredMixin, generic.View):
page_name = resource_action.RESOURCE_IAM_PRINCIPAL
resource = resource_action.RESOURCE_IAM_PRINCIPAL_GROUP
model = IAmPrincipal
template_name = "accounts/iam_module/iam_principal_resource_permission_edit.html"
form_class = IAmPrincipalResourceLinkForm
success_url = reverse_lazy("accounts:principal_group_link_list")
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)
"""Principal Group Link"""

View File

@@ -343,7 +343,9 @@ class PaymentProcessingService:
)
def _update_transaction_success(self):
principal_subscription = self._get_principal_subscription_by_id()
self.transaction.transaction_status = TransactionStatus.SUCCESS
self.transaction.principal_subscription = principal_subscription.id
self.transaction.save()
def _update_transaction_failure(self):
@@ -409,6 +411,7 @@ class EventFilterService:
filtered_events = filtered_events.filter(
Q(title__icontains=search_query)
| Q(key_guest__icontains=search_query)
| Q(tags__name__in=[search_query])
| Q(venue__address__icontains=search_query)
)

View File

@@ -115,7 +115,7 @@ TEMPLATES = [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"accounts.context_processors.resource_action_constants", # resource action context processor
"accounts.context_processors.compute_resource_action_constants", # resource action context processor
],
},
},

View File

@@ -31,6 +31,7 @@ class EventAdmin(admin.ModelAdmin):
"end_date",
"venue",
"status",
# "tags",
"draft",
"deleted",
"active",

View File

@@ -1,5 +1,8 @@
from rest_framework import serializers
from taggit_serializer.serializers import TagListSerializerField
from accounts.models import IAmPrincipalLocation
from manage_cms.api.serializers import TagSerializer
from manage_events.utils import get_location_info
from accounts.api.serializers import ProfileSerializer
from manage_events.models import (
@@ -43,6 +46,7 @@ class EventCategorySerializer(serializers.ModelSerializer):
class EventDetailSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
images = serializers.SerializerMethodField()
venue = VenueSerializer(read_only=True) # Use VenueSerializer for the venue field
category = EventCategorySerializer(read_only=True)
@@ -72,6 +76,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
"images",
"is_favorited",
"reviews",
"tags",
"principal_interaction",
]
@@ -109,6 +114,7 @@ class EventDetailSerializer(serializers.ModelSerializer):
class CreateEventSerializer(serializers.ModelSerializer):
tags = TagListSerializerField()
images = serializers.ListField(
child=serializers.ImageField(), write_only=True, required=False
)
@@ -134,18 +140,21 @@ class CreateEventSerializer(serializers.ModelSerializer):
"age_group",
"draft",
"venue",
"tags",
]
def create(self, validated_data):
tags = validated_data.pop('tags')
images_data = validated_data.pop("images", None)
event = Event.objects.create(**validated_data)
event.tags.add(*tags)
if images_data:
for image_data in images_data:
EventImage.objects.create(event=event, image=image_data)
return event
class CreateVenueSerializer(serializers.ModelSerializer):
class Meta:

View File

@@ -1,12 +1,9 @@
from datetime import timedelta
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status, generics, mixins
from rest_framework.views import APIView
from django.conf import settings
from accounts.models import IAmPrincipalLocation
from accounts.permission import IsOwnerOrReadOnly
from goodtimes import constants
from django.db.models import Q
from django.utils.dateparse import parse_date
@@ -40,7 +37,7 @@ from manage_events.models import (
)
import requests
from manage_events.utils import filter_events_by_location, haversine_one
from manage_events.utils import haversine_one
class CreateEventApi(APIView):
@@ -48,6 +45,7 @@ class CreateEventApi(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
print("Data: ", request.data)
serializer = CreateEventSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@@ -160,6 +158,7 @@ class EventsAPIView(APIView):
"tomorrow",
"category",
"key_guest",
"tags",
]
if filter not in params:
return ApiResponse.error(
@@ -177,6 +176,10 @@ class EventsAPIView(APIView):
events = services.EventFilterService.filter_events_by_search(
search_query=query
)
elif filter == "tags":
events = services.EventFilterService.filter_events_by_search(
search_query=query
)
elif filter == "category" and category_id is not None:
events = services.EventFilterService.filter_events_by_category(
int(category_id)
@@ -335,7 +338,8 @@ class VenueListView(generics.ListAPIView):
class VenueDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Venue.objects.all()
serializer_class = VenueSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
# This ensures a user can only access their own venues

View File

@@ -36,6 +36,7 @@ class EventForm(forms.ModelForm):
"entry_fee",
"key_guest",
"age_group",
"tags",
"draft",
]
widgets = {

View File

@@ -0,0 +1,29 @@
# Generated by Django 5.0.2 on 2024-04-08 11:31
import taggit.managers
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("manage_events", "0006_alter_venue_latitude_alter_venue_longitude"),
(
"taggit",
"0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx",
),
]
operations = [
migrations.AddField(
model_name="event",
name="tags",
field=taggit.managers.TaggableManager(
blank=True,
help_text="A comma-separated list of tags.",
through="taggit.TaggedItem",
to="taggit.Tag",
verbose_name="Tags",
),
),
]

View File

@@ -1,6 +1,7 @@
from django.db import models
from accounts.models import BaseModel, IAmPrincipal
from django.db import transaction
from taggit.managers import TaggableManager
# from django.contrib.gis.db import models as gis_models
@@ -22,8 +23,12 @@ class Venue(BaseModel):
address = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to="venue", null=True, blank=True)
url = models.URLField(max_length=200, blank=True, null=True)
latitude = models.DecimalField(max_digits=14, decimal_places=8, blank=True, null=True)
longitude = models.DecimalField(max_digits=14, decimal_places=8, blank=True, null=True)
latitude = models.DecimalField(
max_digits=14, decimal_places=8, blank=True, null=True
)
longitude = models.DecimalField(
max_digits=14, decimal_places=8, blank=True, null=True
)
def __str__(self):
return self.title
@@ -76,6 +81,7 @@ class Event(BaseModel):
max_digits=14, decimal_places=2, default=0.00
) # Assuming it's an integer. Use DecimalField if you need to handle cents.
key_guest = models.TextField(blank=True, null=True)
tags = TaggableManager(blank=True)
age_group = models.CharField(max_length=100, blank=True, null=True)
draft = models.BooleanField(default=False)

View File

@@ -25,6 +25,7 @@ django-extensions==3.2.3
django-phonenumber-field==7.3.0
django-quill-editor==0.1.40
django-taggit==5.0.1
django-taggit-serializer==0.1.7
django-widget-tweaks==1.5.0
djangorestframework==3.14.0
djangorestframework-simplejwt==5.3.1
@@ -65,7 +66,7 @@ sqlparse==0.4.4
stripe==8.2.0
tqdm==4.66.2
Twisted==23.10.0
# twisted-iocpsupport==1.0.4
twisted-iocpsupport==1.0.4
txaio==23.1.1
typing_extensions==4.9.0
tzdata==2024.1

View File

@@ -128,7 +128,7 @@
<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;">Groups</th>
style="width: 143.516px;">Resource</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"
@@ -143,8 +143,8 @@
<td>{{ subadmin_principal.first_name }} {{ subadmin_principal.last_name }}</td>
<td>{{ subadmin_principal.email }}</td>
<td>
{% if subadmin_principal.principal_group.all %}
{% for group in subadmin_principal.principal_group.all %}
{% if subadmin_principal.principal_resource.all %}
{% for group in subadmin_principal.principal_resource.all %}
<span class="badge badge-warning ">{{ group.name }}</span>
{% endfor %}
{% else %}
@@ -154,7 +154,7 @@
<td class="text-center">{{ subadmin_principal.principal_type }}</td>
<td class="text-center">
<ul class="table-controls">
<li><a href="{% url 'accounts:principal_group_link_edit' subadmin_principal.id%}" class="bs-tooltip"
<li><a href="{% url 'accounts:principal_resource_permission_edit' pk=subadmin_principal.id %}" class="bs-tooltip"
data-bs-toggle="tooltip" data-bs-placement="top" title=""
data-original-title="Edit" data-bs-original-title="Edit"
aria-label="Edit"><svg xmlns="http://www.w3.org/2000/svg"

View File

@@ -0,0 +1,64 @@
{% extends '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 '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 %}

View File

@@ -1,5 +1,6 @@
<!-- BEGIN SIDEBAR -->
{% load static%}
{% load custom_permissions %}
<div class="sidebar-wrapper sidebar-theme">
<nav id="sidebar">
@@ -38,6 +39,7 @@
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg><span>APPLICATIONS</span></div>
</li> {% endcomment %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_DASHBOARD %}active{% endif %}">
<a href="{% url 'dashboard:main_dashboard'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -46,7 +48,9 @@
</div>
</a>
</li>
<!-- <li
{% if user|has_resource_permission:resource_context.RESOURCE_IAM_PRINCIPAL %}
<li
class="menu {% if page_name == resource_context.RESOURCE_IAM_PRINCIPAL or page_name == resource_context.RESOURCE_IAM_PRINCIPAL_GROUP or page_name == resource_context.RESOURCE_IAM_GROUP or page_name == resource_context.RESOURCE_IAM_ROLE %}active{% endif %}">
<a href="#iam" data-bs-toggle="collapse" aria-expanded="true" class="dropdown-toggle">
<div class="">
@@ -70,16 +74,18 @@
<a href="{% url 'accounts:principal_list'%}"> IAM Principal </a>
</li>
<li class="{% if page_name == resource_context.RESOURCE_IAM_PRINCIPAL_GROUP %}active{% endif %}">
<a href="{% url 'accounts:principal_group_link_list'%}"> IAM Principal Group </a>
<a href="{% url 'accounts:principal_group_link_list'%}"> IAM Principal Resource </a>
</li>
<li class="{% if page_name == resource_context.RESOURCE_IAM_GROUP %}active{% endif %}">
<!-- <li class="{% if page_name == resource_context.RESOURCE_IAM_GROUP %}active{% endif %}">
<a href="{% url 'accounts:principal_group_list'%}"> IAM Group </a>
</li>
<li class="{% if page_name == resource_context.RESOURCE_IAM_ROLE %}active{% endif %}">
<a href="{% url 'accounts:role_list'%}"> IAM Role </a>
</li>
</li> -->
</ul>
</li> -->
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_CUSTOMER %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_CUSTOMER %}active{% endif %}">
<a href="{% url 'accounts:customer_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="{% if page_name == RESOURCE_MANAGE_CUSTOMER %}active{% endif %}">
@@ -88,6 +94,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_WALLET %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_WALLET %}active{% endif %}">
<a href="{% url 'manage_wallets:wallet_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -98,6 +106,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_PAYMENT %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_PAYMENT %}active{% endif %}">
<a href="{% url 'manage_wallets:payment_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -106,6 +116,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_BANK_ACCOUNTS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_BANK_ACCOUNTS %}active{% endif %}">
<a href="{% url 'manage_wallets:account_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -114,6 +126,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_WITHDRAWALS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_WITHDRAWALS %}active{% endif %}">
<a href="{% url 'manage_wallets:withdrawal_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -122,6 +136,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_SUBSCRIPTIONS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_SUBSCRIPTIONS %}active{% endif %}">
<a href="{% url 'manage_subscriptions:subscription_list'%}" aria-expanded="false"
class="dropdown-toggle">
@@ -131,6 +147,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_PRINCIPAL_SUBSCRIPTIONS %}
<li class="menu {% if page_name == resource_context.RESOURCE_PRINCIPAL_SUBSCRIPTIONS %}active{% endif %}">
<a href="{% url 'manage_subscriptions:principal_subscriptions_list'%}" aria-expanded="false"
class="dropdown-toggle">
@@ -140,6 +158,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_REFERRALS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_REFERRALS %}active{% endif %}">
<a href="{% url 'manage_referrals:code_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -148,6 +168,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_VENUES %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_VENUES %}active{% endif %}">
<a href="{% url 'manage_events:venue_list'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -158,6 +180,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_EVENTS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_EVENTS %}active{% endif %}">
<a href="{% url 'manage_events:event_list' %}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -166,6 +190,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_CONTACT_US %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_CONTACT_US %}active{% endif %}">
<a href="{% url 'manage_communications:contact_us_list'%}" aria-expanded="false"
class="dropdown-toggle">
@@ -175,6 +201,8 @@
</div>
</a>
</li>
{% endif %}
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_CMS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_CMS %}active{% endif %}">
<a href="{% url 'manage_cms:cms_dashboard'%}" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -183,6 +211,7 @@
</div>
</a>
</li>
{% endif %}
<!-- <li class="menu">
<a href="./app-calendar.html" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -199,6 +228,7 @@
</div>
</a>
</li> -->
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_FEEDBACK %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_FEEDBACK %}active{% endif %}">
<a href="{% url 'manage_communications:feedback_list'%}" aria-expanded="false" class="dropdown-toggle">
@@ -208,6 +238,7 @@
</div>
</a>
</li>
{% endif %}
<!-- <li class="menu">
<a href="./app-calendar.html" aria-expanded="false" class="dropdown-toggle">
<div class="">
@@ -216,6 +247,7 @@
</div>
</a>
</li> -->
{% if user|has_resource_permission:resource_context.RESOURCE_MANAGE_NOTIFICATIONS %}
<li class="menu {% if page_name == resource_context.RESOURCE_MANAGE_NOTIFICATIONS %}active{% endif %}">
<a href="{% url 'manage_notifications:notification_list'%}" aria-expanded="false"
class="dropdown-toggle">
@@ -225,6 +257,7 @@
</div>
</a>
</li>
{% endif %}
</ul>
</nav>