diff --git a/catalog/search/views.py b/catalog/search/views.py index f9f12e45..0f5e234f 100644 --- a/catalog/search/views.py +++ b/catalog/search/views.py @@ -1,4 +1,3 @@ -import logging import re import django_rq @@ -8,11 +7,11 @@ from django.core.cache import cache from django.core.exceptions import BadRequest from django.shortcuts import redirect, render from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods from rq.job import Job from catalog.common.models import ItemCategory, SiteName from catalog.common.sites import AbstractSite, SiteManager -from common.config import PAGE_LINK_NUMBER from common.utils import ( HTTPResponseHXRedirect, PageLinksGenerator, @@ -52,7 +51,7 @@ def fetch(request, url, is_refetch: bool = False, site: AbstractSite | None = No if not site: site = SiteManager.get_site_by_url(url) if not site: - raise BadRequest() + raise BadRequest(_("Invalid URL")) item = site.get_item() if item and not is_refetch: return redirect(item.url) @@ -172,10 +171,9 @@ def external_search(request): @login_required +@require_http_methods(["POST"]) def refetch(request): - if request.method != "POST": - raise BadRequest() url = request.POST.get("url") if not url: - raise BadRequest() + raise BadRequest(_("Invalid URL")) return fetch(request, url, True) diff --git a/catalog/views.py b/catalog/views.py index 1eccf246..b808cd35 100644 --- a/catalog/views.py +++ b/catalog/views.py @@ -50,11 +50,11 @@ def retrieve_redirect(request, item_path, item_uuid): def embed(request, item_path, item_uuid): item = Item.get_by_url(item_uuid) if item is None: - raise Http404() + raise Http404(_("Item not found")) if item.merged_to_item: return redirect(item.merged_to_item.url) if item.is_deleted: - raise Http404() + raise Http404(_("Item no longer exists")) focus_item = None if request.GET.get("focus"): focus_item = get_object_or_404( @@ -73,7 +73,7 @@ def retrieve(request, item_path, item_uuid): # item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) item = Item.get_by_url(item_uuid) if item is None: - raise Http404() + raise Http404(_("Item not found")) item_url = f"/{item_path}/{item_uuid}" if item.url != item_url: return redirect(item.url) @@ -81,7 +81,7 @@ def retrieve(request, item_path, item_uuid): if not skipcheck and item.merged_to_item: return redirect(item.merged_to_item.url) if not skipcheck and item.is_deleted: - raise Http404() + raise Http404(_("Item no longer exists")) if request.headers.get("Accept", "").endswith("json"): return redirect(item.api_url) focus_item = None @@ -146,8 +146,6 @@ def episode_data(request, item_uuid): @login_required def mark_list(request, item_path, item_uuid, following_only=False): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() queryset = ShelfMember.objects.filter(item=item).order_by("-created_time") if following_only: queryset = queryset.filter(q_piece_in_home_feed_of_user(request.user)) @@ -171,8 +169,6 @@ def mark_list(request, item_path, item_uuid, following_only=False): def review_list(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() queryset = Review.objects.filter(item=item).order_by("-created_time") queryset = queryset.filter(q_piece_visible_to_user(request.user)) paginator = Paginator(queryset, NUM_REVIEWS_ON_LIST_PAGE) @@ -192,8 +188,6 @@ def review_list(request, item_path, item_uuid): def comments(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() ids = item.child_item_ids + [item.id] + item.sibling_item_ids queryset = Comment.objects.filter(item_id__in=ids).order_by("-created_time") queryset = queryset.filter(q_piece_visible_to_user(request.user)) @@ -212,8 +206,6 @@ def comments(request, item_path, item_uuid): def comments_by_episode(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() episode_uuid = request.GET.get("episode_uuid") if episode_uuid: episode = TVEpisode.get_by_url(episode_uuid) @@ -238,8 +230,6 @@ def comments_by_episode(request, item_path, item_uuid): def reviews(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() ids = item.child_item_ids + [item.id] + item.sibling_item_ids queryset = Review.objects.filter(item_id__in=ids).order_by("-created_time") queryset = queryset.filter(q_piece_visible_to_user(request.user)) diff --git a/catalog/views_edit.py b/catalog/views_edit.py index 01986030..fc99ba90 100644 --- a/catalog/views_edit.py +++ b/catalog/views_edit.py @@ -97,7 +97,7 @@ def edit(request, item_path, item_uuid): form.fields["primary_lookup_id_type"].disabled = True form.fields["primary_lookup_id_value"].disabled = True return render(request, "catalog_edit.html", {"form": form, "item": item}) - elif request.method == "POST": + else: item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) form_cls = CatalogForms[item.__class__.__name__] form = form_cls(request.POST, request.FILES, instance=item) @@ -115,8 +115,6 @@ def edit(request, item_path, item_uuid): return redirect(form.instance.url) else: raise BadRequest(_add_error_map_detail(form.errors)) - else: - raise BadRequest() @require_http_methods(["POST"]) @@ -124,7 +122,7 @@ def edit(request, item_path, item_uuid): def delete(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not request.user.is_staff and item.journal_exists(): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.POST.get("sure", 0) != "1": return render(request, "catalog_delete.html", {"item": item}) else: @@ -147,7 +145,7 @@ def delete(request, item_path, item_uuid): def undelete(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not request.user.is_staff: - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) item.is_deleted = False item.save() return redirect(item.url) @@ -199,10 +197,10 @@ def recast(request, item_path, item_uuid): @login_required def unlink(request): if not request.user.is_staff: - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) res_id = request.POST.get("id") if not res_id: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) resource = get_object_or_404(ExternalResource, id=res_id) resource.unlink_from_item() return HttpResponseRedirect(request.META.get("HTTP_REFERER")) @@ -251,7 +249,7 @@ def remove_unused_seasons(request, item_path, item_uuid): def fetch_tvepisodes(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if item.class_name != "tvseason" or not item.imdb or item.season_number is None: - raise BadRequest() + raise BadRequest(_("Must be a TV Season with IMDB id and season id")) item.log_action({"!fetch_tvepisodes": ["", ""]}) django_rq.get_queue("crawl").enqueue( fetch_episodes_for_season_task, item.uuid, request.user @@ -275,7 +273,7 @@ def fetch_episodes_for_season_task(item_uuid, user): def merge(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not request.user.is_staff and item.journal_exists(): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.POST.get("sure", 0) != "1": new_item = Item.get_by_url(request.POST.get("target_item_url")) return render( @@ -352,7 +350,7 @@ def link_edition(request, item_path, item_uuid): def unlink_works(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not request.user.is_staff and item.journal_exists(): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) item.unlink_from_all_works() discord_send( "audit", diff --git a/common/templates/400.html b/common/templates/400.html index cb3d9bf2..553ac962 100644 --- a/common/templates/400.html +++ b/common/templates/400.html @@ -8,7 +8,7 @@ - {{ site_name }} - {% trans '无效请求' %} + {{ site_name }} - 400 {% include "common_libs.html" %} @@ -17,11 +17,12 @@
-

🤷🏻 无效的请求

+

🤷🏻 {{ exception }}

- 您可能提交了无效的数据,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。 + {% blocktrans %}You may have submitted invalid data, or the content may have been deleted by the author.{% endblocktrans %} +
+ {% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
- {{ exception }} {% if exception.additonal_detail %} {% for e in exception.additonal_detail %}

{{ e }}

{% endfor %} {% endif %} diff --git a/common/templates/403.html b/common/templates/403.html index 20bb116f..fadf9ca1 100644 --- a/common/templates/403.html +++ b/common/templates/403.html @@ -4,11 +4,11 @@ {% load mastodon %} {% load thumb %} - + - {{ site_name }} - {% trans '权限不符' %} + {{ site_name }} - 403 {% include "common_libs.html" %} @@ -17,10 +17,11 @@
-

🙅🏻 权限不符

+

🙅🏻 {{ exception|default:"" }}

- {{ exception }} - 您可能访问了错误的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。 + {% blocktrans %}Author may require you to log in before accessing this content, or you do not have permission to view it.{% endblocktrans %} +
+ {% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
{% include "_footer.html" %} diff --git a/common/templates/404.html b/common/templates/404.html index 49fb9ce8..18ce0d40 100644 --- a/common/templates/404.html +++ b/common/templates/404.html @@ -4,11 +4,11 @@ {% load mastodon %} {% load thumb %} - + - {{ site_name }} - {% trans '未找到' %} + {{ site_name }} - 404 {% include "common_libs.html" %} @@ -17,9 +17,11 @@
-

🤷🏻 条目未找到

+

🤷🏻 {{ exception|default:"" }}

- 您可能输入了一个无效的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。 + {% blocktrans %}You may have visited an incorrect URL, or the content you are looking for has been deleted by the author.{% endblocktrans %} +
+ {% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
{% include "_footer.html" %} diff --git a/common/templates/500.html b/common/templates/500.html index 5ba080d7..66e25c8c 100644 --- a/common/templates/500.html +++ b/common/templates/500.html @@ -4,11 +4,11 @@ {% load mastodon %} {% load thumb %} - + - {{ site_name }} - {% trans '系统错误' %} + {{ site_name }} - 500 {% include "common_libs.html" %} @@ -17,9 +17,11 @@
-

🤦🏻 系统内部错误

+

🤦🏻 My Bad

- 发生了一个内部错误,如果这个错误多次出现,后台会记录并会由人工处理。如果您有紧急情况或任何疑问,请通过页面底部的链接联系我们。 + {% blocktrans %}An internal error occurred. If this error occurs repeatedly, it will be recorded and handled by a human.{% endblocktrans %} +
+ {% blocktrans %}If you have an urgent situation or any questions, please contact us through the link at the bottom of the page.{% endblocktrans %}
{% include "_footer.html" %} diff --git a/common/utils.py b/common/utils.py index 3e1485d9..1b25963e 100644 --- a/common/utils.py +++ b/common/utils.py @@ -4,9 +4,11 @@ from typing import TYPE_CHECKING from discord import SyncWebhook from django.conf import settings +from django.core.exceptions import PermissionDenied from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict from django.utils import timezone from django.utils.baseconv import base62 +from django.utils.translation import gettext_lazy as _ from .config import PAGE_LINK_NUMBER @@ -52,16 +54,15 @@ def target_identity_required(func): @functools.wraps(func) def wrapper(request, user_name, *args, **kwargs): from users.models import APIdentity - from users.views import render_user_blocked, render_user_not_found try: target = APIdentity.get_by_handle(user_name) except APIdentity.DoesNotExist: - return render_user_not_found(request) + raise Http404(_("User not found")) target_user = target.user viewer = None if target_user and not target_user.is_active: - return render_user_not_found(request) + raise Http404(_("User no longer exists")) if request.user.is_authenticated: try: viewer = APIdentity.objects.get(user=request.user) @@ -69,7 +70,7 @@ def target_identity_required(func): return HttpResponseRedirect("/account/register") if request.user != target_user: if target.is_blocking(viewer) or target.is_blocked_by(viewer): - return render_user_blocked(request) + raise PermissionDenied(_("Access denied")) else: viewer = None request.target_identity = target @@ -83,16 +84,15 @@ def profile_identity_required(func): @functools.wraps(func) def wrapper(request, user_name, *args, **kwargs): from users.models import APIdentity - from users.views import render_user_blocked, render_user_not_found try: target = APIdentity.get_by_handle(user_name, match_linked=True) except APIdentity.DoesNotExist: - return render_user_not_found(request) + raise Http404(_("User not found")) target_user = target.user viewer = None if target_user and not target_user.is_active: - return render_user_not_found(request) + raise Http404(_("User no longer exists")) if request.user.is_authenticated: try: viewer = APIdentity.objects.get(user=request.user) @@ -100,7 +100,7 @@ def profile_identity_required(func): return HttpResponseRedirect("/account/register") if request.user != target_user: if target.is_blocking(viewer) or target.is_blocked_by(viewer): - return render_user_blocked(request) + raise PermissionDenied(_("Access denied")) else: viewer = None request.target_identity = target diff --git a/common/views.py b/common/views.py index da21b41e..60af379b 100644 --- a/common/views.py +++ b/common/views.py @@ -69,21 +69,16 @@ def nodeinfo2(request): def error_400(request, exception=None): - return render( - request, - "400.html", - {"exception": exception}, - status=400, - ) + return render(request, "400.html", status=400, context={"exception": exception}) def error_403(request, exception=None): - return render(request, "403.html", status=403) + return render(request, "403.html", status=403, context={"exception": exception}) def error_404(request, exception=None): - return render(request, "404.html", status=404) + return render(request, "404.html", status=404, context={"exception": exception}) def error_500(request, exception=None): - return render(request, "500.html", status=500) + return render(request, "500.html", status=500, context={"exception": exception}) diff --git a/journal/views/collection.py b/journal/views/collection.py index 3f6ebdf5..d0e5261b 100644 --- a/journal/views/collection.py +++ b/journal/views/collection.py @@ -6,17 +6,11 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods from catalog.models import Item from common.utils import AuthedHttpRequest, get_uuid_or_404 from mastodon.api import boost_toot_later, share_collection -from users.models import User -from users.models.apidentity import APIdentity -from users.views import ( - render_user_blocked, - render_user_noanonymous, - render_user_not_found, -) from ..forms import * from ..models import * @@ -55,7 +49,7 @@ def collection_retrieve_redirect(request: AuthedHttpRequest, collection_uuid): def collection_retrieve(request: AuthedHttpRequest, collection_uuid): collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) follower_count = collection.likes.all().count() following = ( Like.user_liked_piece(request.user.identity, collection) @@ -101,12 +95,11 @@ def collection_retrieve(request: AuthedHttpRequest, collection_uuid): @login_required +@require_http_methods(["POST"]) def collection_add_featured(request: AuthedHttpRequest, collection_uuid): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) FeaturedCollection.objects.update_or_create( owner=request.user.identity, target=collection ) @@ -114,12 +107,11 @@ def collection_add_featured(request: AuthedHttpRequest, collection_uuid): @login_required +@require_http_methods(["POST"]) def collection_remove_featured(request: AuthedHttpRequest, collection_uuid): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) fc = FeaturedCollection.objects.filter( owner=request.user.identity, target=collection ).first() @@ -129,15 +121,16 @@ def collection_remove_featured(request: AuthedHttpRequest, collection_uuid): @login_required +@require_http_methods(["POST", "GET"]) def collection_share(request: AuthedHttpRequest, collection_uuid): collection = get_object_or_404( Collection, uid=get_uuid_or_404(collection_uuid) if collection_uuid else None ) if collection and not collection.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.method == "GET": return render(request, "collection_share.html", {"collection": collection}) - elif request.method == "POST": + else: comment = request.POST.get("comment") # boost if possible, otherwise quote if ( @@ -158,8 +151,6 @@ def collection_share(request: AuthedHttpRequest, collection_uuid): ): return render_relogin(request) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) - else: - raise BadRequest() def collection_retrieve_items( @@ -167,7 +158,7 @@ def collection_retrieve_items( ): collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) form = CollectionForm(instance=collection) return render( request, @@ -182,12 +173,11 @@ def collection_retrieve_items( @login_required +@require_http_methods(["POST"]) def collection_append_item(request: AuthedHttpRequest, collection_uuid): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) url = request.POST.get("url") note = request.POST.get("note") @@ -202,26 +192,24 @@ def collection_append_item(request: AuthedHttpRequest, collection_uuid): @login_required +@require_http_methods(["POST"]) def collection_remove_item(request: AuthedHttpRequest, collection_uuid, item_uuid): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) collection.remove_item(item) return collection_retrieve_items(request, collection_uuid, True) @login_required +@require_http_methods(["POST"]) def collection_move_item( request: AuthedHttpRequest, direction, collection_uuid, item_uuid ): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if direction == "up": collection.move_up_item(item) @@ -231,45 +219,44 @@ def collection_move_item( @login_required +@require_http_methods(["POST"]) def collection_update_member_order(request: AuthedHttpRequest, collection_uuid): - if request.method != "POST": - raise BadRequest() collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) ids = request.POST.get("member_ids", "").strip() if not ids: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) ordered_member_ids = [int(i) for i in ids.split(",")] collection.update_member_order(ordered_member_ids) return collection_retrieve_items(request, collection_uuid, True) @login_required +@require_http_methods(["GET", "POST"]) def collection_update_item_note(request: AuthedHttpRequest, collection_uuid, item_uuid): collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) 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": + else: member = collection.get_member_for_item(item) return render( request, "collection_update_item_note.html", {"collection": collection, "item": item, "note": member.note}, ) - else: - raise BadRequest() @login_required +@require_http_methods(["GET", "POST"]) def collection_edit(request: AuthedHttpRequest, collection_uuid=None): collection = ( get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid)) @@ -277,7 +264,7 @@ def collection_edit(request: AuthedHttpRequest, collection_uuid=None): else None ) if collection and not collection.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.method == "GET": form = CollectionForm(instance=collection) if collection else CollectionForm() if request.GET.get("title"): @@ -292,7 +279,7 @@ def collection_edit(request: AuthedHttpRequest, collection_uuid=None): "identity": collection.owner if collection else request.user.identity, }, ) - elif request.method == "POST": + else: form = ( CollectionForm(request.POST, request.FILES, instance=collection) if collection @@ -306,16 +293,14 @@ def collection_edit(request: AuthedHttpRequest, collection_uuid=None): reverse("journal:collection_retrieve", args=[form.instance.uuid]) ) else: - raise BadRequest() - else: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) @target_identity_required def user_collection_list(request: AuthedHttpRequest, user_name): target = request.target_identity if not request.user.is_authenticated and not target.anonymous_viewable: - return render_user_noanonymous(request) + raise PermissionDenied(_("Login required")) collections = ( Collection.objects.filter(owner=target) .filter(q_owned_piece_visible_to_user(request.user, target)) @@ -336,7 +321,7 @@ def user_collection_list(request: AuthedHttpRequest, user_name): def user_liked_collection_list(request: AuthedHttpRequest, user_name): target = request.target_identity if not request.user.is_authenticated and not target.anonymous_viewable: - return render_user_noanonymous(request) + raise PermissionDenied(_("Login required")) collections = Collection.objects.filter( interactions__identity=target, interactions__interaction_type="like", diff --git a/journal/views/common.py b/journal/views/common.py index 9b24ffc2..639adcef 100644 --- a/journal/views/common.py +++ b/journal/views/common.py @@ -7,6 +7,7 @@ from django.db.models import F, Min, OuterRef, Subquery from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods from catalog.models import * from common.utils import ( @@ -77,7 +78,7 @@ def render_list( elif type == "review" and item_category: queryset = Review.objects.filter(q_item_in_category(item_category)) else: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) if sort == "rating": rating = Rating.objects.filter( owner_id=OuterRef("owner_id"), item_id=OuterRef("item_id") @@ -125,17 +126,16 @@ def render_list( @login_required +@require_http_methods(["GET", "POST"]) def piece_delete(request, piece_uuid): piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid)) return_url = request.GET.get("return_url", None) or "/" if not piece.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.method == "GET": return render( request, "piece_delete.html", {"piece": piece, "return_url": return_url} ) - elif request.method == "POST": + else: piece.delete() return redirect(return_url) - else: - raise BadRequest() diff --git a/journal/views/mark.py b/journal/views/mark.py index ade62155..30f8169b 100644 --- a/journal/views/mark.py +++ b/journal/views/mark.py @@ -9,6 +9,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from django.utils.dateparse import parse_datetime from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods from catalog.models import * from common.utils import AuthedHttpRequest, get_uuid_or_404 @@ -25,12 +26,9 @@ _checkmark = "✔️".encode("utf-8") @login_required +@require_http_methods(["POST"]) def wish(request: AuthedHttpRequest, item_uuid): - if request.method != "POST": - raise BadRequest() item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - if not item: - raise Http404() mark = Mark(request.user.identity, item) if not mark.shelf_type: mark.update(ShelfType.WISHLIST) @@ -40,6 +38,7 @@ def wish(request: AuthedHttpRequest, item_uuid): @login_required +@require_http_methods(["GET", "POST"]) def mark(request: AuthedHttpRequest, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) mark = Mark(request.user.identity, item) @@ -59,7 +58,7 @@ def mark(request: AuthedHttpRequest, item_uuid): "date_today": timezone.localdate().isoformat(), }, ) - elif request.method == "POST": + else: if request.POST.get("delete", default=False): mark.delete() return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @@ -120,27 +119,28 @@ def mark(request: AuthedHttpRequest, item_uuid): }, ) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) - raise BadRequest() @login_required +@require_http_methods(["POST"]) def mark_log(request: AuthedHttpRequest, item_uuid, log_id): """ Delete log of one item by log id. """ item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) mark = Mark(request.user.identity, item) - if request.method == "POST": - if request.GET.get("delete", default=False): - if log_id: - mark.delete_log(log_id) - else: - mark.delete_all_logs() - return render(request, "_item_user_mark_history.html", {"mark": mark}) - raise BadRequest() + if request.GET.get("delete", default=False): + if log_id: + mark.delete_log(log_id) + else: + mark.delete_all_logs() + return render(request, "_item_user_mark_history.html", {"mark": mark}) + else: + raise BadRequest(_("Invalid parameter")) @login_required +@require_http_methods(["GET", "POST"]) def comment(request: AuthedHttpRequest, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) if item.class_name not in ["podcastepisode", "tvepisode"]: @@ -155,10 +155,10 @@ def comment(request: AuthedHttpRequest, item_uuid): "comment": comment, }, ) - elif request.method == "POST": + else: if request.POST.get("delete", default=False): if not comment: - raise Http404() + raise Http404(_("Content not found")) comment.delete() return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) visibility = int(request.POST.get("visibility", default=0)) @@ -176,9 +176,9 @@ def comment(request: AuthedHttpRequest, item_uuid): d = {"text": text, "visibility": visibility} if position: d["metadata"] = {"position": position} - comment, _ = Comment.objects.update_or_create( + comment = Comment.objects.update_or_create( owner=request.user.identity, item=item, defaults=d - ) + )[0] post = Takahe.post_comment(comment, False) share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False)) if post and share_to_mastodon: @@ -187,7 +187,6 @@ def comment(request: AuthedHttpRequest, item_uuid): else: boost_toot_later(request.user, post.url) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) - raise BadRequest() def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category): diff --git a/journal/views/post.py b/journal/views/post.py index f6959b90..2cf1faa1 100644 --- a/journal/views/post.py +++ b/journal/views/post.py @@ -17,7 +17,7 @@ from ..models import * def piece_replies(request: AuthedHttpRequest, piece_uuid: str): piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid)) if not piece.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) replies = piece.get_replies(request.user.identity) return render( request, "replies.html", {"post": piece.latest_post, "replies": replies} @@ -38,7 +38,7 @@ def post_reply(request: AuthedHttpRequest, post_id: int): content = request.POST.get("content", "").strip() visibility = Takahe.Visibilities(int(request.POST.get("visibility", -1))) if not content: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) Takahe.reply_post(post_id, request.user.identity.pk, content, visibility) replies = Takahe.get_replies_for_posts([post_id], request.user.identity.pk) return render( @@ -52,7 +52,7 @@ def post_boost(request: AuthedHttpRequest, post_id: int): # classic_repost = request.user.preference.mastodon_repost_mode == 1 post = Takahe.get_post(post_id) if not post: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) if request.user.mastodon_site: boost_toot_later(request.user, post.object_uri) else: @@ -70,7 +70,5 @@ def post_like(request: AuthedHttpRequest, post_id: int): @require_http_methods(["POST"]) @login_required def post_unlike(request: AuthedHttpRequest, post_id: int): - if request.method != "POST": - raise BadRequest() Takahe.unlike_post(post_id, request.user.identity.pk) return render(request, "action_like_post.html", {"post": Takahe.get_post(post_id)}) diff --git a/journal/views/review.py b/journal/views/review.py index f04dc797..62820e6d 100644 --- a/journal/views/review.py +++ b/journal/views/review.py @@ -27,13 +27,14 @@ def review_retrieve(request, review_uuid): # piece = get_object_or_404(Review, uid=get_uuid_or_404(review_uuid)) piece = Review.get_by_url(review_uuid) if piece is None: - raise Http404() + raise Http404(_("Content not found")) if not piece.is_visible_to(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) return render(request, "review.html", {"review": piece}) @login_required +@require_http_methods(["GET", "POST"]) def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) review = ( @@ -42,7 +43,7 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None): else None ) if review and not review.is_editable_by(request.user): - raise PermissionDenied() + raise PermissionDenied(_("Insufficient permission")) if request.method == "GET": form = ( ReviewForm(instance=review) @@ -63,7 +64,7 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None): "date_today": timezone.localdate().isoformat(), }, ) - elif request.method == "POST": + else: form = ( ReviewForm(request.POST, instance=review) if review @@ -89,12 +90,10 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None): form.cleaned_data["share_to_mastodon"], ) if not review: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) return redirect(reverse("journal:review_retrieve", args=[review.uuid])) else: - raise BadRequest() - else: - raise BadRequest() + raise BadRequest(_("Invalid parameter")) def user_review_list(request, user_name, item_category): @@ -112,7 +111,7 @@ class ReviewFeed(Feed): return ( _("Reviews by {0}").format(owner.display_name) if owner - else _("link unavailable") + else _("Link invalid") ) def link(self, owner): @@ -120,9 +119,9 @@ class ReviewFeed(Feed): def description(self, owner): if not owner: - return _("link unavailable") + return _("Link invalid") elif not owner.anonymous_viewable: - return _("anonymous access disabled by owner") + return _("Login required") else: return _("Reviews by {0}").format(owner.display_name) diff --git a/journal/views/tag.py b/journal/views/tag.py index bfee5828..df660679 100644 --- a/journal/views/tag.py +++ b/journal/views/tag.py @@ -1,15 +1,13 @@ from django.contrib.auth.decorators import login_required -from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied from django.db.models import Count from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods from user_messages import api as msg from catalog.models import * -from users.models import User -from users.views import render_user_blocked, render_user_not_found from ..forms import * from ..models import * @@ -38,16 +36,17 @@ def user_tag_list(request, user_name): @login_required +@require_http_methods(["GET", "POST"]) def user_tag_edit(request): if request.method == "GET": tag_title = Tag.cleanup_title(request.GET.get("tag", ""), replace=False) if not tag_title: - raise Http404() + raise Http404(_("Invalid tag")) tag = Tag.objects.filter(owner=request.user.identity, title=tag_title).first() if not tag: - raise Http404() + raise Http404(_("Tag not found")) return render(request, "tag_edit.html", {"tag": tag}) - elif request.method == "POST": + else: tag_title = Tag.cleanup_title(request.POST.get("title", ""), replace=False) tag_id = request.POST.get("id") tag = ( @@ -56,7 +55,7 @@ def user_tag_edit(request): else None ) if not tag or not tag_title: - msg.error(request.user, _("Invalid tag.")) + msg.error(request.user, _("Invalid tag")) return HttpResponseRedirect(request.META.get("HTTP_REFERER")) if request.POST.get("delete"): tag.delete() @@ -83,7 +82,6 @@ def user_tag_edit(request): args=[request.user.username, tag.title], ) ) - raise BadRequest() def user_tag_member_list(request, user_name, tag_title): diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po index df97422b..aa7d10f1 100644 --- a/locale/zh_Hans/LC_MESSAGES/django.po +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-20 12:47-0400\n" +"POT-Creation-Date: 2024-04-24 01:50-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -578,6 +578,10 @@ msgstr "结束日期" msgid "host" msgstr "主播" +#: catalog/search/views.py:54 catalog/search/views.py:178 +msgid "Invalid URL" +msgstr "无效网址" + #: catalog/templates/_item_card_metadata_edition.html:17 #: catalog/templates/edition.html:54 msgid "年" @@ -1129,59 +1133,113 @@ msgstr "" msgid "season number" msgstr "" -#: catalog/views_edit.py:259 +#: catalog/views.py:53 catalog/views.py:76 +msgid "Item not found" +msgstr "" + +#: catalog/views.py:57 catalog/views.py:84 +msgid "Item no longer exists" +msgstr "" + +#: catalog/views_edit.py:125 catalog/views_edit.py:148 +#: catalog/views_edit.py:200 catalog/views_edit.py:276 +#: catalog/views_edit.py:353 journal/views/collection.py:52 +#: journal/views/collection.py:102 journal/views/collection.py:114 +#: journal/views/collection.py:130 journal/views/collection.py:161 +#: journal/views/collection.py:180 journal/views/collection.py:200 +#: journal/views/collection.py:212 journal/views/collection.py:226 +#: journal/views/collection.py:240 journal/views/collection.py:243 +#: journal/views/collection.py:267 journal/views/common.py:134 +#: journal/views/post.py:20 journal/views/review.py:32 +#: journal/views/review.py:46 +msgid "Insufficient permission" +msgstr "" + +#: catalog/views_edit.py:203 journal/views/collection.py:229 +#: journal/views/collection.py:296 journal/views/common.py:81 +#: journal/views/mark.py:139 journal/views/post.py:41 journal/views/post.py:55 +#: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169 +msgid "Invalid parameter" +msgstr "无效参数" + +#: catalog/views_edit.py:252 +msgid "Must be a TV Season with IMDB id and season id" +msgstr "" + +#: catalog/views_edit.py:257 msgid "Updating episodes" msgstr "" -#: catalog/views_edit.py:289 +#: catalog/views_edit.py:287 msgid "Cannot be merged to an item already deleted or merged" msgstr "" -#: catalog/views_edit.py:292 +#: catalog/views_edit.py:290 msgid "Cannot merge items in different categories" msgstr "" -#: catalog/views_edit.py:329 +#: catalog/views_edit.py:327 msgid "Cannot be linked to an item already deleted or merged" msgstr "" -#: catalog/views_edit.py:331 +#: catalog/views_edit.py:329 msgid "Cannot link items other than editions" msgstr "" -#: common/templates/400.html:11 -msgid "无效请求" -msgstr "" +#: common/templates/400.html:22 +msgid "" +"You may have submitted invalid data, or the content may have been deleted by " +"the author." +msgstr "您可能提交了无效的数据,或者相关内容已被作者删除。" -#: common/templates/403.html:11 -msgid "权限不符" -msgstr "" +#: common/templates/400.html:24 common/templates/403.html:24 +#: common/templates/404.html:24 +msgid "" +"If you believe this is our mistake, please contact us through the link at " +"the bottom of the page." +msgstr "如果您确信这是我们的错误,请通过页面底部的链接联系我们。" -#: common/templates/404.html:11 -msgid "未找到" -msgstr "" +#: common/templates/403.html:22 +msgid "" +"Author may require you to log in before accessing this content, or you do " +"not have permission to view it." +msgstr "作者可能希望您登录后访问,或者您没有权限查看此内容。" -#: common/templates/500.html:11 -msgid "系统错误" -msgstr "" +#: common/templates/404.html:22 +msgid "" +"You may have visited an incorrect URL, or the content you are looking for " +"has been deleted by the author." +msgstr "您可能访问了错误的网址,或者相关内容已被作者删除。" -#: common/templates/_footer.html:11 +#: common/templates/500.html:22 +msgid "" +"An internal error occurred. If this error occurs repeatedly, it will be " +"recorded and handled by a human." +msgstr "发生了一个内部错误,如果这个错误多次出现,后台会记录并会由人工处理。" + +#: common/templates/500.html:24 +msgid "" +"If you have an urgent situation or any questions, please contact us through " +"the link at the bottom of the page." +msgstr "如果您有紧急情况或任何疑问,请通过页面底部的链接联系我们。" + +#: common/templates/_footer.html:6 msgid "Rules" msgstr "站点规则" -#: common/templates/_footer.html:12 +#: common/templates/_footer.html:7 msgid "Terms" msgstr "服务协议" -#: common/templates/_footer.html:13 +#: common/templates/_footer.html:8 msgid "Announcements" msgstr "公告栏" -#: common/templates/_footer.html:14 +#: common/templates/_footer.html:9 msgid "Developer" msgstr "开发者" -#: common/templates/_footer.html:19 +#: common/templates/_footer.html:13 msgid "Source Code" msgstr "源代码" @@ -1294,71 +1352,83 @@ msgstr "关注了你" msgid "just now" msgstr "刚刚" +#: common/utils.py:61 common/utils.py:91 users/views.py:35 users/views.py:121 +msgid "User not found" +msgstr "用户不存在" + +#: common/utils.py:65 common/utils.py:95 users/views.py:124 +msgid "User no longer exists" +msgstr "用户不存在了" + +#: common/utils.py:73 common/utils.py:103 +msgid "Access denied" +msgstr "访问被拒绝" + #: developer/models.py:18 msgid "minimum two characters, words and -_. only, no special characters" -msgstr "" +msgstr "两字符以上,无特殊字符" #: developer/models.py:29 msgid "Allowed URIs list, space separated, at least one URI is required" -msgstr "" +msgstr "至少一个网址,空格分隔" #: developer/templates/console.html:37 #: developer/templates/oauth2_provider/application_list.html:5 msgid "Your applications" -msgstr "" +msgstr "你的应用程序" #: developer/templates/oauth2_provider/application_detail.html:9 msgid "Client ID" -msgstr "" +msgstr "Client ID" #: developer/templates/oauth2_provider/application_detail.html:18 msgid "URL" -msgstr "" +msgstr "网址" #: developer/templates/oauth2_provider/application_detail.html:24 msgid "Description" -msgstr "" +msgstr "描述" #: developer/templates/oauth2_provider/application_detail.html:30 msgid "Redirect Uris" -msgstr "" +msgstr "重定向网址" #: developer/templates/oauth2_provider/application_detail.html:38 #: developer/templates/oauth2_provider/application_form.html:33 msgid "Go Back" -msgstr "" +msgstr "返回" #: developer/templates/oauth2_provider/application_detail.html:40 msgid "Edit" -msgstr "" +msgstr "编辑" #: developer/templates/oauth2_provider/application_detail.html:42 msgid "Delete" -msgstr "" +msgstr "删除" #: developer/templates/oauth2_provider/application_form.html:8 msgid "Edit application" -msgstr "" +msgstr "编辑应用程序" #: developer/templates/oauth2_provider/application_form.html:35 msgid "Save" -msgstr "" +msgstr "保存" #: developer/templates/oauth2_provider/application_list.html:20 msgid "New Application" -msgstr "" +msgstr "新建应用程序" #: developer/templates/oauth2_provider/application_list.html:23 msgid "No applications defined" -msgstr "" +msgstr "尚无应用程序" #: developer/templates/oauth2_provider/application_list.html:23 msgid "Click here" -msgstr "" +msgstr "点此" #: developer/templates/oauth2_provider/application_list.html:23 msgid "if you want to register a new one" -msgstr "" +msgstr "如果你希望新注册一个" #: journal/forms.py:18 journal/forms.py:41 msgid "Title" @@ -1790,7 +1860,7 @@ msgstr "" msgid "喜欢的收藏单" msgstr "" -#: journal/templates/replies.html:39 social/templates/events.html:46 +#: journal/templates/replies.html:46 social/templates/events.html:46 msgid "nothing so far." msgstr "暂无内容。" @@ -1824,83 +1894,92 @@ msgstr "" msgid "分享" msgstr "" -#: journal/views/collection.py:44 +#: journal/views/collection.py:38 #, python-brace-format msgid "Collection by {0}" msgstr "{0} 的收藏单" -#: journal/views/collection.py:200 +#: journal/views/collection.py:190 msgid "Unable to find the item, please use item url from this site." msgstr "" -#: journal/views/common.py:32 journal/views/mark.py:118 +#: journal/views/collection.py:303 journal/views/collection.py:324 +#: journal/views/review.py:124 +msgid "Login required" +msgstr "登录后访问" + +#: journal/views/common.py:33 journal/views/mark.py:117 msgid "Data saved but unable to repost to Fediverse." -msgstr "" +msgstr "数据已保存但未能转发到联邦宇宙。" -#: journal/views/common.py:34 +#: journal/views/common.py:35 msgid "Redirecting to your Mastodon instance now to re-authenticate." -msgstr "" +msgstr "正在重定向到你的Mastodon实例以重新认证。" -#: journal/views/common.py:41 +#: journal/views/common.py:42 msgid "List not found." -msgstr "" +msgstr "列表未找到" -#: journal/views/mark.py:110 +#: journal/views/mark.py:109 msgid "Content too long for your Mastodon instance." -msgstr "" +msgstr "内容过长,超出了你的Mastodon实例的限制。" -#: journal/views/review.py:113 journal/views/review.py:127 +#: journal/views/mark.py:161 journal/views/review.py:30 +msgid "Content not found" +msgstr "内容未找到" + +#: journal/views/review.py:112 journal/views/review.py:126 #, python-brace-format msgid "Reviews by {0}" -msgstr "" +msgstr "{0} 的评论" -#: journal/views/review.py:115 journal/views/review.py:123 -msgid "link unavailable" -msgstr "" +#: journal/views/review.py:114 journal/views/review.py:122 +msgid "Link invalid" +msgstr "链接无效" -#: journal/views/review.py:125 -msgid "anonymous access disabled by owner" -msgstr "" - -#: journal/views/review.py:136 +#: journal/views/review.py:135 #, python-brace-format msgid "{review_title} - a review of {item_title}" -msgstr "" +msgstr "{review_title} - 关于 {item_title} 的评论" -#: journal/views/tag.py:59 -msgid "Invalid tag." -msgstr "" +#: journal/views/tag.py:43 journal/views/tag.py:57 +msgid "Invalid tag" +msgstr "无效标签" -#: journal/views/tag.py:63 +#: journal/views/tag.py:46 +msgid "Tag not found" +msgstr "标签不存在" + +#: journal/views/tag.py:61 msgid "Tag deleted." -msgstr "" +msgstr "标签已删除" -#: journal/views/tag.py:73 +#: journal/views/tag.py:71 msgid "Duplicated tag." -msgstr "" +msgstr "重复标签" -#: journal/views/tag.py:79 +#: journal/views/tag.py:77 msgid "Tag updated." -msgstr "" +msgstr "标签已更新" #: journal/views/wrapped.py:141 msgid "Summary posted to timeline." -msgstr "" +msgstr "总结已发布到时间轴" #: mastodon/api.py:489 takahe/utils.py:514 #, python-brace-format msgid "regarding {item_title}, may contain spoiler or triggering content" msgstr "关于 {item_title},可能包含剧透或敏感内容" -#: mastodon/api.py:639 +#: mastodon/api.py:646 msgid "collection" msgstr "收藏单" -#: mastodon/api.py:644 +#: mastodon/api.py:651 msgid "shared my collection" msgstr "分享我的收藏单" -#: mastodon/api.py:647 +#: mastodon/api.py:654 #, python-brace-format msgid "shared {username}'s collection" msgstr "分享 {username} 的收藏单" @@ -2007,14 +2086,18 @@ msgstr "转播了你的帖文" msgid "" "\n" "boosted your collection %(piece_title)s\n" -msgstr "\n转播了你的收藏单 %(piece_title)s\n" +msgstr "" +"\n" +"转播了你的收藏单 %(piece_title)s\n" #: social/templates/event/boosted_comment.html:3 #, python-format msgid "" "\n" "boosted your comment on %(item_title)s\n" -msgstr "\n转播了你对 %(item_title)s 的短评\n" +msgstr "" +"\n" +"转播了你对 %(item_title)s 的短评\n" #: social/templates/event/boosted_review.html:2 #, python-format @@ -2023,7 +2106,8 @@ msgid "" "boosted your review %(piece_title)s on %(item_title)s\n" msgstr "" -"\n转播了你对 %(item_title)s 的评论 %(item_title)s 的评论 %(piece_title)s\n" #: social/templates/event/boosted_shelfmember.html:3 @@ -2031,7 +2115,9 @@ msgstr "" msgid "" "\n" "boosted your mark on %(item_title)s\n" -msgstr "\n转播了你对 %(item_title)s 的标记\n" +msgstr "" +"\n" +"转播了你对 %(item_title)s 的标记\n" #: social/templates/event/follow_requested.html:3 msgid "requested to follow you" @@ -2050,14 +2136,18 @@ msgstr "赞了你的帖文" msgid "" "\n" "liked your collection %(piece_title)s\n" -msgstr "\n赞了你的收藏单 %(piece_title)s\n" +msgstr "" +"\n" +"赞了你的收藏单 %(piece_title)s\n" #: social/templates/event/liked_comment.html:3 #, python-format msgid "" "\n" "liked your comment on %(item_title)s\n" -msgstr "\n赞了你对 %(item_title)s 的短评\n" +msgstr "" +"\n" +"赞了你对 %(item_title)s 的短评\n" #: social/templates/event/liked_review.html:2 #, python-format @@ -2066,7 +2156,8 @@ msgid "" "liked your review %(piece_title)s on %(item_title)s\n" msgstr "" -"\n赞了你对 %(item_title)s 的评论 %(item_title)s 的评论 %(piece_title)s\n" #: social/templates/event/liked_shelfmember.html:3 @@ -2074,7 +2165,9 @@ msgstr "" msgid "" "\n" "liked your mark on %(item_title)s\n" -msgstr "\n赞了你对 %(item_title)s 的标记\n" +msgstr "" +"\n" +"赞了你对 %(item_title)s 的标记\n" #: social/templates/event/mentioned.html:3 msgid "mentioned you" @@ -2085,14 +2178,18 @@ msgstr "提到了你" msgid "" "\n" "replied to your collection %(piece_title)s\n" -msgstr "\n回应了你的收藏单 %(piece_title)s\n" +msgstr "" +"\n" +"回应了你的收藏单 %(piece_title)s\n" #: social/templates/event/mentioned_comment.html:3 #, python-format msgid "" "\n" "replied to your comment on %(item_title)s\n" -msgstr "\n回应了你对 %(item_title)s 的短评\n" +msgstr "" +"\n" +"回应了你对 %(item_title)s 的短评\n" #: social/templates/event/mentioned_review.html:2 #, python-format @@ -2101,7 +2198,8 @@ msgid "" "replied to your review %(piece_title)s on %(item_title)s\n" msgstr "" -"\n回应了你对 %(item_title)s 的评论 %(item_title)s 的评论 %(piece_title)s\n" #: social/templates/event/mentioned_shelfmember.html:3 @@ -2109,7 +2207,9 @@ msgstr "" msgid "" "\n" "replied to your mark on %(item_title)s\n" -msgstr "\n回应了你对 %(item_title)s 的标记\n" +msgstr "" +"\n" +"回应了你对 %(item_title)s 的标记\n" #: social/templates/events.html:44 msgid "nothing more." @@ -2167,114 +2267,118 @@ msgstr "" msgid "created collection" msgstr "" -#: users/account.py:87 +#: users/account.py:86 msgid "无效的电子邮件地址" msgstr "" -#: users/account.py:105 +#: users/account.py:104 msgid "验证邮件已发送" msgstr "" -#: users/account.py:106 +#: users/account.py:105 msgid "请查阅收件箱" msgstr "" -#: users/account.py:156 users/account.py:163 users/account.py:173 +#: users/account.py:155 users/account.py:162 users/account.py:172 msgid "认证失败😫" msgstr "" -#: users/account.py:156 +#: users/account.py:155 msgid "Mastodon服务未能返回有效认证信息" msgstr "" -#: users/account.py:163 +#: users/account.py:162 msgid "无效会话信息" msgstr "" -#: users/account.py:173 +#: users/account.py:167 +msgid "Invalid instance domain" +msgstr "" + +#: users/account.py:172 msgid "Mastodon服务未能返回有效认证令牌" msgstr "" -#: users/account.py:190 +#: users/account.py:189 msgid "联邦宇宙访问失败😫" msgstr "" -#: users/account.py:212 +#: users/account.py:211 msgid "注册失败😫" msgstr "" -#: users/account.py:213 +#: users/account.py:212 msgid "本站仅限邀请注册" msgstr "" -#: users/account.py:279 +#: users/account.py:273 msgid "This username is already in use." msgstr "" -#: users/account.py:290 +#: users/account.py:284 msgid "This email address is already in use." msgstr "" -#: users/account.py:342 users/account.py:351 +#: users/account.py:336 users/account.py:345 msgid "无效的验证码" msgstr "" -#: users/account.py:370 +#: users/account.py:364 msgid "链接无效或已过期" msgstr "" -#: users/account.py:387 users/account.py:393 +#: users/account.py:381 users/account.py:387 msgid "电子邮件地址不匹配" msgstr "" -#: users/account.py:397 +#: users/account.py:391 msgid "此电子邮件地址已被注册" msgstr "" -#: users/account.py:402 +#: users/account.py:396 msgid "无法完成验证" msgstr "" -#: users/account.py:434 +#: users/account.py:428 msgid "用户名已被使用" msgstr "" -#: users/account.py:449 +#: users/account.py:443 msgid "电子邮件地址已被使用" msgstr "" -#: users/account.py:467 +#: users/account.py:461 msgid "已发送验证邮件,请查收。" msgstr "" -#: users/account.py:471 +#: users/account.py:465 msgid "用户名已设置。" msgstr "" -#: users/account.py:473 +#: users/account.py:467 msgid "电子邮件地址已取消关联。" msgstr "" -#: users/account.py:491 +#: users/account.py:485 #, python-brace-format msgid "该身份 {username}@{site} 与当前账号相同。" msgstr "" -#: users/account.py:499 +#: users/account.py:493 #, python-brace-format msgid "该身份 {username}@{site} 已被用于其它账号。" msgstr "" -#: users/account.py:523 +#: users/account.py:517 #, python-brace-format msgid "账号身份已更新为 {username}@{site}。" msgstr "" -#: users/account.py:526 +#: users/account.py:520 msgid "连接联邦宇宙获取身份信息失败。" msgstr "" -#: users/account.py:575 +#: users/account.py:569 msgid "验证信息不符。" msgstr "" @@ -2534,18 +2638,5 @@ msgstr "" msgid "验证电子邮件" msgstr "" -#: users/views.py:25 -msgid "😖哎呀,这位用户好像还没有加入本站,快去联邦宇宙呼唤TA来注册吧!" -msgstr "" - -#: users/views.py:26 -msgid "未找到用户" -msgstr "" - -#: users/views.py:38 -msgid "没有访问该用户主页的权限" -msgstr "" - -#: users/views.py:49 -msgid "作者已设置仅限登录用户查看" -msgstr "" +#~ msgid "Invalid tag." +#~ msgstr "无效标签" diff --git a/pyproject.toml b/pyproject.toml index 36407d92..2925f380 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ reportIncompatibleVariableOverride = false reportUnusedImport = false [tool.djlint] -ignore="T002,T003,H006,H019,H020,H021,H023,H030,H031,D018" +ignore="T002,T003,H005,H006,H019,H020,H021,H023,H030,H031,D018" indent=2 [tool.isort] diff --git a/users/account.py b/users/account.py index 2c226081..6f175a36 100644 --- a/users/account.py +++ b/users/account.py @@ -32,46 +32,45 @@ from takahe.utils import Takahe from .models import Preference, User from .tasks import * - # the 'login' page that user can see -def login(request): - if request.method == "GET": - selected_site = request.GET.get("site", default="") +require_http_methods(["GET"]) - cache_key = "login_sites" - sites = cache.get(cache_key, []) - if not sites: - sites = list( - User.objects.filter(is_active=True) - .values("mastodon_site") - .annotate(total=Count("mastodon_site")) - .order_by("-total") - .values_list("mastodon_site", flat=True) - ) - cache.set(cache_key, sites, timeout=3600 * 8) - # store redirect url in the cookie - if request.GET.get("next"): - request.session["next_url"] = request.GET.get("next") - invite_status = -1 if settings.INVITE_ONLY else 0 - if settings.INVITE_ONLY and request.GET.get("invite"): - if Takahe.verify_invite(request.GET.get("invite")): - invite_status = 1 - request.session["invite"] = request.GET.get("invite") - else: - invite_status = -2 - return render( - request, - "users/login.html", - { - "sites": sites, - "scope": quote(settings.MASTODON_CLIENT_SCOPE), - "selected_site": selected_site, - "allow_any_site": settings.MASTODON_ALLOW_ANY_SITE, - "invite_status": invite_status, - }, + +def login(request): + selected_site = request.GET.get("site", default="") + + cache_key = "login_sites" + sites = cache.get(cache_key, []) + if not sites: + sites = list( + User.objects.filter(is_active=True) + .values("mastodon_site") + .annotate(total=Count("mastodon_site")) + .order_by("-total") + .values_list("mastodon_site", flat=True) ) - else: - raise BadRequest() + cache.set(cache_key, sites, timeout=3600 * 8) + # store redirect url in the cookie + if request.GET.get("next"): + request.session["next_url"] = request.GET.get("next") + invite_status = -1 if settings.INVITE_ONLY else 0 + if settings.INVITE_ONLY and request.GET.get("invite"): + if Takahe.verify_invite(request.GET.get("invite")): + invite_status = 1 + request.session["invite"] = request.GET.get("invite") + else: + invite_status = -2 + return render( + request, + "users/login.html", + { + "sites": sites, + "scope": quote(settings.MASTODON_CLIENT_SCOPE), + "selected_site": selected_site, + "allow_any_site": settings.MASTODON_ALLOW_ANY_SITE, + "invite_status": invite_status, + }, + ) # connect will send verification email or redirect to mastodon server @@ -165,7 +164,7 @@ def connect_redirect_back(request): try: token, refresh_token = obtain_token(site, request, code) except ObjectDoesNotExist: - raise BadRequest() + raise BadRequest(_("Invalid instance domain")) if not token: return render( request, @@ -239,24 +238,19 @@ def login_existing_user(request, existing_user): @mastodon_request_included @login_required def logout(request): - if request.method == "GET": - # revoke_token(request.user.mastodon_site, request.user.mastodon_token) - return auth_logout(request) - else: - raise BadRequest() + # revoke_token(request.user.mastodon_site, request.user.mastodon_token) + return auth_logout(request) @mastodon_request_included @login_required +@require_http_methods(["POST"]) def reconnect(request): if request.META.get("HTTP_AUTHORIZATION"): raise BadRequest("Only for web login") - if request.method == "POST": - request.session["swap_login"] = True - request.session["swap_domain"] = request.POST["domain"] - return connect(request) - else: - raise BadRequest() + request.session["swap_login"] = True + request.session["swap_domain"] = request.POST["domain"] + return connect(request) class RegistrationForm(forms.ModelForm): diff --git a/users/views.py b/users/views.py index be495afa..2588d825 100644 --- a/users/views.py +++ b/users/views.py @@ -2,7 +2,7 @@ import json from django.contrib.auth.decorators import login_required from django.core.exceptions import BadRequest -from django.http import HttpResponseRedirect +from django.http import Http404, HttpResponseRedirect from django.shortcuts import redirect, render from django.urls import reverse from django.utils.translation import gettext_lazy as _ @@ -21,41 +21,6 @@ from .data import * from .models import APIdentity -def render_user_not_found(request, user_name=""): - sec_msg = _("😖哎呀,这位用户好像还没有加入本站,快去联邦宇宙呼唤TA来注册吧!") - msg = _("未找到用户") + user_name - return render( - request, - "common/error.html", - { - "msg": msg, - "secondary_msg": sec_msg, - }, - ) - - -def render_user_blocked(request): - msg = _("没有访问该用户主页的权限") - return render( - request, - "common/error.html", - { - "msg": msg, - }, - ) - - -def render_user_noanonymous(request): - msg = _("作者已设置仅限登录用户查看") - return render( - request, - "common/error.html", - { - "msg": msg, - }, - ) - - def query_identity(request, handle): try: i = APIdentity.get_by_handle(handle) @@ -67,7 +32,7 @@ def query_identity(request, handle): request, "users/fetch_identity_pending.html", {"handle": handle} ) else: - return render_user_not_found(request, handle) + raise Http404(_("User not found")) def fetch_refresh(request): @@ -153,10 +118,10 @@ def unblock(request: AuthedHttpRequest, user_name): try: target = APIdentity.get_by_handle(user_name) except APIdentity.DoesNotExist: - return render_user_not_found(request) + raise Http404(_("User not found")) target_user = target.user if target_user and not target_user.is_active: - return render_user_not_found(request) + raise Http404(_("User no longer exists")) request.user.identity.unblock(target) return render( request, @@ -201,7 +166,7 @@ def set_layout(request: AuthedHttpRequest): request.user.preference.discover_layout = layout request.user.preference.save(update_fields=["discover_layout"]) return redirect(reverse("catalog:discover")) - raise BadRequest() + raise BadRequest(_("Invalid parameter")) @login_required