Compare commits
2 Commits
41f9be4c7e
...
36aef98986
| Author | SHA1 | Date | |
|---|---|---|---|
| 36aef98986 | |||
| 029f0c7b8d |
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
@@ -39,12 +40,14 @@ from apps.blog.permissions import (
|
|||||||
can_review_blog_posts,
|
can_review_blog_posts,
|
||||||
can_write_blog_posts,
|
can_write_blog_posts,
|
||||||
)
|
)
|
||||||
|
from apps.notifications.services import notify_user
|
||||||
from core.api.schemas import ErrorSchema, MessageSchema
|
from core.api.schemas import ErrorSchema, MessageSchema
|
||||||
from core.authentication import jwt_auth
|
from core.authentication import jwt_auth
|
||||||
|
|
||||||
|
|
||||||
blog_router = Router()
|
blog_router = Router()
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".svg"}
|
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".svg"}
|
||||||
VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".mkv", ".avi"}
|
VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".mkv", ".avi"}
|
||||||
@@ -240,6 +243,73 @@ def _interaction_payload(post: Post, user) -> BlogInteractionSchema:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _frontend_blog_url(post: Post) -> str:
|
||||||
|
root = getattr(settings, "FRONTEND_ROOT", "/") or "/"
|
||||||
|
if not root.endswith("/"):
|
||||||
|
root = f"{root}/"
|
||||||
|
return f"{root}blog/{post.slug}"
|
||||||
|
|
||||||
|
|
||||||
|
def _blog_moderator_ids() -> set[int]:
|
||||||
|
return set(
|
||||||
|
User.objects.filter(is_active=True)
|
||||||
|
.filter(
|
||||||
|
Q(is_staff=True)
|
||||||
|
| Q(is_superuser=True)
|
||||||
|
| Q(groups__permissions__content_type__app_label="blog", groups__permissions__codename="moderate_blog_comment")
|
||||||
|
| Q(user_permissions__content_type__app_label="blog", user_permissions__codename="moderate_blog_comment")
|
||||||
|
)
|
||||||
|
.distinct()
|
||||||
|
.values_list("id", flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _post_author_ids(post: Post) -> set[int]:
|
||||||
|
author_ids = set(post.writers.values_list("id", flat=True))
|
||||||
|
if post.author_id:
|
||||||
|
author_ids.add(post.author_id)
|
||||||
|
return author_ids
|
||||||
|
|
||||||
|
|
||||||
|
def _notify_blog_comment(comment: Comment) -> None:
|
||||||
|
post = comment.post
|
||||||
|
actor_id = comment.author_id
|
||||||
|
action_url = _frontend_blog_url(post)
|
||||||
|
excluded_ids = {actor_id}
|
||||||
|
|
||||||
|
if comment.parent_id and comment.parent.author_id != actor_id:
|
||||||
|
notify_user(
|
||||||
|
comment.parent.author_id,
|
||||||
|
{
|
||||||
|
"type": "blog_reply",
|
||||||
|
"title": "پاسخ جدید",
|
||||||
|
"message": f"به کامنت شما در «{post.title}» پاسخ داده شد.",
|
||||||
|
"level": "info",
|
||||||
|
"action_url": action_url,
|
||||||
|
"entity_type": "blog_comment",
|
||||||
|
"entity_id": comment.id,
|
||||||
|
"meta": {"post_id": post.id, "post_slug": post.slug, "parent_id": comment.parent_id},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
excluded_ids.add(comment.parent.author_id)
|
||||||
|
|
||||||
|
recipient_ids = (_blog_moderator_ids() | _post_author_ids(post)) - excluded_ids
|
||||||
|
for user_id in recipient_ids:
|
||||||
|
notify_user(
|
||||||
|
user_id,
|
||||||
|
{
|
||||||
|
"type": "blog_comment",
|
||||||
|
"title": "کامنت جدید",
|
||||||
|
"message": f"برای «{post.title}» کامنت جدید ثبت شد.",
|
||||||
|
"level": "info",
|
||||||
|
"action_url": action_url,
|
||||||
|
"entity_type": "blog_post",
|
||||||
|
"entity_id": post.id,
|
||||||
|
"meta": {"post_id": post.id, "post_slug": post.slug, "comment_id": comment.id},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@blog_router.get("/admin/writers", response={200: List[AuthorSchema], 403: ErrorSchema}, auth=jwt_auth)
|
@blog_router.get("/admin/writers", response={200: List[AuthorSchema], 403: ErrorSchema}, auth=jwt_auth)
|
||||||
def list_blog_writers(request):
|
def list_blog_writers(request):
|
||||||
if not (request.auth.is_superuser or request.auth.is_staff or can_review_blog_posts(request.auth)):
|
if not (request.auth.is_superuser or request.auth.is_staff or can_review_blog_posts(request.auth)):
|
||||||
@@ -437,16 +507,23 @@ def delete_post_asset(request, post_id: int, asset_id: int):
|
|||||||
@blog_router.get("/me/activity", response=BlogProfileActivitySchema, auth=jwt_auth)
|
@blog_router.get("/me/activity", response=BlogProfileActivitySchema, auth=jwt_auth)
|
||||||
def my_blog_activity(request):
|
def my_blog_activity(request):
|
||||||
comments = (
|
comments = (
|
||||||
Comment.objects.filter(author=request.auth)
|
Comment.objects.filter(
|
||||||
|
author=request.auth,
|
||||||
|
is_approved=True,
|
||||||
|
is_hidden=False,
|
||||||
|
is_deleted=False,
|
||||||
|
post__status=Post.StatusChoices.PUBLISHED,
|
||||||
|
post__is_deleted=False,
|
||||||
|
)
|
||||||
.select_related("author", "post")
|
.select_related("author", "post")
|
||||||
.order_by("-created_at")[:20]
|
.order_by("-created_at")[:20]
|
||||||
)
|
)
|
||||||
return BlogProfileActivitySchema(
|
return {
|
||||||
liked_posts=list(_published_queryset().filter(likes__user=request.auth)[:20]),
|
"liked_posts": list(_published_queryset().filter(likes__user=request.auth)[:20]),
|
||||||
saved_posts=list(_published_queryset().filter(saves__user=request.auth)[:20]),
|
"saved_posts": list(_published_queryset().filter(saves__user=request.auth)[:20]),
|
||||||
comments=list([comment for comment in comments if comment.parent_id is None]),
|
"comments": [comment for comment in comments if comment.parent_id is None],
|
||||||
replies=list([comment for comment in comments if comment.parent_id is not None]),
|
"replies": [comment for comment in comments if comment.parent_id is not None],
|
||||||
)
|
}
|
||||||
|
|
||||||
|
|
||||||
@blog_router.get("/banners", response=List[BlogBannerSchema])
|
@blog_router.get("/banners", response=List[BlogBannerSchema])
|
||||||
@@ -619,6 +696,10 @@ def create_comment(request, slug: str, data: CommentCreateSchema):
|
|||||||
if data.parent_id:
|
if data.parent_id:
|
||||||
parent = get_object_or_404(Comment, id=data.parent_id, post=post, is_approved=True, is_hidden=False, is_deleted=False)
|
parent = get_object_or_404(Comment, id=data.parent_id, post=post, is_approved=True, is_hidden=False, is_deleted=False)
|
||||||
comment = Comment.objects.create(post=post, author=request.auth, content=data.content, parent=parent)
|
comment = Comment.objects.create(post=post, author=request.auth, content=data.content, parent=parent)
|
||||||
|
try:
|
||||||
|
_notify_blog_comment(comment)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Failed to send blog comment notifications for comment=%s", comment.id)
|
||||||
return 201, comment
|
return 201, comment
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user