I am principal module completed and added website and tags
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -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",
|
||||
),
|
||||
),
|
||||
]
|
||||
18
accounts/migrations/0008_iamprincipal_website.py
Normal file
18
accounts/migrations/0008_iamprincipal_website.py
Normal 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"),
|
||||
),
|
||||
]
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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"),
|
||||
|
||||
@@ -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"""
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -31,6 +31,7 @@ class EventAdmin(admin.ModelAdmin):
|
||||
"end_date",
|
||||
"venue",
|
||||
"status",
|
||||
# "tags",
|
||||
"draft",
|
||||
"deleted",
|
||||
"active",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,6 +36,7 @@ class EventForm(forms.ModelForm):
|
||||
"entry_fee",
|
||||
"key_guest",
|
||||
"age_group",
|
||||
"tags",
|
||||
"draft",
|
||||
]
|
||||
widgets = {
|
||||
|
||||
29
manage_events/migrations/0007_event_tags.py
Normal file
29
manage_events/migrations/0007_event_tags.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user