mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:51:08 -05:00
Add new JavaScript and GIF assets for enhanced UI features
- Introduced a new loading indicator GIF to improve user experience during asynchronous operations. - Added jQuery Ajax Queue plugin to manage queued Ajax requests, ensuring that new requests wait for previous ones to complete. - Implemented jQuery Autocomplete plugin for enhanced input fields, allowing users to receive suggestions as they type. - Included jQuery Bgiframe plugin to ensure proper rendering of elements in Internet Explorer 6.
This commit is contained in:
@@ -4,10 +4,9 @@
|
|||||||
IMPORTANT: Always follow these instructions exactly when starting the development server:
|
IMPORTANT: Always follow these instructions exactly when starting the development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; uv run manage.py tailwind runserver
|
lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; ./scripts/dev_server.sh
|
||||||
```
|
|
||||||
|
|
||||||
Note: These steps must be executed in this exact order as a single command to ensure consistent behavior.
|
Note: These steps must be executed in this exact order as a single command to ensure consistent behavior. If server does not start correctly, do not attempt to modify the dev_server.sh script.
|
||||||
|
|
||||||
## Package Management
|
## Package Management
|
||||||
IMPORTANT: When a Python package is needed, only use UV to add it:
|
IMPORTANT: When a Python package is needed, only use UV to add it:
|
||||||
@@ -24,8 +23,8 @@ uv run manage.py <command>
|
|||||||
This applies to all management commands including but not limited to:
|
This applies to all management commands including but not limited to:
|
||||||
- Making migrations: `uv run manage.py makemigrations`
|
- Making migrations: `uv run manage.py makemigrations`
|
||||||
- Applying migrations: `uv run manage.py migrate`
|
- Applying migrations: `uv run manage.py migrate`
|
||||||
- Creating superuser: `uv run manage.py createsuperuser`
|
- Creating superuser: `uv run manage.py createsuperuser` and possible echo commands before for the necessary data input.
|
||||||
- Starting shell: `uv run manage.py shell`
|
- Starting shell: `uv run manage.py shell` and possible echo commands before for the necessary data input.
|
||||||
|
|
||||||
NEVER use `python manage.py` or `uv run python manage.py`. Always use `uv run manage.py` directly.
|
NEVER use `python manage.py` or `uv run python manage.py`. Always use `uv run manage.py` directly.
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ from django.contrib.sites.shortcuts import get_current_site
|
|||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class CustomAccountAdapter(DefaultAccountAdapter):
|
class CustomAccountAdapter(DefaultAccountAdapter):
|
||||||
def is_open_for_signup(self, request):
|
def is_open_for_signup(self, request):
|
||||||
"""
|
"""
|
||||||
Whether to allow sign ups.
|
Whether to allow sign ups.
|
||||||
"""
|
"""
|
||||||
return getattr(settings, 'ACCOUNT_ALLOW_SIGNUPS', True)
|
return True
|
||||||
|
|
||||||
def get_email_confirmation_url(self, request, emailconfirmation):
|
def get_email_confirmation_url(self, request, emailconfirmation):
|
||||||
"""
|
"""
|
||||||
@@ -25,7 +26,8 @@ class CustomAccountAdapter(DefaultAccountAdapter):
|
|||||||
Sends the confirmation email.
|
Sends the confirmation email.
|
||||||
"""
|
"""
|
||||||
current_site = get_current_site(request)
|
current_site = get_current_site(request)
|
||||||
activate_url = self.get_email_confirmation_url(request, emailconfirmation)
|
activate_url = self.get_email_confirmation_url(
|
||||||
|
request, emailconfirmation)
|
||||||
ctx = {
|
ctx = {
|
||||||
'user': emailconfirmation.email_address.user,
|
'user': emailconfirmation.email_address.user,
|
||||||
'activate_url': activate_url,
|
'activate_url': activate_url,
|
||||||
@@ -36,14 +38,16 @@ class CustomAccountAdapter(DefaultAccountAdapter):
|
|||||||
email_template = 'account/email/email_confirmation_signup'
|
email_template = 'account/email/email_confirmation_signup'
|
||||||
else:
|
else:
|
||||||
email_template = 'account/email/email_confirmation'
|
email_template = 'account/email/email_confirmation'
|
||||||
self.send_mail(email_template, emailconfirmation.email_address.email, ctx)
|
self.send_mail(
|
||||||
|
email_template, emailconfirmation.email_address.email, ctx)
|
||||||
|
|
||||||
|
|
||||||
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
|
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||||
def is_open_for_signup(self, request, sociallogin):
|
def is_open_for_signup(self, request, sociallogin):
|
||||||
"""
|
"""
|
||||||
Whether to allow social account sign ups.
|
Whether to allow social account sign ups.
|
||||||
"""
|
"""
|
||||||
return getattr(settings, 'SOCIALACCOUNT_ALLOW_SIGNUPS', True)
|
return True
|
||||||
|
|
||||||
def populate_user(self, request, sociallogin, data):
|
def populate_user(self, request, sociallogin, data):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from django.urls import reverse
|
|||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from .models import User, UserProfile, EmailVerification, TopList, TopListItem
|
from .models import User, UserProfile, EmailVerification, TopList, TopListItem
|
||||||
|
|
||||||
|
|
||||||
class UserProfileInline(admin.StackedInline):
|
class UserProfileInline(admin.StackedInline):
|
||||||
model = UserProfile
|
model = UserProfile
|
||||||
can_delete = False
|
can_delete = False
|
||||||
@@ -26,19 +27,24 @@ class UserProfileInline(admin.StackedInline):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TopListItemInline(admin.TabularInline):
|
class TopListItemInline(admin.TabularInline):
|
||||||
model = TopListItem
|
model = TopListItem
|
||||||
extra = 1
|
extra = 1
|
||||||
fields = ('content_type', 'object_id', 'rank', 'notes')
|
fields = ('content_type', 'object_id', 'rank', 'notes')
|
||||||
ordering = ('rank',)
|
ordering = ('rank',)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class CustomUserAdmin(UserAdmin):
|
class CustomUserAdmin(UserAdmin):
|
||||||
list_display = ('username', 'email', 'get_avatar', 'get_status', 'role', 'date_joined', 'last_login', 'get_credits')
|
list_display = ('username', 'email', 'get_avatar', 'get_status',
|
||||||
list_filter = ('is_active', 'is_staff', 'role', 'is_banned', 'groups', 'date_joined')
|
'role', 'date_joined', 'last_login', 'get_credits')
|
||||||
|
list_filter = ('is_active', 'is_staff', 'role',
|
||||||
|
'is_banned', 'groups', 'date_joined')
|
||||||
search_fields = ('username', 'email')
|
search_fields = ('username', 'email')
|
||||||
ordering = ('-date_joined',)
|
ordering = ('-date_joined',)
|
||||||
actions = ['activate_users', 'deactivate_users', 'ban_users', 'unban_users']
|
actions = ['activate_users', 'deactivate_users',
|
||||||
|
'ban_users', 'unban_users']
|
||||||
inlines = [UserProfileInline]
|
inlines = [UserProfileInline]
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -67,12 +73,13 @@ class CustomUserAdmin(UserAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@admin.display(description='Avatar')
|
||||||
def get_avatar(self, obj):
|
def get_avatar(self, obj):
|
||||||
if obj.profile.avatar:
|
if obj.profile.avatar:
|
||||||
return format_html('<img src="{}" width="30" height="30" style="border-radius:50%;" />', obj.profile.avatar.url)
|
return format_html('<img src="{}" width="30" height="30" style="border-radius:50%;" />', obj.profile.avatar.url)
|
||||||
return format_html('<div style="width:30px; height:30px; border-radius:50%; background-color:#007bff; color:white; display:flex; align-items:center; justify-content:center;">{}</div>', obj.username[0].upper())
|
return format_html('<div style="width:30px; height:30px; border-radius:50%; background-color:#007bff; color:white; display:flex; align-items:center; justify-content:center;">{}</div>', obj.username[0].upper())
|
||||||
get_avatar.short_description = 'Avatar'
|
|
||||||
|
|
||||||
|
@admin.display(description='Status')
|
||||||
def get_status(self, obj):
|
def get_status(self, obj):
|
||||||
if obj.is_banned:
|
if obj.is_banned:
|
||||||
return format_html('<span style="color: red;">Banned</span>')
|
return format_html('<span style="color: red;">Banned</span>')
|
||||||
@@ -83,8 +90,8 @@ class CustomUserAdmin(UserAdmin):
|
|||||||
if obj.is_staff:
|
if obj.is_staff:
|
||||||
return format_html('<span style="color: blue;">Staff</span>')
|
return format_html('<span style="color: blue;">Staff</span>')
|
||||||
return format_html('<span style="color: green;">Active</span>')
|
return format_html('<span style="color: green;">Active</span>')
|
||||||
get_status.short_description = 'Status'
|
|
||||||
|
|
||||||
|
@admin.display(description='Ride Credits')
|
||||||
def get_credits(self, obj):
|
def get_credits(self, obj):
|
||||||
try:
|
try:
|
||||||
profile = obj.profile
|
profile = obj.profile
|
||||||
@@ -97,24 +104,23 @@ class CustomUserAdmin(UserAdmin):
|
|||||||
)
|
)
|
||||||
except UserProfile.DoesNotExist:
|
except UserProfile.DoesNotExist:
|
||||||
return '-'
|
return '-'
|
||||||
get_credits.short_description = 'Ride Credits'
|
|
||||||
|
|
||||||
|
@admin.action(description="Activate selected users")
|
||||||
def activate_users(self, request, queryset):
|
def activate_users(self, request, queryset):
|
||||||
queryset.update(is_active=True)
|
queryset.update(is_active=True)
|
||||||
activate_users.short_description = "Activate selected users"
|
|
||||||
|
|
||||||
|
@admin.action(description="Deactivate selected users")
|
||||||
def deactivate_users(self, request, queryset):
|
def deactivate_users(self, request, queryset):
|
||||||
queryset.update(is_active=False)
|
queryset.update(is_active=False)
|
||||||
deactivate_users.short_description = "Deactivate selected users"
|
|
||||||
|
|
||||||
|
@admin.action(description="Ban selected users")
|
||||||
def ban_users(self, request, queryset):
|
def ban_users(self, request, queryset):
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
queryset.update(is_banned=True, ban_date=timezone.now())
|
queryset.update(is_banned=True, ban_date=timezone.now())
|
||||||
ban_users.short_description = "Ban selected users"
|
|
||||||
|
|
||||||
|
@admin.action(description="Unban selected users")
|
||||||
def unban_users(self, request, queryset):
|
def unban_users(self, request, queryset):
|
||||||
queryset.update(is_banned=False, ban_date=None, ban_reason='')
|
queryset.update(is_banned=False, ban_date=None, ban_reason='')
|
||||||
unban_users.short_description = "Unban selected users"
|
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
creating = not obj.pk
|
creating = not obj.pk
|
||||||
@@ -125,10 +131,13 @@ class CustomUserAdmin(UserAdmin):
|
|||||||
if group:
|
if group:
|
||||||
obj.groups.add(group)
|
obj.groups.add(group)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(UserProfile)
|
@admin.register(UserProfile)
|
||||||
class UserProfileAdmin(admin.ModelAdmin):
|
class UserProfileAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'display_name', 'coaster_credits', 'dark_ride_credits', 'flat_ride_credits', 'water_ride_credits')
|
list_display = ('user', 'display_name', 'coaster_credits',
|
||||||
list_filter = ('coaster_credits', 'dark_ride_credits', 'flat_ride_credits', 'water_ride_credits')
|
'dark_ride_credits', 'flat_ride_credits', 'water_ride_credits')
|
||||||
|
list_filter = ('coaster_credits', 'dark_ride_credits',
|
||||||
|
'flat_ride_credits', 'water_ride_credits')
|
||||||
search_fields = ('user__username', 'user__email', 'display_name', 'bio')
|
search_fields = ('user__username', 'user__email', 'display_name', 'bio')
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -148,6 +157,7 @@ class UserProfileAdmin(admin.ModelAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(EmailVerification)
|
@admin.register(EmailVerification)
|
||||||
class EmailVerificationAdmin(admin.ModelAdmin):
|
class EmailVerificationAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'created_at', 'last_sent', 'is_expired')
|
list_display = ('user', 'created_at', 'last_sent', 'is_expired')
|
||||||
@@ -164,13 +174,14 @@ class EmailVerificationAdmin(admin.ModelAdmin):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@admin.display(description='Status')
|
||||||
def is_expired(self, obj):
|
def is_expired(self, obj):
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
if timezone.now() - obj.last_sent > timedelta(days=1):
|
if timezone.now() - obj.last_sent > timedelta(days=1):
|
||||||
return format_html('<span style="color: red;">Expired</span>')
|
return format_html('<span style="color: red;">Expired</span>')
|
||||||
return format_html('<span style="color: green;">Valid</span>')
|
return format_html('<span style="color: green;">Valid</span>')
|
||||||
is_expired.short_description = 'Status'
|
|
||||||
|
|
||||||
@admin.register(TopList)
|
@admin.register(TopList)
|
||||||
class TopListAdmin(admin.ModelAdmin):
|
class TopListAdmin(admin.ModelAdmin):
|
||||||
@@ -190,6 +201,7 @@ class TopListAdmin(admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
readonly_fields = ('created_at', 'updated_at')
|
readonly_fields = ('created_at', 'updated_at')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(TopListItem)
|
@admin.register(TopListItem)
|
||||||
class TopListItemAdmin(admin.ModelAdmin):
|
class TopListItemAdmin(admin.ModelAdmin):
|
||||||
list_display = ('top_list', 'content_type', 'object_id', 'rank')
|
list_display = ('top_list', 'content_type', 'object_id', 'rank')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from django.core.management.base import BaseCommand
|
|||||||
from allauth.socialaccount.models import SocialApp, SocialAccount, SocialToken
|
from allauth.socialaccount.models import SocialApp, SocialAccount, SocialToken
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Check all social auth related tables'
|
help = 'Check all social auth related tables'
|
||||||
|
|
||||||
@@ -9,7 +10,8 @@ class Command(BaseCommand):
|
|||||||
# Check SocialApp
|
# Check SocialApp
|
||||||
self.stdout.write('\nChecking SocialApp table:')
|
self.stdout.write('\nChecking SocialApp table:')
|
||||||
for app in SocialApp.objects.all():
|
for app in SocialApp.objects.all():
|
||||||
self.stdout.write(f'ID: {app.id}, Provider: {app.provider}, Name: {app.name}, Client ID: {app.client_id}')
|
self.stdout.write(
|
||||||
|
f'ID: {app.pk}, Provider: {app.provider}, Name: {app.name}, Client ID: {app.client_id}')
|
||||||
self.stdout.write('Sites:')
|
self.stdout.write('Sites:')
|
||||||
for site in app.sites.all():
|
for site in app.sites.all():
|
||||||
self.stdout.write(f' - {site.domain}')
|
self.stdout.write(f' - {site.domain}')
|
||||||
@@ -17,14 +19,17 @@ class Command(BaseCommand):
|
|||||||
# Check SocialAccount
|
# Check SocialAccount
|
||||||
self.stdout.write('\nChecking SocialAccount table:')
|
self.stdout.write('\nChecking SocialAccount table:')
|
||||||
for account in SocialAccount.objects.all():
|
for account in SocialAccount.objects.all():
|
||||||
self.stdout.write(f'ID: {account.id}, Provider: {account.provider}, UID: {account.uid}')
|
self.stdout.write(
|
||||||
|
f'ID: {account.pk}, Provider: {account.provider}, UID: {account.uid}')
|
||||||
|
|
||||||
# Check SocialToken
|
# Check SocialToken
|
||||||
self.stdout.write('\nChecking SocialToken table:')
|
self.stdout.write('\nChecking SocialToken table:')
|
||||||
for token in SocialToken.objects.all():
|
for token in SocialToken.objects.all():
|
||||||
self.stdout.write(f'ID: {token.id}, Account: {token.account}, App: {token.app}')
|
self.stdout.write(
|
||||||
|
f'ID: {token.pk}, Account: {token.account}, App: {token.app}')
|
||||||
|
|
||||||
# Check Site
|
# Check Site
|
||||||
self.stdout.write('\nChecking Site table:')
|
self.stdout.write('\nChecking Site table:')
|
||||||
for site in Site.objects.all():
|
for site in Site.objects.all():
|
||||||
self.stdout.write(f'ID: {site.id}, Domain: {site.domain}, Name: {site.name}')
|
self.stdout.write(
|
||||||
|
f'ID: {site.pk}, Domain: {site.domain}, Name: {site.name}')
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ class Command(BaseCommand):
|
|||||||
user = User.objects.create_user(
|
user = User.objects.create_user(
|
||||||
username="testuser",
|
username="testuser",
|
||||||
email="testuser@example.com",
|
email="testuser@example.com",
|
||||||
[PASSWORD-REMOVED]",
|
password="testpass123",
|
||||||
)
|
)
|
||||||
self.stdout.write(self.style.SUCCESS(f"Created test user: {user.username}"))
|
self.stdout.write(self.style.SUCCESS(
|
||||||
|
f"Created test user: {user.username}"))
|
||||||
else:
|
else:
|
||||||
self.stdout.write(self.style.WARNING("Test user already exists"))
|
self.stdout.write(self.style.WARNING("Test user already exists"))
|
||||||
|
|
||||||
@@ -25,11 +26,12 @@ class Command(BaseCommand):
|
|||||||
moderator = User.objects.create_user(
|
moderator = User.objects.create_user(
|
||||||
username="moderator",
|
username="moderator",
|
||||||
email="moderator@example.com",
|
email="moderator@example.com",
|
||||||
[PASSWORD-REMOVED]",
|
password="modpass123",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create moderator group if it doesn't exist
|
# Create moderator group if it doesn't exist
|
||||||
moderator_group, created = Group.objects.get_or_create(name="Moderators")
|
moderator_group, created = Group.objects.get_or_create(
|
||||||
|
name="Moderators")
|
||||||
|
|
||||||
# Add relevant permissions
|
# Add relevant permissions
|
||||||
permissions = Permission.objects.filter(
|
permissions = Permission.objects.filter(
|
||||||
@@ -48,9 +50,11 @@ class Command(BaseCommand):
|
|||||||
moderator.groups.add(moderator_group)
|
moderator.groups.add(moderator_group)
|
||||||
|
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS(f"Created moderator user: {moderator.username}")
|
self.style.SUCCESS(
|
||||||
|
f"Created moderator user: {moderator.username}")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.stdout.write(self.style.WARNING("Moderator user already exists"))
|
self.stdout.write(self.style.WARNING(
|
||||||
|
"Moderator user already exists"))
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS("Test users setup complete"))
|
self.stdout.write(self.style.SUCCESS("Test users setup complete"))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from django.db import connection
|
|||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Reset database and create admin user'
|
help = 'Reset database and create admin user'
|
||||||
|
|
||||||
@@ -58,7 +59,10 @@ class Command(BaseCommand):
|
|||||||
) RETURNING id;
|
) RETURNING id;
|
||||||
""", [make_password('admin'), user_id])
|
""", [make_password('admin'), user_id])
|
||||||
|
|
||||||
user_db_id = cursor.fetchone()[0]
|
result = cursor.fetchone()
|
||||||
|
if result is None:
|
||||||
|
raise Exception("Failed to create user - no ID returned")
|
||||||
|
user_db_id = result[0]
|
||||||
|
|
||||||
# Create profile
|
# Create profile
|
||||||
profile_id = str(uuid.uuid4())[:10]
|
profile_id = str(uuid.uuid4())[:10]
|
||||||
@@ -79,7 +83,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
self.stdout.write('Superuser created.')
|
self.stdout.write('Superuser created.')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stdout.write(self.style.ERROR(f'Error creating superuser: {str(e)}'))
|
self.stdout.write(self.style.ERROR(
|
||||||
|
f'Error creating superuser: {str(e)}'))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Database reset complete.'))
|
self.stdout.write(self.style.SUCCESS('Database reset complete.'))
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
Local development settings for thrillwiki project.
|
Local development settings for thrillwiki project.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
from .base import *
|
from .base import *
|
||||||
from ..settings import database
|
from ..settings import database
|
||||||
from ..settings import email # Import the module and use its members, e.g., email.EMAIL_HOST
|
# Import the module and use its members, e.g., email.EMAIL_HOST
|
||||||
from ..settings import security # Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
from ..settings import email
|
||||||
|
# Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
||||||
|
from ..settings import security
|
||||||
from .base import env # Import env for environment variable access
|
from .base import env # Import env for environment variable access
|
||||||
|
|
||||||
# Import database configuration
|
# Import database configuration
|
||||||
@@ -24,9 +27,8 @@ CSRF_TRUSTED_ORIGINS = [
|
|||||||
"https://beta.thrillwiki.com",
|
"https://beta.thrillwiki.com",
|
||||||
]
|
]
|
||||||
|
|
||||||
# GeoDjango Settings for macOS development
|
GDAL_LIBRARY_PATH = "/opt/homebrew/lib/libgdal.dylib"
|
||||||
GDAL_LIBRARY_PATH = env('GDAL_LIBRARY_PATH', default="/opt/homebrew/lib/libgdal.dylib")
|
GEOS_LIBRARY_PATH = "/opt/homebrew/lib/libgeos_c.dylib"
|
||||||
GEOS_LIBRARY_PATH = env('GEOS_LIBRARY_PATH', default="/opt/homebrew/lib/libgeos_c.dylib")
|
|
||||||
|
|
||||||
# Local cache configuration
|
# Local cache configuration
|
||||||
LOC_MEM_CACHE_BACKEND = "django.core.cache.backends.locmem.LocMemCache"
|
LOC_MEM_CACHE_BACKEND = "django.core.cache.backends.locmem.LocMemCache"
|
||||||
@@ -69,6 +71,7 @@ DEVELOPMENT_APPS = [
|
|||||||
'silk',
|
'silk',
|
||||||
'debug_toolbar',
|
'debug_toolbar',
|
||||||
'nplusone.ext.django',
|
'nplusone.ext.django',
|
||||||
|
'django_extensions',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add development apps if available
|
# Add development apps if available
|
||||||
@@ -94,17 +97,19 @@ for middleware in DEVELOPMENT_MIDDLEWARE:
|
|||||||
INTERNAL_IPS = ['127.0.0.1', '::1']
|
INTERNAL_IPS = ['127.0.0.1', '::1']
|
||||||
|
|
||||||
# Silk configuration for development
|
# Silk configuration for development
|
||||||
SILKY_PYTHON_PROFILER = False # Disable profiler to avoid silk_profile installation issues
|
# Disable profiler to avoid silk_profile installation issues
|
||||||
|
SILKY_PYTHON_PROFILER = False
|
||||||
SILKY_PYTHON_PROFILER_BINARY = False # Disable binary profiler
|
SILKY_PYTHON_PROFILER_BINARY = False # Disable binary profiler
|
||||||
# SILKY_PYTHON_PROFILER_RESULT_PATH = BASE_DIR / 'profiles' # Not needed when profiler is disabled
|
SILKY_PYTHON_PROFILER_RESULT_PATH = BASE_DIR / \
|
||||||
|
'profiles' # Not needed when profiler is disabled
|
||||||
SILKY_AUTHENTICATION = True # Require login to access Silk
|
SILKY_AUTHENTICATION = True # Require login to access Silk
|
||||||
SILKY_AUTHORISATION = True # Enable authorization
|
SILKY_AUTHORISATION = True # Enable authorization
|
||||||
SILKY_MAX_REQUEST_BODY_SIZE = -1 # Don't limit request body size
|
SILKY_MAX_REQUEST_BODY_SIZE = -1 # Don't limit request body size
|
||||||
SILKY_MAX_RESPONSE_BODY_SIZE = 1024 # Limit response body size to 1KB for performance
|
# Limit response body size to 1KB for performance
|
||||||
|
SILKY_MAX_RESPONSE_BODY_SIZE = 1024
|
||||||
SILKY_META = True # Record metadata about requests
|
SILKY_META = True # Record metadata about requests
|
||||||
|
|
||||||
# NPlusOne configuration
|
# NPlusOne configuration
|
||||||
import logging
|
|
||||||
NPLUSONE_LOGGER = logging.getLogger('nplusone')
|
NPLUSONE_LOGGER = logging.getLogger('nplusone')
|
||||||
NPLUSONE_LOG_LEVEL = logging.WARN
|
NPLUSONE_LOG_LEVEL = logging.WARN
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,27 @@
|
|||||||
Production settings for thrillwiki project.
|
Production settings for thrillwiki project.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import base # Import the module and use its members, e.g., base.BASE_DIR, base***REMOVED***
|
# Import the module and use its members, e.g., base.BASE_DIR, base***REMOVED***
|
||||||
from ..settings import database # Import the module and use its members, e.g., database.DATABASES
|
from . import base
|
||||||
from ..settings import email # Import the module and use its members, e.g., email.EMAIL_HOST
|
# Import the module and use its members, e.g., database.DATABASES
|
||||||
from ..settings import security # Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
from ..settings import database
|
||||||
from ..settings import email # Import the module and use its members, e.g., email.EMAIL_HOST
|
# Import the module and use its members, e.g., email.EMAIL_HOST
|
||||||
from ..settings import security # Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
from ..settings import email
|
||||||
|
# Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
||||||
|
from ..settings import security
|
||||||
|
# Import the module and use its members, e.g., email.EMAIL_HOST
|
||||||
|
from ..settings import email
|
||||||
|
# Import the module and use its members, e.g., security.SECURE_HSTS_SECONDS
|
||||||
|
from ..settings import security
|
||||||
|
|
||||||
# Production settings
|
# Production settings
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
# Allowed hosts must be explicitly set in production
|
# Allowed hosts must be explicitly set in production
|
||||||
ALLOWED_HOSTS = base***REMOVED***('ALLOWED_HOSTS')
|
ALLOWED_HOSTS = base.env.list('ALLOWED_HOSTS')
|
||||||
|
|
||||||
# CSRF trusted origins for production
|
# CSRF trusted origins for production
|
||||||
CSRF_TRUSTED_ORIGINS = base***REMOVED***('CSRF_TRUSTED_ORIGINS', default=[])
|
CSRF_TRUSTED_ORIGINS = base.env.list('CSRF_TRUSTED_ORIGINS')
|
||||||
|
|
||||||
# Security settings for production
|
# Security settings for production
|
||||||
SECURE_SSL_REDIRECT = True
|
SECURE_SSL_REDIRECT = True
|
||||||
@@ -80,11 +86,12 @@ LOGGING = {
|
|||||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
|
|
||||||
# Cache settings for production (Redis recommended)
|
# Cache settings for production (Redis recommended)
|
||||||
if base***REMOVED***('REDIS_URL', default=None):
|
redis_url = base.env.str('REDIS_URL', default=None)
|
||||||
|
if redis_url:
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django_redis.cache.RedisCache',
|
'BACKEND': 'django_redis.cache.RedisCache',
|
||||||
'LOCATION': base***REMOVED***('REDIS_URL'),
|
'LOCATION': redis_url,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
}
|
}
|
||||||
@@ -94,4 +101,3 @@ if base***REMOVED***('REDIS_URL', default=None):
|
|||||||
# Use Redis for sessions in production
|
# Use Redis for sessions in production
|
||||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||||
SESSION_CACHE_ALIAS = 'default'
|
SESSION_CACHE_ALIAS = 'default'
|
||||||
|
|
||||||
|
|||||||
@@ -50,4 +50,6 @@ dependencies = [
|
|||||||
"python-json-logger>=2.0.7",
|
"python-json-logger>=2.0.7",
|
||||||
"django-cloudflare-images>=0.6.0",
|
"django-cloudflare-images>=0.6.0",
|
||||||
"psutil>=7.0.0",
|
"psutil>=7.0.0",
|
||||||
|
"django-extensions>=4.1",
|
||||||
|
"werkzeug>=3.1.3",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -77,6 +77,13 @@ else
|
|||||||
echo "🔄 Running database migrations..."
|
echo "🔄 Running database migrations..."
|
||||||
uv run manage.py migrate --noinput
|
uv run manage.py migrate --noinput
|
||||||
fi
|
fi
|
||||||
|
echo "Resetting database..."
|
||||||
|
if uv run manage.py seed_sample_data 2>/dev/null; then
|
||||||
|
echo "Seeding complete!"
|
||||||
|
else
|
||||||
|
echo "Seeding test data to database..."
|
||||||
|
uv run manage.py seed_sample_data
|
||||||
|
fi
|
||||||
|
|
||||||
# Create superuser if it doesn't exist
|
# Create superuser if it doesn't exist
|
||||||
echo "👤 Checking for superuser..."
|
echo "👤 Checking for superuser..."
|
||||||
|
|||||||
38
staticfiles/django_extensions/css/jquery.autocomplete.css
Normal file
38
staticfiles/django_extensions/css/jquery.autocomplete.css
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @fileOverview CSS for jquery-autocomplete, the jQuery Autocompleter
|
||||||
|
* @author <a href="mailto:dylan@dyve.net">Dylan Verheul</a>
|
||||||
|
* @license MIT | GPL | Apache 2.0, see LICENSE.txt
|
||||||
|
* @see https://github.com/dyve/jquery-autocomplete
|
||||||
|
*/
|
||||||
|
.acResults {
|
||||||
|
padding: 0px;
|
||||||
|
border: 1px solid WindowFrame;
|
||||||
|
background-color: Window;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acResults ul {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
list-style-position: outside;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acResults ul li {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
font: menu;
|
||||||
|
font-size: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acLoading {
|
||||||
|
background : url('../img/indicator.gif') right center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.acSelect {
|
||||||
|
background-color: Highlight;
|
||||||
|
color: HighlightText;
|
||||||
|
}
|
||||||
BIN
staticfiles/django_extensions/img/indicator.gif
Normal file
BIN
staticfiles/django_extensions/img/indicator.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
116
staticfiles/django_extensions/js/jquery.ajaxQueue.js
Normal file
116
staticfiles/django_extensions/js/jquery.ajaxQueue.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Ajax Queue Plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
jQuery.ajaxQueue({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append(html); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxQueue({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append(html); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxSync({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
|
||||||
|
});
|
||||||
|
jQuery.ajaxSync({
|
||||||
|
url: "test.php",
|
||||||
|
success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<ul style="position: absolute; top: 5px; right: 5px;"></ul>
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Queued Ajax requests.
|
||||||
|
* A new Ajax request won't be started until the previous queued
|
||||||
|
* request has finished.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Synced Ajax requests.
|
||||||
|
* The Ajax request will happen as soon as you call this method, but
|
||||||
|
* the callbacks (success/error/complete) won't fire until all previous
|
||||||
|
* synced requests have been completed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
(function(jQuery) {
|
||||||
|
|
||||||
|
var ajax = jQuery.ajax;
|
||||||
|
|
||||||
|
var pendingRequests = {};
|
||||||
|
|
||||||
|
var synced = [];
|
||||||
|
var syncedData = [];
|
||||||
|
|
||||||
|
jQuery.ajax = function(settings) {
|
||||||
|
// create settings for compatibility with ajaxSetup
|
||||||
|
settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings));
|
||||||
|
|
||||||
|
var port = settings.port;
|
||||||
|
|
||||||
|
switch(settings.mode) {
|
||||||
|
case "abort":
|
||||||
|
if ( pendingRequests[port] ) {
|
||||||
|
pendingRequests[port].abort();
|
||||||
|
}
|
||||||
|
return pendingRequests[port] = ajax.apply(this, arguments);
|
||||||
|
case "queue":
|
||||||
|
var _old = settings.complete;
|
||||||
|
settings.complete = function(){
|
||||||
|
if ( _old )
|
||||||
|
_old.apply( this, arguments );
|
||||||
|
jQuery([ajax]).dequeue("ajax" + port );;
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery([ ajax ]).queue("ajax" + port, function(){
|
||||||
|
ajax( settings );
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "sync":
|
||||||
|
var pos = synced.length;
|
||||||
|
|
||||||
|
synced[ pos ] = {
|
||||||
|
error: settings.error,
|
||||||
|
success: settings.success,
|
||||||
|
complete: settings.complete,
|
||||||
|
done: false
|
||||||
|
};
|
||||||
|
|
||||||
|
syncedData[ pos ] = {
|
||||||
|
error: [],
|
||||||
|
success: [],
|
||||||
|
complete: []
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.error = function(){ syncedData[ pos ].error = arguments; };
|
||||||
|
settings.success = function(){ syncedData[ pos ].success = arguments; };
|
||||||
|
settings.complete = function(){
|
||||||
|
syncedData[ pos ].complete = arguments;
|
||||||
|
synced[ pos ].done = true;
|
||||||
|
|
||||||
|
if ( pos == 0 || !synced[ pos-1 ] )
|
||||||
|
for ( var i = pos; i < synced.length && synced[i].done; i++ ) {
|
||||||
|
if ( synced[i].error ) synced[i].error.apply( jQuery, syncedData[i].error );
|
||||||
|
if ( synced[i].success ) synced[i].success.apply( jQuery, syncedData[i].success );
|
||||||
|
if ( synced[i].complete ) synced[i].complete.apply( jQuery, syncedData[i].complete );
|
||||||
|
|
||||||
|
synced[i] = null;
|
||||||
|
syncedData[i] = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ajax.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
})((typeof window.jQuery == 'undefined' && typeof window.django != 'undefined')
|
||||||
|
? django.jQuery
|
||||||
|
: jQuery
|
||||||
|
);
|
||||||
1152
staticfiles/django_extensions/js/jquery.autocomplete.js
Normal file
1152
staticfiles/django_extensions/js/jquery.autocomplete.js
Normal file
File diff suppressed because it is too large
Load Diff
39
staticfiles/django_extensions/js/jquery.bgiframe.js
Normal file
39
staticfiles/django_extensions/js/jquery.bgiframe.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*! Copyright (c) 2010 Brandon Aaron (http://brandon.aaron.sh/)
|
||||||
|
* Licensed under the MIT License (LICENSE.txt).
|
||||||
|
*
|
||||||
|
* Version 2.1.2
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($){
|
||||||
|
|
||||||
|
$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) {
|
||||||
|
s = $.extend({
|
||||||
|
top : 'auto', // auto == .currentStyle.borderTopWidth
|
||||||
|
left : 'auto', // auto == .currentStyle.borderLeftWidth
|
||||||
|
width : 'auto', // auto == offsetWidth
|
||||||
|
height : 'auto', // auto == offsetHeight
|
||||||
|
opacity : true,
|
||||||
|
src : 'javascript:false;'
|
||||||
|
}, s);
|
||||||
|
var html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
|
||||||
|
'style="display:block;position:absolute;z-index:-1;'+
|
||||||
|
(s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
|
||||||
|
'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
|
||||||
|
'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
|
||||||
|
'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
|
||||||
|
'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
|
||||||
|
'"/>';
|
||||||
|
return this.each(function() {
|
||||||
|
if ( $(this).children('iframe.bgiframe').length === 0 )
|
||||||
|
this.insertBefore( document.createElement(html), this.firstChild );
|
||||||
|
});
|
||||||
|
} : function() { return this; });
|
||||||
|
|
||||||
|
// old alias
|
||||||
|
$.fn.bgIframe = $.fn.bgiframe;
|
||||||
|
|
||||||
|
function prop(n) {
|
||||||
|
return n && n.constructor === Number ? n + 'px' : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
})((typeof window.jQuery == 'undefined' && typeof window.django != 'undefined') ? django.jQuery : jQuery);
|
||||||
@@ -11,6 +11,6 @@ import os
|
|||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os***REMOVED***iron.setdefault("DJANGO_SETTINGS_MODULE", "config.django.production")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.django.production")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|||||||
Reference in New Issue
Block a user