Mohsen Taba 1 week ago
parent
commit
ffed87aaa4
  1. 8
      apps/account/views/user.py
  2. 5
      apps/article/serializers_admin.py
  3. 5
      apps/article/views.py
  4. 17
      apps/hadis/serializers/hadis.py
  5. 19
      apps/hadis/serializers/reference_v2.py
  6. 11
      apps/hadis/serializers/serializers_admin.py
  7. 8
      apps/hadis/urls.py
  8. 3
      apps/hadis/views/category.py
  9. 5
      apps/hadis/views/hadis.py
  10. 5
      apps/library/serializers_admin.py
  11. 5
      apps/library/views.py
  12. 5
      apps/podcast/serializers_admin.py
  13. 5
      apps/podcast/views.py
  14. 5
      apps/video/serializers_admin.py
  15. 5
      apps/video/views.py
  16. 2
      config/__init__.py
  17. 18
      utils/__init__.py

8
apps/account/views/user.py

@ -27,7 +27,7 @@ from apps.account.serializers import UserRegisterSerializer, UserProfileSerializ
from apps.account.serializers.user_web import WebUserRegisterSerializer from apps.account.serializers.user_web import WebUserRegisterSerializer
from utils.redis import RedisManager from utils.redis import RedisManager
from utils.exceptions import AppAPIException from utils.exceptions import AppAPIException
from utils import send_email, is_valid_email
from utils import send_email, is_valid_email, absolute_https_url
from config.settings import base as settings from config.settings import base as settings
from apps.account.permissions import IsActiveUser, IsSuperAdminOrReadOnlyForProfessor from apps.account.permissions import IsActiveUser, IsSuperAdminOrReadOnlyForProfessor
from apps.account.doc import * from apps.account.doc import *
@ -431,7 +431,7 @@ class UserLoginView(CreateAPIView):
"email": user.email, "email": user.email,
"token": token.key, "token": token.key,
"user_type": user.user_type, "user_type": user.user_type,
"avatar": request.build_absolute_uri(user.avatar.url) if user.avatar else None,
"avatar": absolute_https_url(user.avatar.url, request) if user.avatar else None,
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
@ -492,7 +492,7 @@ class UserRecoverPassword(CreateAPIView):
"fullname": user.fullname, "fullname": user.fullname,
"phone_number": str(user.phone_number) if user.phone_number else None, "phone_number": str(user.phone_number) if user.phone_number else None,
"email": user.email if user.email else None, "email": user.email if user.email else None,
"avatar": request.build_absolute_uri(user.avatar.url) if user.avatar else None,
"avatar": absolute_https_url(user.avatar.url, request) if user.avatar else None,
"message": "Forgot password code sent" "message": "Forgot password code sent"
}, },
status=status.HTTP_202_ACCEPTED, status=status.HTTP_202_ACCEPTED,
@ -633,7 +633,7 @@ class AdminLoginView(CreateAPIView):
"email": user.email, "email": user.email,
"token": token.key, "token": token.key,
"user_type": effective_user_type, "user_type": effective_user_type,
"avatar": request.build_absolute_uri(user.avatar.url) if user.avatar else None,
"avatar": absolute_https_url(user.avatar.url, request) if user.avatar else None,
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)

5
apps/article/serializers_admin.py

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
from .models import Article, ArticleCategory, ArticleCollection, ArticleContent, ContentPart, TextSection from .models import Article, ArticleCategory, ArticleCollection, ArticleContent, ContentPart, TextSection
@ -23,9 +24,7 @@ class AbsoluteImageField(serializers.ImageField):
return None return None
request = self.context.get("request") request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value) url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
return absolute_https_url(url, request)
class AbsoluteFileField(serializers.FileField): class AbsoluteFileField(serializers.FileField):

5
apps/article/views.py

