586 lines
20 KiB
Python
586 lines
20 KiB
Python
import logging
|
|
from django.shortcuts import render, get_object_or_404, redirect, reverse
|
|
from django.contrib.auth.decorators import login_required, permission_required
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.http import (
|
|
HttpResponse,
|
|
HttpResponseBadRequest,
|
|
HttpResponseServerError,
|
|
HttpResponseNotFound,
|
|
)
|
|
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
|
from django.db.models import Count
|
|
from django.utils import timezone
|
|
from django.core.paginator import Paginator
|
|
from .models import *
|
|
from django.conf import settings
|
|
import re
|
|
from django.http import HttpResponseRedirect
|
|
from django.db.models import Q
|
|
import time
|
|
from management.models import Announcement
|
|
from django.utils.baseconv import base62
|
|
from .forms import *
|
|
from mastodon.api import share_review
|
|
from users.views import render_user_blocked, render_user_not_found
|
|
from users.models import User, Report, Preference
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
PAGE_SIZE = 10
|
|
|
|
_checkmark = "✔️".encode("utf-8")
|
|
|
|
|
|
@login_required
|
|
def wish(request, item_uuid):
|
|
if request.method == "POST":
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
if not item:
|
|
return HttpResponseNotFound(b"item not found")
|
|
request.user.shelf_manager.move_item(item, ShelfType.WISHLIST)
|
|
if request.GET.get("back"):
|
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
|
return HttpResponse(_checkmark)
|
|
else:
|
|
return HttpResponseBadRequest(b"invalid request")
|
|
|
|
|
|
@login_required
|
|
def like(request, piece_uuid):
|
|
if request.method == "POST":
|
|
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
|
if not piece:
|
|
return HttpResponseNotFound(b"piece not found")
|
|
Like.user_like_piece(request.user, piece)
|
|
return HttpResponse(_checkmark)
|
|
else:
|
|
return HttpResponseBadRequest(b"invalid request")
|
|
|
|
|
|
@login_required
|
|
def unlike(request, piece_uuid):
|
|
if request.method == "POST":
|
|
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
|
if not piece:
|
|
return HttpResponseNotFound(b"piece not found")
|
|
Like.user_unlike_piece(request.user, piece)
|
|
if request.GET.get("back"):
|
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
|
return HttpResponse(_checkmark)
|
|
else:
|
|
return HttpResponseBadRequest(b"invalid request")
|
|
|
|
|
|
@login_required
|
|
def add_to_collection(request, item_uuid):
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
if request.method == "GET":
|
|
collections = Collection.objects.filter(owner=request.user)
|
|
return render(
|
|
request,
|
|
"add_to_collection.html",
|
|
{
|
|
"item": item,
|
|
"collections": collections,
|
|
},
|
|
)
|
|
else:
|
|
cid = int(request.POST.get("collection_id", default=0))
|
|
if not cid:
|
|
cid = Collection.objects.create(
|
|
owner=request.user, title=f"{request.user.username}的收藏单"
|
|
).id
|
|
collection = Collection.objects.get(owner=request.user, id=cid)
|
|
collection.append_item(item, metadata={"comment": request.POST.get("comment")})
|
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def render_relogin(request):
|
|
return render(
|
|
request,
|
|
"common/error.html",
|
|
{
|
|
"url": reverse("users:connect") + "?domain=" + request.user.mastodon_site,
|
|
"msg": _("信息已保存,但是未能分享到联邦网络"),
|
|
"secondary_msg": _(
|
|
"可能是你在联邦网络(Mastodon/Pleroma/...)的登录状态过期了,正在跳转到联邦网络重新登录😼"
|
|
),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def mark(request, item_uuid):
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
mark = Mark(request.user, item)
|
|
if request.method == "GET":
|
|
tags = TagManager.get_item_tags_by_user(item, request.user)
|
|
shelf_types = [
|
|
(n[1], n[2]) for n in iter(ShelfTypeNames) if n[0] == item.category
|
|
]
|
|
shelf_type = request.GET.get("shelf_type", mark.shelf_type)
|
|
return render(
|
|
request,
|
|
"mark.html",
|
|
{
|
|
"item": item,
|
|
"mark": mark,
|
|
"shelf_type": shelf_type,
|
|
"tags": ",".join(tags),
|
|
"shelf_types": shelf_types,
|
|
},
|
|
)
|
|
elif request.method == "POST":
|
|
if request.POST.get("delete", default=False):
|
|
mark.delete()
|
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
|
else:
|
|
visibility = int(request.POST.get("visibility", default=0))
|
|
rating = request.POST.get("rating", default=0)
|
|
rating = int(rating) if rating else None
|
|
status = ShelfType(request.POST.get("status"))
|
|
text = request.POST.get("text")
|
|
tags = request.POST.get("tags")
|
|
tags = tags.split(",") if tags else []
|
|
share_to_mastodon = bool(
|
|
request.POST.get("share_to_mastodon", default=False)
|
|
)
|
|
TagManager.tag_item_by_user(item, request.user, tags, visibility)
|
|
try:
|
|
mark.update(
|
|
status,
|
|
text,
|
|
rating,
|
|
visibility,
|
|
share_to_mastodon=share_to_mastodon,
|
|
)
|
|
except Exception:
|
|
return render_relogin(request)
|
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def collection_retrieve(request, collection_uuid):
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if not collection.is_visible_to(request.user):
|
|
raise PermissionDenied()
|
|
return render(request, "collection.html", {"collection": collection})
|
|
|
|
|
|
def collection_retrieve_items(request, collection_uuid, edit=False):
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if not collection.is_visible_to(request.user):
|
|
raise PermissionDenied()
|
|
form = CollectionForm(instance=collection)
|
|
return render(
|
|
request,
|
|
"collection_items.html",
|
|
{
|
|
"collection": collection,
|
|
"form": form,
|
|
"collection_edit": edit or request.GET.get("edit"),
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def collection_append_item(request, collection_uuid):
|
|
if request.method != "POST":
|
|
return HttpResponseBadRequest()
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
|
|
url = request.POST.get("url")
|
|
note = request.POST.get("note")
|
|
item = Item.get_by_url(url)
|
|
collection.append_item(item, metadata={"note": note})
|
|
collection.save()
|
|
return collection_retrieve_items(request, collection_uuid, True)
|
|
|
|
|
|
@login_required
|
|
def collection_remove_item(request, collection_uuid, item_uuid):
|
|
if request.method != "POST":
|
|
return HttpResponseBadRequest()
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
if not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
collection.remove_item(item)
|
|
return collection_retrieve_items(request, collection_uuid, True)
|
|
|
|
|
|
@login_required
|
|
def collection_move_item(request, direction, collection_uuid, item_uuid):
|
|
if request.method != "POST":
|
|
return HttpResponseBadRequest()
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
if direction == "up":
|
|
collection.move_up_item(item)
|
|
else:
|
|
collection.move_down_item(item)
|
|
return collection_retrieve_items(request, collection_uuid, True)
|
|
|
|
|
|
@login_required
|
|
def collection_update_item_note(request, collection_uuid, item_uuid):
|
|
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
if not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
if request.method == "POST":
|
|
collection.update_item_metadata(
|
|
item, {"note": request.POST.get("note", default="")}
|
|
)
|
|
return collection_retrieve_items(request, collection_uuid, True)
|
|
elif request.method == "GET":
|
|
member = collection.get_member_for_item(item)
|
|
return render(
|
|
request,
|
|
"collection_update_item_note.html",
|
|
{"collection": collection, "item": item, "note": member.note},
|
|
)
|
|
else:
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
@login_required
|
|
def collection_edit(request, collection_uuid=None):
|
|
collection = (
|
|
get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
|
if collection_uuid
|
|
else None
|
|
)
|
|
if collection and not collection.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
if request.method == "GET":
|
|
form = CollectionForm(instance=collection) if collection else CollectionForm()
|
|
return render(
|
|
request, "collection_edit.html", {"form": form, "collection": collection}
|
|
)
|
|
elif request.method == "POST":
|
|
form = (
|
|
CollectionForm(request.POST, instance=collection)
|
|
if collection
|
|
else CollectionForm(request.POST)
|
|
)
|
|
if form.is_valid():
|
|
if not collection:
|
|
form.instance.owner = request.user
|
|
form.instance.edited_time = timezone.now()
|
|
form.save()
|
|
return redirect(
|
|
reverse("journal:collection_retrieve", args=[form.instance.uuid])
|
|
)
|
|
else:
|
|
return HttpResponseBadRequest(form.errors)
|
|
else:
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
def review_retrieve(request, review_uuid):
|
|
piece = get_object_or_404(Review, uid=base62.decode(review_uuid))
|
|
if not piece.is_visible_to(request.user):
|
|
raise PermissionDenied()
|
|
return render(request, "review.html", {"review": piece})
|
|
|
|
|
|
@login_required
|
|
def review_edit(request, item_uuid, review_uuid=None):
|
|
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
|
review = (
|
|
get_object_or_404(Review, uid=base62.decode(review_uuid))
|
|
if review_uuid
|
|
else None
|
|
)
|
|
if review and not review.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
if request.method == "GET":
|
|
form = (
|
|
ReviewForm(instance=review)
|
|
if review
|
|
else ReviewForm(initial={"item": item.id})
|
|
)
|
|
return render(request, "review_edit.html", {"form": form, "item": item})
|
|
elif request.method == "POST":
|
|
form = (
|
|
ReviewForm(request.POST, instance=review)
|
|
if review
|
|
else ReviewForm(request.POST)
|
|
)
|
|
if form.is_valid():
|
|
if not review:
|
|
form.instance.owner = request.user
|
|
form.instance.edited_time = timezone.now()
|
|
form.save()
|
|
if form.cleaned_data["share_to_mastodon"]:
|
|
form.instance.save = lambda **args: None
|
|
form.instance.shared_link = None
|
|
if not share_review(form.instance):
|
|
return render_relogin(request)
|
|
return redirect(
|
|
reverse("journal:review_retrieve", args=[form.instance.uuid])
|
|
)
|
|
else:
|
|
return HttpResponseBadRequest(form.errors)
|
|
else:
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
@login_required
|
|
def piece_delete(request, piece_uuid):
|
|
piece = get_object_or_404(Piece, uid=base62.decode(piece_uuid))
|
|
return_url = request.GET.get("return_url", None) or "/"
|
|
if not piece.is_editable_by(request.user):
|
|
raise PermissionDenied()
|
|
if request.method == "GET":
|
|
return render(
|
|
request, "piece_delete.html", {"piece": piece, "return_url": return_url}
|
|
)
|
|
elif request.method == "POST":
|
|
piece.delete()
|
|
return redirect(return_url)
|
|
else:
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
def render_list_not_fount(request):
|
|
msg = _("相关列表不存在")
|
|
return render(
|
|
request,
|
|
"common/error.html",
|
|
{
|
|
"msg": msg,
|
|
},
|
|
)
|
|
|
|
|
|
def _render_list(
|
|
request, user_name, type, shelf_type=None, item_category=None, tag_title=None
|
|
):
|
|
user = User.get(user_name)
|
|
if user is None:
|
|
return render_user_not_found(request)
|
|
if user != request.user and (
|
|
request.user.is_blocked_by(user) or request.user.is_blocking(user)
|
|
):
|
|
return render_user_blocked(request)
|
|
if type == "mark":
|
|
shelf = user.shelf_manager.get_shelf(item_category, shelf_type)
|
|
queryset = ShelfMember.objects.filter(owner=user, parent=shelf)
|
|
elif type == "tagmember":
|
|
tag = Tag.objects.filter(owner=user, title=tag_title).first()
|
|
if not tag:
|
|
return render_list_not_fount(request)
|
|
if tag.visibility != 0 and user != request.user:
|
|
return render_list_not_fount(request)
|
|
queryset = TagMember.objects.filter(parent=tag)
|
|
elif type == "review":
|
|
queryset = Review.objects.filter(owner=user)
|
|
queryset = queryset.filter(query_item_category(item_category))
|
|
else:
|
|
return HttpResponseBadRequest()
|
|
queryset = queryset.filter(q_visible_to(request.user, user))
|
|
paginator = Paginator(queryset, PAGE_SIZE)
|
|
page_number = request.GET.get("page", default=1)
|
|
members = paginator.get_page(page_number)
|
|
return render(
|
|
request,
|
|
f"user_{type}_list.html",
|
|
{
|
|
"user": user,
|
|
"members": members,
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def user_mark_list(request, user_name, shelf_type, item_category):
|
|
return _render_list(
|
|
request, user_name, "mark", shelf_type=shelf_type, item_category=item_category
|
|
)
|
|
|
|
|
|
@login_required
|
|
def user_tag_member_list(request, user_name, tag_title):
|
|
return _render_list(request, user_name, "tagmember", tag_title=tag_title)
|
|
|
|
|
|
@login_required
|
|
def user_review_list(request, user_name, item_category):
|
|
return _render_list(request, user_name, "review", item_category=item_category)
|
|
|
|
|
|
@login_required
|
|
def user_tag_list(request, user_name):
|
|
user = User.get(user_name)
|
|
if user is None:
|
|
return render_user_not_found(request)
|
|
if user != request.user and (
|
|
request.user.is_blocked_by(user) or request.user.is_blocking(user)
|
|
):
|
|
return render_user_blocked(request)
|
|
tags = Tag.objects.filter(owner=user)
|
|
tags = user.tag_set.all()
|
|
if user != request.user:
|
|
tags = tags.filter(visibility=0)
|
|
tags = tags.values("title").annotate(total=Count("members")).order_by("-total")
|
|
return render(
|
|
request,
|
|
"user_tag_list.html",
|
|
{
|
|
"user": user,
|
|
"tags": tags,
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def user_collection_list(request, user_name):
|
|
user = User.get(user_name)
|
|
if user is None:
|
|
return render_user_not_found(request)
|
|
if user != request.user and (
|
|
request.user.is_blocked_by(user) or request.user.is_blocking(user)
|
|
):
|
|
return render_user_blocked(request)
|
|
collections = Collection.objects.filter(owner=user)
|
|
if user != request.user:
|
|
if request.user.is_following(user):
|
|
collections = collections.filter(visibility__ne=2)
|
|
else:
|
|
collections = collections.filter(visibility=0)
|
|
return render(
|
|
request,
|
|
"user_collection_list.html",
|
|
{
|
|
"user": user,
|
|
"collections": collections,
|
|
},
|
|
)
|
|
|
|
|
|
@login_required
|
|
def user_liked_collection_list(request, user_name):
|
|
user = User.get(user_name)
|
|
if user is None:
|
|
return render_user_not_found(request)
|
|
if user != request.user and (
|
|
request.user.is_blocked_by(user) or request.user.is_blocking(user)
|
|
):
|
|
return render_user_blocked(request)
|
|
collections = Collection.objects.filter(likes__owner=user)
|
|
if user != request.user:
|
|
collections = collections.filter(query_visible(request.user))
|
|
return render(
|
|
request,
|
|
"user_collection_list.html",
|
|
{
|
|
"user": user,
|
|
"collections": collections,
|
|
},
|
|
)
|
|
|
|
|
|
def home_anonymous(request, id):
|
|
login_url = settings.LOGIN_URL + "?next=" + request.get_full_path()
|
|
try:
|
|
username = id.split("@")[0]
|
|
site = id.split("@")[1]
|
|
return render(
|
|
request,
|
|
"users/home_anonymous.html",
|
|
{
|
|
"login_url": login_url,
|
|
"username": username,
|
|
"site": site,
|
|
},
|
|
)
|
|
except Exception:
|
|
return redirect(login_url)
|
|
|
|
|
|
def home(request, user_name):
|
|
if not request.user.is_authenticated:
|
|
return home_anonymous(request, user_name)
|
|
if request.method != "GET":
|
|
return HttpResponseBadRequest()
|
|
user = User.get(user_name)
|
|
if user is None:
|
|
return render_user_not_found(request)
|
|
|
|
# access one's own home page
|
|
if user == request.user:
|
|
reports = Report.objects.order_by("-submitted_time").filter(is_read=False)
|
|
unread_announcements = Announcement.objects.filter(
|
|
pk__gt=request.user.read_announcement_index
|
|
).order_by("-pk")
|
|
try:
|
|
request.user.read_announcement_index = Announcement.objects.latest("pk").pk
|
|
request.user.save(update_fields=["read_announcement_index"])
|
|
except ObjectDoesNotExist:
|
|
# when there is no annoucenment
|
|
pass
|
|
# visit other's home page
|
|
else:
|
|
if request.user.is_blocked_by(user) or request.user.is_blocking(user):
|
|
return render_user_blocked(request)
|
|
# no these value on other's home page
|
|
reports = None
|
|
unread_announcements = None
|
|
|
|
qv = q_visible_to(request.user, user)
|
|
shelf_list = {}
|
|
visbile_categories = [
|
|
ItemCategory.Book,
|
|
ItemCategory.Movie,
|
|
ItemCategory.TV,
|
|
ItemCategory.Music,
|
|
ItemCategory.Game,
|
|
]
|
|
for category in visbile_categories:
|
|
shelf_list[category] = {}
|
|
for shelf_type in ShelfType:
|
|
shelf = user.shelf_manager.get_shelf(category, shelf_type)
|
|
members = shelf.recent_members.filter(qv)
|
|
shelf_list[category][shelf_type] = {
|
|
"title": shelf.title,
|
|
"count": members.count(),
|
|
"members": members[:5].prefetch_related("item"),
|
|
}
|
|
reviews = Review.objects.filter(owner=user).filter(qv)
|
|
shelf_list[category]["reviewed"] = {
|
|
"title": "评论过的" + category.label,
|
|
"count": reviews.count(),
|
|
"members": reviews[:5].prefetch_related("item"),
|
|
}
|
|
collections = (
|
|
Collection.objects.filter(owner=user).filter(qv).order_by("-edited_time")
|
|
)
|
|
liked_collections = (
|
|
Collection.objects.none().filter(likes__owner=user).order_by("-edited_time")
|
|
)
|
|
if user != request.user:
|
|
liked_collections = liked_collections.filter(query_visible(request.user))
|
|
|
|
layout = user.get_preference().get_serialized_home_layout()
|
|
return render(
|
|
request,
|
|
"profile.html",
|
|
{
|
|
"user": user,
|
|
"shelf_list": shelf_list,
|
|
"collections": collections[:5],
|
|
"collections_count": collections.count(),
|
|
"liked_collections": liked_collections.order_by("-edited_time")[:5],
|
|
"liked_collections_count": liked_collections.count(),
|
|
"layout": layout,
|
|
"reports": reports,
|
|
"unread_announcements": unread_announcements,
|
|
},
|
|
)
|