@ -64,8 +64,9 @@ class PinnedArticleCollectionListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
return PinnedArticleCollection.objects.filter( return PinnedArticleCollection.objects.filter(
status=True, status=True,
display_position=ArticleCollection.DisplayPosition.PINNED
).order_by('order', '-created_at')
display_position=ArticleCollection.DisplayPosition.PINNED,
articles__status=True
).distinct().order_by('order', '-created_at')
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs) response = super().list(request, *args, **kwargs)

17
apps/hadis/serializers/hadis.py

@ -8,6 +8,7 @@ from .category import LocalizedField, ArabicLocalizedField
from .category import get_localized_text, get_arabic_localized_text from .category import get_localized_text, get_arabic_localized_text
from .category import get_localized_text from .category import get_localized_text
from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer
from utils import absolute_https_url
from ..models import ( from ..models import (
Hadis, HadisStatus, HadisTag, HadisTransmitter, Hadis, HadisStatus, HadisTag, HadisTransmitter,
@ -58,8 +59,8 @@ class PinnedHadisCollectionSerializer(serializers.ModelSerializer):
if obj.thumbnail: if obj.thumbnail:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(obj.thumbnail.url)
return obj.thumbnail.url
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None return None
class HadisCollectionListSerializer(serializers.ModelSerializer): class HadisCollectionListSerializer(serializers.ModelSerializer):
@ -75,8 +76,8 @@ class HadisCollectionListSerializer(serializers.ModelSerializer):
if obj.thumbnail: if obj.thumbnail:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(obj.thumbnail.url)
return obj.thumbnail.url
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None return None
@ -142,7 +143,7 @@ class HadisSyncSerializer(serializers.ModelSerializer):
for img in reference.images.all(): for img in reference.images.all():
thumb_url = None thumb_url = None
if img.thumbnail: if img.thumbnail:
thumb_url = request.build_absolute_uri(img.thumbnail.url) if request else img.thumbnail.url
thumb_url = absolute_https_url(img.thumbnail.url, request)
reference_images_block.append({ reference_images_block.append({
'id': img.id, 'id': img.id,
'thumbnail': thumb_url, 'thumbnail': thumb_url,
@ -770,8 +771,8 @@ class ReferenceImageSerializer(serializers.ModelSerializer):
if obj.thumbnail: if obj.thumbnail:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(obj.thumbnail.url)
return obj.thumbnail.url
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None return None
@ -1220,4 +1221,4 @@ class TransmitterOriginalTextDetailSerializer(serializers.ModelSerializer):
"id": obj.transmitter.id, "id": obj.transmitter.id,
"slug": obj.transmitter.slug, "slug": obj.transmitter.slug,
"name": get_localized_text(obj.transmitter.full_name, self.context.get('request')) "name": get_localized_text(obj.transmitter.full_name, self.context.get('request'))
}
}

19
apps/hadis/serializers/reference_v2.py

@ -5,6 +5,7 @@ from .category import get_localized_text
from apps.hadis.serializers import HadisListSerializer, HadisShortSerializer from apps.hadis.serializers import HadisListSerializer, HadisShortSerializer
from ..serializers.category import LocalizedField from ..serializers.category import LocalizedField
from ..serializers.reference import BookAttributeSerializer, BookReferenceImageSerializer from ..serializers.reference import BookAttributeSerializer, BookReferenceImageSerializer
from utils import absolute_https_url
class BookTagV2Serializer(serializers.ModelSerializer): class BookTagV2Serializer(serializers.ModelSerializer):
title = LocalizedField() title = LocalizedField()
@ -58,8 +59,8 @@ class BookReferenceV2ListSerializer(serializers.ModelSerializer):
if first_image and first_image.image: if first_image and first_image.image:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(first_image.image.url)
return first_image.image.url
return absolute_https_url(first_image.image.url, request)
return absolute_https_url(first_image.image.url)
return None return None
class BookEditionV2Serializer(serializers.ModelSerializer): class BookEditionV2Serializer(serializers.ModelSerializer):
@ -99,8 +100,8 @@ class BookVolumeV2Serializer(serializers.ModelSerializer):
if first_image and first_image.image: if first_image and first_image.image:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(first_image.image.url)
return first_image.image.url
return absolute_https_url(first_image.image.url, request)
return absolute_https_url(first_image.image.url)
return None return None
class BookReferenceV2DetailSerializer(serializers.ModelSerializer): class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
@ -128,8 +129,8 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
if first_image and first_image.image: if first_image and first_image.image:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(first_image.image.url)
return first_image.image.url
return absolute_https_url(first_image.image.url, request)
return absolute_https_url(first_image.image.url)
return None return None
def get_information(self, obj): def get_information(self, obj):
@ -305,8 +306,8 @@ class AuthorBookReferenceSerializer(serializers.ModelSerializer):
if first_image and first_image.image: if first_image and first_image.image:
request = self.context.get('request') request = self.context.get('request')
if request: if request:
return request.build_absolute_uri(first_image.image.url)
return first_image.image.url
return absolute_https_url(first_image.image.url, request)
return absolute_https_url(first_image.image.url)
return None return None
# یک سریالایزر مینیمال برای کتاب‌ها داخل سینک # یک سریالایزر مینیمال برای کتاب‌ها داخل سینک
@ -350,4 +351,4 @@ class BookAuthorSyncSerializer(serializers.ModelSerializer):
'death_year_miladi', 'death_year_miladi',
'books', # 👈 لیست کتاب‌های هر نویسنده 'books', # 👈 لیست کتاب‌های هر نویسنده
'related_narrator_slug' 'related_narrator_slug'
]
]

11
apps/hadis/serializers/serializers_admin.py

@ -2,6 +2,7 @@ from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
from ..models import ( from ..models import (
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, HadisReference, Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, HadisReference,
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher, BookEditor, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher, BookEditor,
@ -33,9 +34,7 @@ class AbsoluteImageField(serializers.ImageField):
return None return None
request = self.context.get("request") request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value) url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
return absolute_https_url(url, request)
class AdminHadisCategorySerializer(serializers.ModelSerializer): class AdminHadisCategorySerializer(serializers.ModelSerializer):
@ -415,7 +414,7 @@ class AdminHadisCorrectionSerializer(serializers.ModelSerializer):
ret["images"] = [ ret["images"] = [
{ {
"id": img.id, "id": img.id,
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
"image": absolute_https_url(img.image.url, request) if request and img.image else absolute_https_url(img.image.url)
} }
for img in images for img in images
] ]
@ -534,7 +533,7 @@ class AdminHadisInterpretationSerializer(serializers.ModelSerializer):
ret["images"] = [ ret["images"] = [
{ {
"id": img.id, "id": img.id,
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
"image": absolute_https_url(img.image.url, request) if request and img.image else absolute_https_url(img.image.url)
} }
for img in images for img in images
] ]
@ -1198,7 +1197,7 @@ class AdminTransmitterOriginalTextSerializer(serializers.ModelSerializer):
ret["images"] = [ ret["images"] = [
{ {
"id": img.id, "id": img.id,
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
"image": absolute_https_url(img.image.url, request) if request and img.image else absolute_https_url(img.image.url)
} }
for img in (ref.images.all() if ref else []) for img in (ref.images.all() if ref else [])
] ]

8
apps/hadis/urls.py

@ -104,10 +104,10 @@ urlpatterns = [
# Narrator paths # Narrator paths
path('narrators/<str:narrator_slug>/teachers/', NarratorTeachersView.as_view(), name='narrator-teachers'), path('narrators/<str:narrator_slug>/teachers/', NarratorTeachersView.as_view(), name='narrator-teachers'),
path('narrators/<str:narrator_slug>/students/', NarratorStudentsView.as_view(), name='narrator-students'), path('narrators/<str:narrator_slug>/students/', NarratorStudentsView.as_view(), name='narrator-students'),
path('narrators/<str:narrator_slug>/arguments', NarratorArgumentsListView.as_view(), name='narrator-arguments'),
path('narrators/<str:narrator_slug>/opinions', TransmitterOpinionView.as_view(), name='narrator-opinions'),
path('narrators/<str:narrator_slug>/original_texts', TransmitterOriginalTextView.as_view(), name='narrator-original-texts'),
path('narrators/<str:narrator_slug>', TransmitterDetailView.as_view(), name='narrator-detail'),
path('narrators/<str:narrator_slug>/arguments/', NarratorArgumentsListView.as_view(), name='narrator-arguments'),
path('narrators/<str:narrator_slug>/opinions/', TransmitterOpinionView.as_view(), name='narrator-opinions'),
path('narrators/<str:narrator_slug>/original_texts/', TransmitterOriginalTextView.as_view(), name='narrator-original-texts'),
path('narrators/<str:narrator_slug>/', TransmitterDetailView.as_view(), name='narrator-detail'),
path('narrators/filters/', TransmitterFiltersView.as_view(), name='narrator-filters'), path('narrators/filters/', TransmitterFiltersView.as_view(), name='narrator-filters'),
path('narrators/', TransmitterView.as_view(), name='narrators'), path('narrators/', TransmitterView.as_view(), name='narrators'),

3
apps/hadis/views/category.py

@ -5,6 +5,7 @@ from utils.pagination import NoPagination
from django.db.models import Q from django.db.models import Q
from collections import defaultdict from collections import defaultdict
from utils.pagination import StandardResultsSetPagination from utils.pagination import StandardResultsSetPagination
from utils import absolute_https_url
from ..models import HadisSect, HadisCategory,Hadis from ..models import HadisSect, HadisCategory,Hadis
from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer
from ..serializers import ( from ..serializers import (
@ -218,7 +219,7 @@ class HadisCategoryTreeView(ListAPIView):
def _get_thumbnail_url(self, category, request): def _get_thumbnail_url(self, category, request):
"""Get absolute thumbnail URL""" """Get absolute thumbnail URL"""
if hasattr(category, 'thumbnail') and category.thumbnail: if hasattr(category, 'thumbnail') and category.thumbnail:
return request.build_absolute_uri(category.thumbnail.url) if request else category.thumbnail.url
return absolute_https_url(category.thumbnail.url, request)
return None return None
def _get_xmind_url(self, category, request): def _get_xmind_url(self, category, request):

5
apps/hadis/views/hadis.py

@ -33,8 +33,9 @@ class PinnedHadisCollectionListView(ListAPIView):
def get_queryset(self): def get_queryset(self):
return HadisCollection.objects.filter( return HadisCollection.objects.filter(
status=True, status=True,
display_position=HadisCollection.DisplayPosition.PINNED
).order_by('order', '-created_at')
display_position=HadisCollection.DisplayPosition.PINNED,
hadis_items__hadis__status=True
).distinct().order_by('order', '-created_at')
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
# 1. گرفتن خروجی استاندارد لیست مجموعه‌ها # 1. گرفتن خروجی استاندارد لیست مجموعه‌ها

5
apps/library/serializers_admin.py

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
from .models import Author, Book, BookCollection, Category from .models import Author, Book, BookCollection, Category
@ -23,9 +24,7 @@ class AbsoluteImageField(serializers.ImageField):
return None return None
request = self.context.get("request") request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value) url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
return absolute_https_url(url, request)
class AbsoluteFileField(serializers.FileField): class AbsoluteFileField(serializers.FileField):

5
apps/library/views.py

@ -67,8 +67,9 @@ class PinnedBookCollectionListView(ListAPIView):
def get_queryset(self): def get_queryset(self):
return BookCollection.objects.filter( return BookCollection.objects.filter(
status=True, status=True,
display_position=BookCollection.DisplayPosition.PINNED
).order_by('order', '-created_at')
display_position=BookCollection.DisplayPosition.PINNED,
books__status=True
).distinct().order_by('order', '-created_at')
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):

5
apps/podcast/serializers_admin.py

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
from .models import PlaylistItem, Podcast, PodcastCategory, PodcastCollection, PodcastPlaylist from .models import PlaylistItem, Podcast, PodcastCategory, PodcastCollection, PodcastPlaylist
@ -23,9 +24,7 @@ class AbsoluteImageField(serializers.ImageField):
return None return None
request = self.context.get("request") request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value) url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
return absolute_https_url(url, request)
class AbsoluteFileField(serializers.FileField): class AbsoluteFileField(serializers.FileField):

5
apps/podcast/views.py

@ -71,8 +71,9 @@ class PinnedPodcastCollectionListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
return PinnedPodcastCollection.objects.filter( return PinnedPodcastCollection.objects.filter(
status=True, status=True,
display_position=PodcastCollection.DisplayPosition.PINNED
).order_by('order', '-created_at')
display_position=PodcastCollection.DisplayPosition.PINNED,
podcasts__status=True
).distinct().order_by('order', '-created_at')
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs) response = super().list(request, *args, **kwargs)

5
apps/video/serializers_admin.py

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from utils import absolute_https_url
from .models import PlaylistItem, Video, VideoCategory, VideoCollection, VideoPlaylist from .models import PlaylistItem, Video, VideoCategory, VideoCollection, VideoPlaylist
@ -23,9 +24,7 @@ class AbsoluteImageField(serializers.ImageField):
return None return None
request = self.context.get("request") request = self.context.get("request")
url = value.url if hasattr(value, "url") else str(value) url = value.url if hasattr(value, "url") else str(value)
if request:
return request.build_absolute_uri(url)
return url
return absolute_https_url(url, request)
class AbsoluteFileField(serializers.FileField): class AbsoluteFileField(serializers.FileField):

5
apps/video/views.py

@ -84,8 +84,9 @@ class PinnedVideoCollectionListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
queryset = PinnedVideoCollection.objects.filter( queryset = PinnedVideoCollection.objects.filter(
status=True, status=True,
display_position=VideoCollection.DisplayPosition.PINNED
).order_by('order', '-created_at')
display_position=VideoCollection.DisplayPosition.PINNED,
related_playlists__status=True
).distinct().order_by('order', '-created_at')
# Filter by bookmarks if requested # Filter by bookmarks if requested
is_bookmark = self.request.query_params.get('is_bookmark', '').lower() is_bookmark = self.request.query_params.get('is_bookmark', '').lower()

2
config/__init__.py

@ -5,4 +5,4 @@ from __future__ import absolute_import, unicode_literals
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
from .celery import app as celery_app from .celery import app as celery_app
__all__ = ('celery_app',)
__all__ = ('celery_app',)

18
utils/__init__.py

@ -36,6 +36,17 @@ from utils.image_compression import maybe_compress_uploaded_file
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def absolute_https_url(url, request=None):
"""Build an absolute URL and always expose it over HTTPS."""
if request:
url = request.build_absolute_uri(url)
if isinstance(url, str) and url.startswith('http://'):
return f'https://{url[7:]}'
return url
# Moved filer imports to avoid circular imports # Moved filer imports to avoid circular imports
# These will be imported when needed in functions # These will be imported when needed in functions
@ -63,12 +74,7 @@ def get_thumbs(obj, request: HttpRequest = None) -> dict:
# برای هر سه سایز، همان URL اصلی را برگردان # برای هر سه سایز، همان URL اصلی را برگردان
for size in sizes: for size in sizes:
if request:
url = request.build_absolute_uri(original_url)
else:
url = original_url
thumbnail_object[size] = url
thumbnail_object[size] = absolute_https_url(original_url, request)
return thumbnail_object return thumbnail_object

Loading…
Cancel
Save