i18n for 4xx 5xx pages
This commit is contained in:
parent
c74e3c5332
commit
a776dcfbb5
19 changed files with 390 additions and 374 deletions
|
@ -1,4 +1,3 @@
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import django_rq
|
import django_rq
|
||||||
|
@ -8,11 +7,11 @@ from django.core.cache import cache
|
||||||
from django.core.exceptions import BadRequest
|
from django.core.exceptions import BadRequest
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
from rq.job import Job
|
from rq.job import Job
|
||||||
|
|
||||||
from catalog.common.models import ItemCategory, SiteName
|
from catalog.common.models import ItemCategory, SiteName
|
||||||
from catalog.common.sites import AbstractSite, SiteManager
|
from catalog.common.sites import AbstractSite, SiteManager
|
||||||
from common.config import PAGE_LINK_NUMBER
|
|
||||||
from common.utils import (
|
from common.utils import (
|
||||||
HTTPResponseHXRedirect,
|
HTTPResponseHXRedirect,
|
||||||
PageLinksGenerator,
|
PageLinksGenerator,
|
||||||
|
@ -52,7 +51,7 @@ def fetch(request, url, is_refetch: bool = False, site: AbstractSite | None = No
|
||||||
if not site:
|
if not site:
|
||||||
site = SiteManager.get_site_by_url(url)
|
site = SiteManager.get_site_by_url(url)
|
||||||
if not site:
|
if not site:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid URL"))
|
||||||
item = site.get_item()
|
item = site.get_item()
|
||||||
if item and not is_refetch:
|
if item and not is_refetch:
|
||||||
return redirect(item.url)
|
return redirect(item.url)
|
||||||
|
@ -172,10 +171,9 @@ def external_search(request):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def refetch(request):
|
def refetch(request):
|
||||||
if request.method != "POST":
|
|
||||||
raise BadRequest()
|
|
||||||
url = request.POST.get("url")
|
url = request.POST.get("url")
|
||||||
if not url:
|
if not url:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid URL"))
|
||||||
return fetch(request, url, True)
|
return fetch(request, url, True)
|
||||||
|
|
|
@ -50,11 +50,11 @@ def retrieve_redirect(request, item_path, item_uuid):
|
||||||
def embed(request, item_path, item_uuid):
|
def embed(request, item_path, item_uuid):
|
||||||
item = Item.get_by_url(item_uuid)
|
item = Item.get_by_url(item_uuid)
|
||||||
if item is None:
|
if item is None:
|
||||||
raise Http404()
|
raise Http404(_("Item not found"))
|
||||||
if item.merged_to_item:
|
if item.merged_to_item:
|
||||||
return redirect(item.merged_to_item.url)
|
return redirect(item.merged_to_item.url)
|
||||||
if item.is_deleted:
|
if item.is_deleted:
|
||||||
raise Http404()
|
raise Http404(_("Item no longer exists"))
|
||||||
focus_item = None
|
focus_item = None
|
||||||
if request.GET.get("focus"):
|
if request.GET.get("focus"):
|
||||||
focus_item = get_object_or_404(
|
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 = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
item = Item.get_by_url(item_uuid)
|
item = Item.get_by_url(item_uuid)
|
||||||
if item is None:
|
if item is None:
|
||||||
raise Http404()
|
raise Http404(_("Item not found"))
|
||||||
item_url = f"/{item_path}/{item_uuid}"
|
item_url = f"/{item_path}/{item_uuid}"
|
||||||
if item.url != item_url:
|
if item.url != item_url:
|
||||||
return redirect(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:
|
if not skipcheck and item.merged_to_item:
|
||||||
return redirect(item.merged_to_item.url)
|
return redirect(item.merged_to_item.url)
|
||||||
if not skipcheck and item.is_deleted:
|
if not skipcheck and item.is_deleted:
|
||||||
raise Http404()
|
raise Http404(_("Item no longer exists"))
|
||||||
if request.headers.get("Accept", "").endswith("json"):
|
if request.headers.get("Accept", "").endswith("json"):
|
||||||
return redirect(item.api_url)
|
return redirect(item.api_url)
|
||||||
focus_item = None
|
focus_item = None
|
||||||
|
@ -146,8 +146,6 @@ def episode_data(request, item_uuid):
|
||||||
@login_required
|
@login_required
|
||||||
def mark_list(request, item_path, item_uuid, following_only=False):
|
def mark_list(request, item_path, item_uuid, following_only=False):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
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")
|
queryset = ShelfMember.objects.filter(item=item).order_by("-created_time")
|
||||||
if following_only:
|
if following_only:
|
||||||
queryset = queryset.filter(q_piece_in_home_feed_of_user(request.user))
|
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):
|
def review_list(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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 = Review.objects.filter(item=item).order_by("-created_time")
|
||||||
queryset = queryset.filter(q_piece_visible_to_user(request.user))
|
queryset = queryset.filter(q_piece_visible_to_user(request.user))
|
||||||
paginator = Paginator(queryset, NUM_REVIEWS_ON_LIST_PAGE)
|
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):
|
def comments(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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
|
ids = item.child_item_ids + [item.id] + item.sibling_item_ids
|
||||||
queryset = Comment.objects.filter(item_id__in=ids).order_by("-created_time")
|
queryset = Comment.objects.filter(item_id__in=ids).order_by("-created_time")
|
||||||
queryset = queryset.filter(q_piece_visible_to_user(request.user))
|
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):
|
def comments_by_episode(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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")
|
episode_uuid = request.GET.get("episode_uuid")
|
||||||
if episode_uuid:
|
if episode_uuid:
|
||||||
episode = TVEpisode.get_by_url(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):
|
def reviews(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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
|
ids = item.child_item_ids + [item.id] + item.sibling_item_ids
|
||||||
queryset = Review.objects.filter(item_id__in=ids).order_by("-created_time")
|
queryset = Review.objects.filter(item_id__in=ids).order_by("-created_time")
|
||||||
queryset = queryset.filter(q_piece_visible_to_user(request.user))
|
queryset = queryset.filter(q_piece_visible_to_user(request.user))
|
||||||
|
|
|
@ -97,7 +97,7 @@ def edit(request, item_path, item_uuid):
|
||||||
form.fields["primary_lookup_id_type"].disabled = True
|
form.fields["primary_lookup_id_type"].disabled = True
|
||||||
form.fields["primary_lookup_id_value"].disabled = True
|
form.fields["primary_lookup_id_value"].disabled = True
|
||||||
return render(request, "catalog_edit.html", {"form": form, "item": item})
|
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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
form_cls = CatalogForms[item.__class__.__name__]
|
form_cls = CatalogForms[item.__class__.__name__]
|
||||||
form = form_cls(request.POST, request.FILES, instance=item)
|
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)
|
return redirect(form.instance.url)
|
||||||
else:
|
else:
|
||||||
raise BadRequest(_add_error_map_detail(form.errors))
|
raise BadRequest(_add_error_map_detail(form.errors))
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
|
@ -124,7 +122,7 @@ def edit(request, item_path, item_uuid):
|
||||||
def delete(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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not request.user.is_staff and item.journal_exists():
|
if not request.user.is_staff and item.journal_exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.POST.get("sure", 0) != "1":
|
if request.POST.get("sure", 0) != "1":
|
||||||
return render(request, "catalog_delete.html", {"item": item})
|
return render(request, "catalog_delete.html", {"item": item})
|
||||||
else:
|
else:
|
||||||
|
@ -147,7 +145,7 @@ def delete(request, item_path, item_uuid):
|
||||||
def undelete(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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not request.user.is_staff:
|
if not request.user.is_staff:
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
item.is_deleted = False
|
item.is_deleted = False
|
||||||
item.save()
|
item.save()
|
||||||
return redirect(item.url)
|
return redirect(item.url)
|
||||||
|
@ -199,10 +197,10 @@ def recast(request, item_path, item_uuid):
|
||||||
@login_required
|
@login_required
|
||||||
def unlink(request):
|
def unlink(request):
|
||||||
if not request.user.is_staff:
|
if not request.user.is_staff:
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
res_id = request.POST.get("id")
|
res_id = request.POST.get("id")
|
||||||
if not res_id:
|
if not res_id:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
resource = get_object_or_404(ExternalResource, id=res_id)
|
resource = get_object_or_404(ExternalResource, id=res_id)
|
||||||
resource.unlink_from_item()
|
resource.unlink_from_item()
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
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):
|
def fetch_tvepisodes(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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:
|
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": ["", ""]})
|
item.log_action({"!fetch_tvepisodes": ["", ""]})
|
||||||
django_rq.get_queue("crawl").enqueue(
|
django_rq.get_queue("crawl").enqueue(
|
||||||
fetch_episodes_for_season_task, item.uuid, request.user
|
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):
|
def merge(request, item_path, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(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():
|
if not request.user.is_staff and item.journal_exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.POST.get("sure", 0) != "1":
|
if request.POST.get("sure", 0) != "1":
|
||||||
new_item = Item.get_by_url(request.POST.get("target_item_url"))
|
new_item = Item.get_by_url(request.POST.get("target_item_url"))
|
||||||
return render(
|
return render(
|
||||||
|
@ -352,7 +350,7 @@ def link_edition(request, item_path, item_uuid):
|
||||||
def unlink_works(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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not request.user.is_staff and item.journal_exists():
|
if not request.user.is_staff and item.journal_exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
item.unlink_from_all_works()
|
item.unlink_from_all_works()
|
||||||
discord_send(
|
discord_send(
|
||||||
"audit",
|
"audit",
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ site_name }} - {% trans '无效请求' %}</title>
|
<title>{{ site_name }} - 400</title>
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
{% include "common_libs.html" %}
|
{% include "common_libs.html" %}
|
||||||
</head>
|
</head>
|
||||||
|
@ -17,11 +17,12 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<article class="error">
|
<article class="error">
|
||||||
<header>
|
<header>
|
||||||
<h3>🤷🏻 无效的请求</h3>
|
<h3>🤷🏻 {{ exception }}</h3>
|
||||||
</header>
|
</header>
|
||||||
您可能提交了无效的数据,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
|
{% blocktrans %}You may have submitted invalid data, or the content may have been deleted by the author.{% endblocktrans %}
|
||||||
|
<br>
|
||||||
|
{% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
|
||||||
<section>
|
<section>
|
||||||
{{ exception }}
|
|
||||||
{% if exception.additonal_detail %}
|
{% if exception.additonal_detail %}
|
||||||
{% for e in exception.additonal_detail %}<p>{{ e }}</p>{% endfor %}
|
{% for e in exception.additonal_detail %}<p>{{ e }}</p>{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
{% load mastodon %}
|
{% load mastodon %}
|
||||||
{% load thumb %}
|
{% load thumb %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ site_name }} - {% trans '权限不符' %}</title>
|
<title>{{ site_name }} - 403</title>
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
{% include "common_libs.html" %}
|
{% include "common_libs.html" %}
|
||||||
</head>
|
</head>
|
||||||
|
@ -17,10 +17,11 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<article class="error">
|
<article class="error">
|
||||||
<header>
|
<header>
|
||||||
<h3>🙅🏻 权限不符</h3>
|
<h3>🙅🏻 {{ exception|default:"" }}</h3>
|
||||||
</header>
|
</header>
|
||||||
{{ exception }}
|
{% blocktrans %}Author may require you to log in before accessing this content, or you do not have permission to view it.{% endblocktrans %}
|
||||||
您可能访问了错误的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
|
<br>
|
||||||
|
{% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
{% include "_footer.html" %}
|
{% include "_footer.html" %}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
{% load mastodon %}
|
{% load mastodon %}
|
||||||
{% load thumb %}
|
{% load thumb %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ site_name }} - {% trans '未找到' %}</title>
|
<title>{{ site_name }} - 404</title>
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
{% include "common_libs.html" %}
|
{% include "common_libs.html" %}
|
||||||
</head>
|
</head>
|
||||||
|
@ -17,9 +17,11 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<article class="error">
|
<article class="error">
|
||||||
<header>
|
<header>
|
||||||
<h3>🤷🏻 条目未找到</h3>
|
<h3>🤷🏻 {{ exception|default:"" }}</h3>
|
||||||
</header>
|
</header>
|
||||||
您可能输入了一个无效的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
|
{% blocktrans %}You may have visited an incorrect URL, or the content you are looking for has been deleted by the author.{% endblocktrans %}
|
||||||
|
<br>
|
||||||
|
{% blocktrans %}If you believe this is our mistake, please contact us through the link at the bottom of the page.{% endblocktrans %}
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
{% include "_footer.html" %}
|
{% include "_footer.html" %}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
{% load mastodon %}
|
{% load mastodon %}
|
||||||
{% load thumb %}
|
{% load thumb %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ site_name }} - {% trans '系统错误' %}</title>
|
<title>{{ site_name }} - 500</title>
|
||||||
<meta name="robots" content="noindex">
|
<meta name="robots" content="noindex">
|
||||||
{% include "common_libs.html" %}
|
{% include "common_libs.html" %}
|
||||||
</head>
|
</head>
|
||||||
|
@ -17,9 +17,11 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<article class="error">
|
<article class="error">
|
||||||
<header>
|
<header>
|
||||||
<h3>🤦🏻 系统内部错误</h3>
|
<h3>🤦🏻 My Bad</h3>
|
||||||
</header>
|
</header>
|
||||||
发生了一个内部错误,如果这个错误多次出现,后台会记录并会由人工处理。如果您有紧急情况或任何疑问,请通过页面底部的链接联系我们。
|
{% blocktrans %}An internal error occurred. If this error occurs repeatedly, it will be recorded and handled by a human.{% endblocktrans %}
|
||||||
|
<br>
|
||||||
|
{% blocktrans %}If you have an urgent situation or any questions, please contact us through the link at the bottom of the page.{% endblocktrans %}
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
{% include "_footer.html" %}
|
{% include "_footer.html" %}
|
||||||
|
|
|
@ -4,9 +4,11 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from discord import SyncWebhook
|
from discord import SyncWebhook
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict
|
from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.baseconv import base62
|
from django.utils.baseconv import base62
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .config import PAGE_LINK_NUMBER
|
from .config import PAGE_LINK_NUMBER
|
||||||
|
|
||||||
|
@ -52,16 +54,15 @@ def target_identity_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(request, user_name, *args, **kwargs):
|
def wrapper(request, user_name, *args, **kwargs):
|
||||||
from users.models import APIdentity
|
from users.models import APIdentity
|
||||||
from users.views import render_user_blocked, render_user_not_found
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = APIdentity.get_by_handle(user_name)
|
target = APIdentity.get_by_handle(user_name)
|
||||||
except APIdentity.DoesNotExist:
|
except APIdentity.DoesNotExist:
|
||||||
return render_user_not_found(request)
|
raise Http404(_("User not found"))
|
||||||
target_user = target.user
|
target_user = target.user
|
||||||
viewer = None
|
viewer = None
|
||||||
if target_user and not target_user.is_active:
|
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:
|
if request.user.is_authenticated:
|
||||||
try:
|
try:
|
||||||
viewer = APIdentity.objects.get(user=request.user)
|
viewer = APIdentity.objects.get(user=request.user)
|
||||||
|
@ -69,7 +70,7 @@ def target_identity_required(func):
|
||||||
return HttpResponseRedirect("/account/register")
|
return HttpResponseRedirect("/account/register")
|
||||||
if request.user != target_user:
|
if request.user != target_user:
|
||||||
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
|
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
|
||||||
return render_user_blocked(request)
|
raise PermissionDenied(_("Access denied"))
|
||||||
else:
|
else:
|
||||||
viewer = None
|
viewer = None
|
||||||
request.target_identity = target
|
request.target_identity = target
|
||||||
|
@ -83,16 +84,15 @@ def profile_identity_required(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(request, user_name, *args, **kwargs):
|
def wrapper(request, user_name, *args, **kwargs):
|
||||||
from users.models import APIdentity
|
from users.models import APIdentity
|
||||||
from users.views import render_user_blocked, render_user_not_found
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target = APIdentity.get_by_handle(user_name, match_linked=True)
|
target = APIdentity.get_by_handle(user_name, match_linked=True)
|
||||||
except APIdentity.DoesNotExist:
|
except APIdentity.DoesNotExist:
|
||||||
return render_user_not_found(request)
|
raise Http404(_("User not found"))
|
||||||
target_user = target.user
|
target_user = target.user
|
||||||
viewer = None
|
viewer = None
|
||||||
if target_user and not target_user.is_active:
|
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:
|
if request.user.is_authenticated:
|
||||||
try:
|
try:
|
||||||
viewer = APIdentity.objects.get(user=request.user)
|
viewer = APIdentity.objects.get(user=request.user)
|
||||||
|
@ -100,7 +100,7 @@ def profile_identity_required(func):
|
||||||
return HttpResponseRedirect("/account/register")
|
return HttpResponseRedirect("/account/register")
|
||||||
if request.user != target_user:
|
if request.user != target_user:
|
||||||
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
|
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
|
||||||
return render_user_blocked(request)
|
raise PermissionDenied(_("Access denied"))
|
||||||
else:
|
else:
|
||||||
viewer = None
|
viewer = None
|
||||||
request.target_identity = target
|
request.target_identity = target
|
||||||
|
|
|
@ -69,21 +69,16 @@ def nodeinfo2(request):
|
||||||
|
|
||||||
|
|
||||||
def error_400(request, exception=None):
|
def error_400(request, exception=None):
|
||||||
return render(
|
return render(request, "400.html", status=400, context={"exception": exception})
|
||||||
request,
|
|
||||||
"400.html",
|
|
||||||
{"exception": exception},
|
|
||||||
status=400,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def error_403(request, exception=None):
|
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):
|
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):
|
def error_500(request, exception=None):
|
||||||
return render(request, "500.html", status=500)
|
return render(request, "500.html", status=500, context={"exception": exception})
|
||||||
|
|
|
@ -6,17 +6,11 @@ from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
|
|
||||||
from catalog.models import Item
|
from catalog.models import Item
|
||||||
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
||||||
from mastodon.api import boost_toot_later, share_collection
|
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 ..forms import *
|
||||||
from ..models import *
|
from ..models import *
|
||||||
|
@ -55,7 +49,7 @@ def collection_retrieve_redirect(request: AuthedHttpRequest, collection_uuid):
|
||||||
def collection_retrieve(request: AuthedHttpRequest, collection_uuid):
|
def collection_retrieve(request: AuthedHttpRequest, collection_uuid):
|
||||||
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_visible_to(request.user):
|
if not collection.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
follower_count = collection.likes.all().count()
|
follower_count = collection.likes.all().count()
|
||||||
following = (
|
following = (
|
||||||
Like.user_liked_piece(request.user.identity, collection)
|
Like.user_liked_piece(request.user.identity, collection)
|
||||||
|
@ -101,12 +95,11 @@ def collection_retrieve(request: AuthedHttpRequest, collection_uuid):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_add_featured(request: AuthedHttpRequest, collection_uuid):
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_visible_to(request.user):
|
if not collection.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
FeaturedCollection.objects.update_or_create(
|
FeaturedCollection.objects.update_or_create(
|
||||||
owner=request.user.identity, target=collection
|
owner=request.user.identity, target=collection
|
||||||
)
|
)
|
||||||
|
@ -114,12 +107,11 @@ def collection_add_featured(request: AuthedHttpRequest, collection_uuid):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_remove_featured(request: AuthedHttpRequest, collection_uuid):
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_visible_to(request.user):
|
if not collection.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
fc = FeaturedCollection.objects.filter(
|
fc = FeaturedCollection.objects.filter(
|
||||||
owner=request.user.identity, target=collection
|
owner=request.user.identity, target=collection
|
||||||
).first()
|
).first()
|
||||||
|
@ -129,15 +121,16 @@ def collection_remove_featured(request: AuthedHttpRequest, collection_uuid):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST", "GET"])
|
||||||
def collection_share(request: AuthedHttpRequest, collection_uuid):
|
def collection_share(request: AuthedHttpRequest, collection_uuid):
|
||||||
collection = get_object_or_404(
|
collection = get_object_or_404(
|
||||||
Collection, uid=get_uuid_or_404(collection_uuid) if collection_uuid else None
|
Collection, uid=get_uuid_or_404(collection_uuid) if collection_uuid else None
|
||||||
)
|
)
|
||||||
if collection and not collection.is_visible_to(request.user):
|
if collection and not collection.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render(request, "collection_share.html", {"collection": collection})
|
return render(request, "collection_share.html", {"collection": collection})
|
||||||
elif request.method == "POST":
|
else:
|
||||||
comment = request.POST.get("comment")
|
comment = request.POST.get("comment")
|
||||||
# boost if possible, otherwise quote
|
# boost if possible, otherwise quote
|
||||||
if (
|
if (
|
||||||
|
@ -158,8 +151,6 @@ def collection_share(request: AuthedHttpRequest, collection_uuid):
|
||||||
):
|
):
|
||||||
return render_relogin(request)
|
return render_relogin(request)
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
def collection_retrieve_items(
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_visible_to(request.user):
|
if not collection.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
form = CollectionForm(instance=collection)
|
form = CollectionForm(instance=collection)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -182,12 +173,11 @@ def collection_retrieve_items(
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_append_item(request: AuthedHttpRequest, collection_uuid):
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
if not collection.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
|
|
||||||
url = request.POST.get("url")
|
url = request.POST.get("url")
|
||||||
note = request.POST.get("note")
|
note = request.POST.get("note")
|
||||||
|
@ -202,26 +192,24 @@ def collection_append_item(request: AuthedHttpRequest, collection_uuid):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_remove_item(request: AuthedHttpRequest, collection_uuid, item_uuid):
|
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))
|
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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
if not collection.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
collection.remove_item(item)
|
collection.remove_item(item)
|
||||||
return collection_retrieve_items(request, collection_uuid, True)
|
return collection_retrieve_items(request, collection_uuid, True)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_move_item(
|
def collection_move_item(
|
||||||
request: AuthedHttpRequest, direction, collection_uuid, item_uuid
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if direction == "up":
|
if direction == "up":
|
||||||
collection.move_up_item(item)
|
collection.move_up_item(item)
|
||||||
|
@ -231,45 +219,44 @@ def collection_move_item(
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def collection_update_member_order(request: AuthedHttpRequest, collection_uuid):
|
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))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
if not collection.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
ids = request.POST.get("member_ids", "").strip()
|
ids = request.POST.get("member_ids", "").strip()
|
||||||
if not ids:
|
if not ids:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
ordered_member_ids = [int(i) for i in ids.split(",")]
|
ordered_member_ids = [int(i) for i in ids.split(",")]
|
||||||
collection.update_member_order(ordered_member_ids)
|
collection.update_member_order(ordered_member_ids)
|
||||||
return collection_retrieve_items(request, collection_uuid, True)
|
return collection_retrieve_items(request, collection_uuid, True)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def collection_update_item_note(request: AuthedHttpRequest, collection_uuid, item_uuid):
|
def collection_update_item_note(request: AuthedHttpRequest, collection_uuid, item_uuid):
|
||||||
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
collection = get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not collection.is_editable_by(request.user):
|
if not collection.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
collection.update_item_metadata(
|
collection.update_item_metadata(
|
||||||
item, {"note": request.POST.get("note", default="")}
|
item, {"note": request.POST.get("note", default="")}
|
||||||
)
|
)
|
||||||
return collection_retrieve_items(request, collection_uuid, True)
|
return collection_retrieve_items(request, collection_uuid, True)
|
||||||
elif request.method == "GET":
|
else:
|
||||||
member = collection.get_member_for_item(item)
|
member = collection.get_member_for_item(item)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"collection_update_item_note.html",
|
"collection_update_item_note.html",
|
||||||
{"collection": collection, "item": item, "note": member.note},
|
{"collection": collection, "item": item, "note": member.note},
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def collection_edit(request: AuthedHttpRequest, collection_uuid=None):
|
def collection_edit(request: AuthedHttpRequest, collection_uuid=None):
|
||||||
collection = (
|
collection = (
|
||||||
get_object_or_404(Collection, uid=get_uuid_or_404(collection_uuid))
|
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
|
else None
|
||||||
)
|
)
|
||||||
if collection and not collection.is_editable_by(request.user):
|
if collection and not collection.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
form = CollectionForm(instance=collection) if collection else CollectionForm()
|
form = CollectionForm(instance=collection) if collection else CollectionForm()
|
||||||
if request.GET.get("title"):
|
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,
|
"identity": collection.owner if collection else request.user.identity,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
elif request.method == "POST":
|
else:
|
||||||
form = (
|
form = (
|
||||||
CollectionForm(request.POST, request.FILES, instance=collection)
|
CollectionForm(request.POST, request.FILES, instance=collection)
|
||||||
if collection
|
if collection
|
||||||
|
@ -306,16 +293,14 @@ def collection_edit(request: AuthedHttpRequest, collection_uuid=None):
|
||||||
reverse("journal:collection_retrieve", args=[form.instance.uuid])
|
reverse("journal:collection_retrieve", args=[form.instance.uuid])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
@target_identity_required
|
@target_identity_required
|
||||||
def user_collection_list(request: AuthedHttpRequest, user_name):
|
def user_collection_list(request: AuthedHttpRequest, user_name):
|
||||||
target = request.target_identity
|
target = request.target_identity
|
||||||
if not request.user.is_authenticated and not target.anonymous_viewable:
|
if not request.user.is_authenticated and not target.anonymous_viewable:
|
||||||
return render_user_noanonymous(request)
|
raise PermissionDenied(_("Login required"))
|
||||||
collections = (
|
collections = (
|
||||||
Collection.objects.filter(owner=target)
|
Collection.objects.filter(owner=target)
|
||||||
.filter(q_owned_piece_visible_to_user(request.user, 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):
|
def user_liked_collection_list(request: AuthedHttpRequest, user_name):
|
||||||
target = request.target_identity
|
target = request.target_identity
|
||||||
if not request.user.is_authenticated and not target.anonymous_viewable:
|
if not request.user.is_authenticated and not target.anonymous_viewable:
|
||||||
return render_user_noanonymous(request)
|
raise PermissionDenied(_("Login required"))
|
||||||
collections = Collection.objects.filter(
|
collections = Collection.objects.filter(
|
||||||
interactions__identity=target,
|
interactions__identity=target,
|
||||||
interactions__interaction_type="like",
|
interactions__interaction_type="like",
|
||||||
|
|
|
@ -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.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
|
|
||||||
from catalog.models import *
|
from catalog.models import *
|
||||||
from common.utils import (
|
from common.utils import (
|
||||||
|
@ -77,7 +78,7 @@ def render_list(
|
||||||
elif type == "review" and item_category:
|
elif type == "review" and item_category:
|
||||||
queryset = Review.objects.filter(q_item_in_category(item_category))
|
queryset = Review.objects.filter(q_item_in_category(item_category))
|
||||||
else:
|
else:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
if sort == "rating":
|
if sort == "rating":
|
||||||
rating = Rating.objects.filter(
|
rating = Rating.objects.filter(
|
||||||
owner_id=OuterRef("owner_id"), item_id=OuterRef("item_id")
|
owner_id=OuterRef("owner_id"), item_id=OuterRef("item_id")
|
||||||
|
@ -125,17 +126,16 @@ def render_list(
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def piece_delete(request, piece_uuid):
|
def piece_delete(request, piece_uuid):
|
||||||
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
|
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
|
||||||
return_url = request.GET.get("return_url", None) or "/"
|
return_url = request.GET.get("return_url", None) or "/"
|
||||||
if not piece.is_editable_by(request.user):
|
if not piece.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render(
|
return render(
|
||||||
request, "piece_delete.html", {"piece": piece, "return_url": return_url}
|
request, "piece_delete.html", {"piece": piece, "return_url": return_url}
|
||||||
)
|
)
|
||||||
elif request.method == "POST":
|
else:
|
||||||
piece.delete()
|
piece.delete()
|
||||||
return redirect(return_url)
|
return redirect(return_url)
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
|
|
||||||
from catalog.models import *
|
from catalog.models import *
|
||||||
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
||||||
|
@ -25,12 +26,9 @@ _checkmark = "✔️".encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def wish(request: AuthedHttpRequest, item_uuid):
|
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))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if not item:
|
|
||||||
raise Http404()
|
|
||||||
mark = Mark(request.user.identity, item)
|
mark = Mark(request.user.identity, item)
|
||||||
if not mark.shelf_type:
|
if not mark.shelf_type:
|
||||||
mark.update(ShelfType.WISHLIST)
|
mark.update(ShelfType.WISHLIST)
|
||||||
|
@ -40,6 +38,7 @@ def wish(request: AuthedHttpRequest, item_uuid):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def mark(request: AuthedHttpRequest, item_uuid):
|
def mark(request: AuthedHttpRequest, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
mark = Mark(request.user.identity, item)
|
mark = Mark(request.user.identity, item)
|
||||||
|
@ -59,7 +58,7 @@ def mark(request: AuthedHttpRequest, item_uuid):
|
||||||
"date_today": timezone.localdate().isoformat(),
|
"date_today": timezone.localdate().isoformat(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
elif request.method == "POST":
|
else:
|
||||||
if request.POST.get("delete", default=False):
|
if request.POST.get("delete", default=False):
|
||||||
mark.delete()
|
mark.delete()
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||||
|
@ -120,27 +119,28 @@ def mark(request: AuthedHttpRequest, item_uuid):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def mark_log(request: AuthedHttpRequest, item_uuid, log_id):
|
def mark_log(request: AuthedHttpRequest, item_uuid, log_id):
|
||||||
"""
|
"""
|
||||||
Delete log of one item by log id.
|
Delete log of one item by log id.
|
||||||
"""
|
"""
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
mark = Mark(request.user.identity, item)
|
mark = Mark(request.user.identity, item)
|
||||||
if request.method == "POST":
|
if request.GET.get("delete", default=False):
|
||||||
if request.GET.get("delete", default=False):
|
if log_id:
|
||||||
if log_id:
|
mark.delete_log(log_id)
|
||||||
mark.delete_log(log_id)
|
else:
|
||||||
else:
|
mark.delete_all_logs()
|
||||||
mark.delete_all_logs()
|
return render(request, "_item_user_mark_history.html", {"mark": mark})
|
||||||
return render(request, "_item_user_mark_history.html", {"mark": mark})
|
else:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def comment(request: AuthedHttpRequest, item_uuid):
|
def comment(request: AuthedHttpRequest, item_uuid):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
if item.class_name not in ["podcastepisode", "tvepisode"]:
|
if item.class_name not in ["podcastepisode", "tvepisode"]:
|
||||||
|
@ -155,10 +155,10 @@ def comment(request: AuthedHttpRequest, item_uuid):
|
||||||
"comment": comment,
|
"comment": comment,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
elif request.method == "POST":
|
else:
|
||||||
if request.POST.get("delete", default=False):
|
if request.POST.get("delete", default=False):
|
||||||
if not comment:
|
if not comment:
|
||||||
raise Http404()
|
raise Http404(_("Content not found"))
|
||||||
comment.delete()
|
comment.delete()
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||||
visibility = int(request.POST.get("visibility", default=0))
|
visibility = int(request.POST.get("visibility", default=0))
|
||||||
|
@ -176,9 +176,9 @@ def comment(request: AuthedHttpRequest, item_uuid):
|
||||||
d = {"text": text, "visibility": visibility}
|
d = {"text": text, "visibility": visibility}
|
||||||
if position:
|
if position:
|
||||||
d["metadata"] = {"position": 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
|
owner=request.user.identity, item=item, defaults=d
|
||||||
)
|
)[0]
|
||||||
post = Takahe.post_comment(comment, False)
|
post = Takahe.post_comment(comment, False)
|
||||||
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
|
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
|
||||||
if post and share_to_mastodon:
|
if post and share_to_mastodon:
|
||||||
|
@ -187,7 +187,6 @@ def comment(request: AuthedHttpRequest, item_uuid):
|
||||||
else:
|
else:
|
||||||
boost_toot_later(request.user, post.url)
|
boost_toot_later(request.user, post.url)
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category):
|
def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category):
|
||||||
|
|
|
@ -17,7 +17,7 @@ from ..models import *
|
||||||
def piece_replies(request: AuthedHttpRequest, piece_uuid: str):
|
def piece_replies(request: AuthedHttpRequest, piece_uuid: str):
|
||||||
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
|
piece = get_object_or_404(Piece, uid=get_uuid_or_404(piece_uuid))
|
||||||
if not piece.is_visible_to(request.user):
|
if not piece.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
replies = piece.get_replies(request.user.identity)
|
replies = piece.get_replies(request.user.identity)
|
||||||
return render(
|
return render(
|
||||||
request, "replies.html", {"post": piece.latest_post, "replies": replies}
|
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()
|
content = request.POST.get("content", "").strip()
|
||||||
visibility = Takahe.Visibilities(int(request.POST.get("visibility", -1)))
|
visibility = Takahe.Visibilities(int(request.POST.get("visibility", -1)))
|
||||||
if not content:
|
if not content:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
Takahe.reply_post(post_id, request.user.identity.pk, content, visibility)
|
Takahe.reply_post(post_id, request.user.identity.pk, content, visibility)
|
||||||
replies = Takahe.get_replies_for_posts([post_id], request.user.identity.pk)
|
replies = Takahe.get_replies_for_posts([post_id], request.user.identity.pk)
|
||||||
return render(
|
return render(
|
||||||
|
@ -52,7 +52,7 @@ def post_boost(request: AuthedHttpRequest, post_id: int):
|
||||||
# classic_repost = request.user.preference.mastodon_repost_mode == 1
|
# classic_repost = request.user.preference.mastodon_repost_mode == 1
|
||||||
post = Takahe.get_post(post_id)
|
post = Takahe.get_post(post_id)
|
||||||
if not post:
|
if not post:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
if request.user.mastodon_site:
|
if request.user.mastodon_site:
|
||||||
boost_toot_later(request.user, post.object_uri)
|
boost_toot_later(request.user, post.object_uri)
|
||||||
else:
|
else:
|
||||||
|
@ -70,7 +70,5 @@ def post_like(request: AuthedHttpRequest, post_id: int):
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def post_unlike(request: AuthedHttpRequest, post_id: int):
|
def post_unlike(request: AuthedHttpRequest, post_id: int):
|
||||||
if request.method != "POST":
|
|
||||||
raise BadRequest()
|
|
||||||
Takahe.unlike_post(post_id, request.user.identity.pk)
|
Takahe.unlike_post(post_id, request.user.identity.pk)
|
||||||
return render(request, "action_like_post.html", {"post": Takahe.get_post(post_id)})
|
return render(request, "action_like_post.html", {"post": Takahe.get_post(post_id)})
|
||||||
|
|
|
@ -27,13 +27,14 @@ def review_retrieve(request, review_uuid):
|
||||||
# piece = get_object_or_404(Review, uid=get_uuid_or_404(review_uuid))
|
# piece = get_object_or_404(Review, uid=get_uuid_or_404(review_uuid))
|
||||||
piece = Review.get_by_url(review_uuid)
|
piece = Review.get_by_url(review_uuid)
|
||||||
if piece is None:
|
if piece is None:
|
||||||
raise Http404()
|
raise Http404(_("Content not found"))
|
||||||
if not piece.is_visible_to(request.user):
|
if not piece.is_visible_to(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
return render(request, "review.html", {"review": piece})
|
return render(request, "review.html", {"review": piece})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
||||||
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
|
||||||
review = (
|
review = (
|
||||||
|
@ -42,7 +43,7 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
if review and not review.is_editable_by(request.user):
|
if review and not review.is_editable_by(request.user):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied(_("Insufficient permission"))
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
form = (
|
form = (
|
||||||
ReviewForm(instance=review)
|
ReviewForm(instance=review)
|
||||||
|
@ -63,7 +64,7 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
||||||
"date_today": timezone.localdate().isoformat(),
|
"date_today": timezone.localdate().isoformat(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
elif request.method == "POST":
|
else:
|
||||||
form = (
|
form = (
|
||||||
ReviewForm(request.POST, instance=review)
|
ReviewForm(request.POST, instance=review)
|
||||||
if review
|
if review
|
||||||
|
@ -89,12 +90,10 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
||||||
form.cleaned_data["share_to_mastodon"],
|
form.cleaned_data["share_to_mastodon"],
|
||||||
)
|
)
|
||||||
if not review:
|
if not review:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
return redirect(reverse("journal:review_retrieve", args=[review.uuid]))
|
return redirect(reverse("journal:review_retrieve", args=[review.uuid]))
|
||||||
else:
|
else:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
def user_review_list(request, user_name, item_category):
|
def user_review_list(request, user_name, item_category):
|
||||||
|
@ -112,7 +111,7 @@ class ReviewFeed(Feed):
|
||||||
return (
|
return (
|
||||||
_("Reviews by {0}").format(owner.display_name)
|
_("Reviews by {0}").format(owner.display_name)
|
||||||
if owner
|
if owner
|
||||||
else _("link unavailable")
|
else _("Link invalid")
|
||||||
)
|
)
|
||||||
|
|
||||||
def link(self, owner):
|
def link(self, owner):
|
||||||
|
@ -120,9 +119,9 @@ class ReviewFeed(Feed):
|
||||||
|
|
||||||
def description(self, owner):
|
def description(self, owner):
|
||||||
if not owner:
|
if not owner:
|
||||||
return _("link unavailable")
|
return _("Link invalid")
|
||||||
elif not owner.anonymous_viewable:
|
elif not owner.anonymous_viewable:
|
||||||
return _("anonymous access disabled by owner")
|
return _("Login required")
|
||||||
else:
|
else:
|
||||||
return _("Reviews by {0}").format(owner.display_name)
|
return _("Reviews by {0}").format(owner.display_name)
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied
|
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
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 user_messages import api as msg
|
||||||
|
|
||||||
from catalog.models import *
|
from catalog.models import *
|
||||||
from users.models import User
|
|
||||||
from users.views import render_user_blocked, render_user_not_found
|
|
||||||
|
|
||||||
from ..forms import *
|
from ..forms import *
|
||||||
from ..models import *
|
from ..models import *
|
||||||
|
@ -38,16 +36,17 @@ def user_tag_list(request, user_name):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["GET", "POST"])
|
||||||
def user_tag_edit(request):
|
def user_tag_edit(request):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
tag_title = Tag.cleanup_title(request.GET.get("tag", ""), replace=False)
|
tag_title = Tag.cleanup_title(request.GET.get("tag", ""), replace=False)
|
||||||
if not tag_title:
|
if not tag_title:
|
||||||
raise Http404()
|
raise Http404(_("Invalid tag"))
|
||||||
tag = Tag.objects.filter(owner=request.user.identity, title=tag_title).first()
|
tag = Tag.objects.filter(owner=request.user.identity, title=tag_title).first()
|
||||||
if not tag:
|
if not tag:
|
||||||
raise Http404()
|
raise Http404(_("Tag not found"))
|
||||||
return render(request, "tag_edit.html", {"tag": tag})
|
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_title = Tag.cleanup_title(request.POST.get("title", ""), replace=False)
|
||||||
tag_id = request.POST.get("id")
|
tag_id = request.POST.get("id")
|
||||||
tag = (
|
tag = (
|
||||||
|
@ -56,7 +55,7 @@ def user_tag_edit(request):
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
if not tag or not tag_title:
|
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"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||||
if request.POST.get("delete"):
|
if request.POST.get("delete"):
|
||||||
tag.delete()
|
tag.delete()
|
||||||
|
@ -83,7 +82,6 @@ def user_tag_edit(request):
|
||||||
args=[request.user.username, tag.title],
|
args=[request.user.username, tag.title],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
def user_tag_member_list(request, user_name, tag_title):
|
def user_tag_member_list(request, user_name, tag_title):
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -578,6 +578,10 @@ msgstr "结束日期"
|
||||||
msgid "host"
|
msgid "host"
|
||||||
msgstr "主播"
|
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/_item_card_metadata_edition.html:17
|
||||||
#: catalog/templates/edition.html:54
|
#: catalog/templates/edition.html:54
|
||||||
msgid "年"
|
msgid "年"
|
||||||
|
@ -1129,59 +1133,113 @@ msgstr ""
|
||||||
msgid "season number"
|
msgid "season number"
|
||||||
msgstr ""
|
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"
|
msgid "Updating episodes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: catalog/views_edit.py:289
|
#: catalog/views_edit.py:287
|
||||||
msgid "Cannot be merged to an item already deleted or merged"
|
msgid "Cannot be merged to an item already deleted or merged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: catalog/views_edit.py:292
|
#: catalog/views_edit.py:290
|
||||||
msgid "Cannot merge items in different categories"
|
msgid "Cannot merge items in different categories"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: catalog/views_edit.py:329
|
#: catalog/views_edit.py:327
|
||||||
msgid "Cannot be linked to an item already deleted or merged"
|
msgid "Cannot be linked to an item already deleted or merged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: catalog/views_edit.py:331
|
#: catalog/views_edit.py:329
|
||||||
msgid "Cannot link items other than editions"
|
msgid "Cannot link items other than editions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: common/templates/400.html:11
|
#: common/templates/400.html:22
|
||||||
msgid "无效请求"
|
msgid ""
|
||||||
msgstr ""
|
"You may have submitted invalid data, or the content may have been deleted by "
|
||||||
|
"the author."
|
||||||
|
msgstr "您可能提交了无效的数据,或者相关内容已被作者删除。"
|
||||||
|
|
||||||
#: common/templates/403.html:11
|
#: common/templates/400.html:24 common/templates/403.html:24
|
||||||
msgid "权限不符"
|
#: common/templates/404.html:24
|
||||||
msgstr ""
|
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
|
#: common/templates/403.html:22
|
||||||
msgid "未找到"
|
msgid ""
|
||||||
msgstr ""
|
"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
|
#: common/templates/404.html:22
|
||||||
msgid "系统错误"
|
msgid ""
|
||||||
msgstr ""
|
"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"
|
msgid "Rules"
|
||||||
msgstr "站点规则"
|
msgstr "站点规则"
|
||||||
|
|
||||||
#: common/templates/_footer.html:12
|
#: common/templates/_footer.html:7
|
||||||
msgid "Terms"
|
msgid "Terms"
|
||||||
msgstr "服务协议"
|
msgstr "服务协议"
|
||||||
|
|
||||||
#: common/templates/_footer.html:13
|
#: common/templates/_footer.html:8
|
||||||
msgid "Announcements"
|
msgid "Announcements"
|
||||||
msgstr "公告栏"
|
msgstr "公告栏"
|
||||||
|
|
||||||
#: common/templates/_footer.html:14
|
#: common/templates/_footer.html:9
|
||||||
msgid "Developer"
|
msgid "Developer"
|
||||||
msgstr "开发者"
|
msgstr "开发者"
|
||||||
|
|
||||||
#: common/templates/_footer.html:19
|
#: common/templates/_footer.html:13
|
||||||
msgid "Source Code"
|
msgid "Source Code"
|
||||||
msgstr "源代码"
|
msgstr "源代码"
|
||||||
|
|
||||||
|
@ -1294,71 +1352,83 @@ msgstr "关注了你"
|
||||||
msgid "just now"
|
msgid "just now"
|
||||||
msgstr "刚刚"
|
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
|
#: developer/models.py:18
|
||||||
msgid "minimum two characters, words and -_. only, no special characters"
|
msgid "minimum two characters, words and -_. only, no special characters"
|
||||||
msgstr ""
|
msgstr "两字符以上,无特殊字符"
|
||||||
|
|
||||||
#: developer/models.py:29
|
#: developer/models.py:29
|
||||||
msgid "Allowed URIs list, space separated, at least one URI is required"
|
msgid "Allowed URIs list, space separated, at least one URI is required"
|
||||||
msgstr ""
|
msgstr "至少一个网址,空格分隔"
|
||||||
|
|
||||||
#: developer/templates/console.html:37
|
#: developer/templates/console.html:37
|
||||||
#: developer/templates/oauth2_provider/application_list.html:5
|
#: developer/templates/oauth2_provider/application_list.html:5
|
||||||
msgid "Your applications"
|
msgid "Your applications"
|
||||||
msgstr ""
|
msgstr "你的应用程序"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:9
|
#: developer/templates/oauth2_provider/application_detail.html:9
|
||||||
msgid "Client ID"
|
msgid "Client ID"
|
||||||
msgstr ""
|
msgstr "Client ID"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:18
|
#: developer/templates/oauth2_provider/application_detail.html:18
|
||||||
msgid "URL"
|
msgid "URL"
|
||||||
msgstr ""
|
msgstr "网址"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:24
|
#: developer/templates/oauth2_provider/application_detail.html:24
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
msgstr "描述"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:30
|
#: developer/templates/oauth2_provider/application_detail.html:30
|
||||||
msgid "Redirect Uris"
|
msgid "Redirect Uris"
|
||||||
msgstr ""
|
msgstr "重定向网址"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:38
|
#: developer/templates/oauth2_provider/application_detail.html:38
|
||||||
#: developer/templates/oauth2_provider/application_form.html:33
|
#: developer/templates/oauth2_provider/application_form.html:33
|
||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr ""
|
msgstr "返回"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:40
|
#: developer/templates/oauth2_provider/application_detail.html:40
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr "编辑"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_detail.html:42
|
#: developer/templates/oauth2_provider/application_detail.html:42
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "删除"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_form.html:8
|
#: developer/templates/oauth2_provider/application_form.html:8
|
||||||
msgid "Edit application"
|
msgid "Edit application"
|
||||||
msgstr ""
|
msgstr "编辑应用程序"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_form.html:35
|
#: developer/templates/oauth2_provider/application_form.html:35
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr "保存"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_list.html:20
|
#: developer/templates/oauth2_provider/application_list.html:20
|
||||||
msgid "New Application"
|
msgid "New Application"
|
||||||
msgstr ""
|
msgstr "新建应用程序"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_list.html:23
|
#: developer/templates/oauth2_provider/application_list.html:23
|
||||||
msgid "No applications defined"
|
msgid "No applications defined"
|
||||||
msgstr ""
|
msgstr "尚无应用程序"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_list.html:23
|
#: developer/templates/oauth2_provider/application_list.html:23
|
||||||
msgid "Click here"
|
msgid "Click here"
|
||||||
msgstr ""
|
msgstr "点此"
|
||||||
|
|
||||||
#: developer/templates/oauth2_provider/application_list.html:23
|
#: developer/templates/oauth2_provider/application_list.html:23
|
||||||
msgid "if you want to register a new one"
|
msgid "if you want to register a new one"
|
||||||
msgstr ""
|
msgstr "如果你希望新注册一个"
|
||||||
|
|
||||||
#: journal/forms.py:18 journal/forms.py:41
|
#: journal/forms.py:18 journal/forms.py:41
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
|
@ -1790,7 +1860,7 @@ msgstr ""
|
||||||
msgid "喜欢的收藏单"
|
msgid "喜欢的收藏单"
|
||||||
msgstr ""
|
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."
|
msgid "nothing so far."
|
||||||
msgstr "暂无内容。"
|
msgstr "暂无内容。"
|
||||||
|
|
||||||
|
@ -1824,83 +1894,92 @@ msgstr ""
|
||||||
msgid "分享"
|
msgid "分享"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: journal/views/collection.py:44
|
#: journal/views/collection.py:38
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Collection by {0}"
|
msgid "Collection by {0}"
|
||||||
msgstr "{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."
|
msgid "Unable to find the item, please use item url from this site."
|
||||||
msgstr ""
|
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."
|
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."
|
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."
|
msgid "List not found."
|
||||||
msgstr ""
|
msgstr "列表未找到"
|
||||||
|
|
||||||
#: journal/views/mark.py:110
|
#: journal/views/mark.py:109
|
||||||
msgid "Content too long for your Mastodon instance."
|
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
|
#, python-brace-format
|
||||||
msgid "Reviews by {0}"
|
msgid "Reviews by {0}"
|
||||||
msgstr ""
|
msgstr "{0} 的评论"
|
||||||
|
|
||||||
#: journal/views/review.py:115 journal/views/review.py:123
|
#: journal/views/review.py:114 journal/views/review.py:122
|
||||||
msgid "link unavailable"
|
msgid "Link invalid"
|
||||||
msgstr ""
|
msgstr "链接无效"
|
||||||
|
|
||||||
#: journal/views/review.py:125
|
#: journal/views/review.py:135
|
||||||
msgid "anonymous access disabled by owner"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: journal/views/review.py:136
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{review_title} - a review of {item_title}"
|
msgid "{review_title} - a review of {item_title}"
|
||||||
msgstr ""
|
msgstr "{review_title} - 关于 {item_title} 的评论"
|
||||||
|
|
||||||
#: journal/views/tag.py:59
|
#: journal/views/tag.py:43 journal/views/tag.py:57
|
||||||
msgid "Invalid tag."
|
msgid "Invalid tag"
|
||||||
msgstr ""
|
msgstr "无效标签"
|
||||||
|
|
||||||
#: journal/views/tag.py:63
|
#: journal/views/tag.py:46
|
||||||
|
msgid "Tag not found"
|
||||||
|
msgstr "标签不存在"
|
||||||
|
|
||||||
|
#: journal/views/tag.py:61
|
||||||
msgid "Tag deleted."
|
msgid "Tag deleted."
|
||||||
msgstr ""
|
msgstr "标签已删除"
|
||||||
|
|
||||||
#: journal/views/tag.py:73
|
#: journal/views/tag.py:71
|
||||||
msgid "Duplicated tag."
|
msgid "Duplicated tag."
|
||||||
msgstr ""
|
msgstr "重复标签"
|
||||||
|
|
||||||
#: journal/views/tag.py:79
|
#: journal/views/tag.py:77
|
||||||
msgid "Tag updated."
|
msgid "Tag updated."
|
||||||
msgstr ""
|
msgstr "标签已更新"
|
||||||
|
|
||||||
#: journal/views/wrapped.py:141
|
#: journal/views/wrapped.py:141
|
||||||
msgid "Summary posted to timeline."
|
msgid "Summary posted to timeline."
|
||||||
msgstr ""
|
msgstr "总结已发布到时间轴"
|
||||||
|
|
||||||
#: mastodon/api.py:489 takahe/utils.py:514
|
#: mastodon/api.py:489 takahe/utils.py:514
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "regarding {item_title}, may contain spoiler or triggering content"
|
msgid "regarding {item_title}, may contain spoiler or triggering content"
|
||||||
msgstr "关于 {item_title},可能包含剧透或敏感内容"
|
msgstr "关于 {item_title},可能包含剧透或敏感内容"
|
||||||
|
|
||||||
#: mastodon/api.py:639
|
#: mastodon/api.py:646
|
||||||
msgid "collection"
|
msgid "collection"
|
||||||
msgstr "收藏单"
|
msgstr "收藏单"
|
||||||
|
|
||||||
#: mastodon/api.py:644
|
#: mastodon/api.py:651
|
||||||
msgid "shared my collection"
|
msgid "shared my collection"
|
||||||
msgstr "分享我的收藏单"
|
msgstr "分享我的收藏单"
|
||||||
|
|
||||||
#: mastodon/api.py:647
|
#: mastodon/api.py:654
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "shared {username}'s collection"
|
msgid "shared {username}'s collection"
|
||||||
msgstr "分享 {username} 的收藏单"
|
msgstr "分享 {username} 的收藏单"
|
||||||
|
@ -2007,14 +2086,18 @@ msgstr "转播了你的帖文"
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"boosted your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"boosted your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
msgstr "\n转播了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"转播了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/boosted_comment.html:3
|
#: social/templates/event/boosted_comment.html:3
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"boosted your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"boosted your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
||||||
|
|
||||||
#: social/templates/event/boosted_review.html:2
|
#: social/templates/event/boosted_review.html:2
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2023,7 +2106,8 @@ msgid ""
|
||||||
"boosted your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
"boosted your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
||||||
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
"\n"
|
||||||
|
"转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
||||||
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/boosted_shelfmember.html:3
|
#: social/templates/event/boosted_shelfmember.html:3
|
||||||
|
@ -2031,7 +2115,9 @@ msgstr ""
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"boosted your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"boosted your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"转播了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
||||||
|
|
||||||
#: social/templates/event/follow_requested.html:3
|
#: social/templates/event/follow_requested.html:3
|
||||||
msgid "requested to follow you"
|
msgid "requested to follow you"
|
||||||
|
@ -2050,14 +2136,18 @@ msgstr "赞了你的帖文"
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"liked your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"liked your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
msgstr "\n赞了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"赞了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/liked_comment.html:3
|
#: social/templates/event/liked_comment.html:3
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"liked your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"liked your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
||||||
|
|
||||||
#: social/templates/event/liked_review.html:2
|
#: social/templates/event/liked_review.html:2
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2066,7 +2156,8 @@ msgid ""
|
||||||
"liked your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
"liked your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
||||||
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
"\n"
|
||||||
|
"赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
||||||
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/liked_shelfmember.html:3
|
#: social/templates/event/liked_shelfmember.html:3
|
||||||
|
@ -2074,7 +2165,9 @@ msgstr ""
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"liked your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"liked your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"赞了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
||||||
|
|
||||||
#: social/templates/event/mentioned.html:3
|
#: social/templates/event/mentioned.html:3
|
||||||
msgid "mentioned you"
|
msgid "mentioned you"
|
||||||
|
@ -2085,14 +2178,18 @@ msgstr "提到了你"
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"replied to your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"replied to your collection <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
msgstr "\n回应了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"回应了你的收藏单 <a href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/mentioned_comment.html:3
|
#: social/templates/event/mentioned_comment.html:3
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"replied to your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"replied to your comment on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的短评\n"
|
||||||
|
|
||||||
#: social/templates/event/mentioned_review.html:2
|
#: social/templates/event/mentioned_review.html:2
|
||||||
#, python-format
|
#, python-format
|
||||||
|
@ -2101,7 +2198,8 @@ msgid ""
|
||||||
"replied to your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
"replied to your review <a href=\"%(piece_url)s\">%(piece_title)s</a> on <a "
|
||||||
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
"\n"
|
||||||
|
"回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的评论 <a "
|
||||||
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
"href=\"%(piece_url)s\">%(piece_title)s</a>\n"
|
||||||
|
|
||||||
#: social/templates/event/mentioned_shelfmember.html:3
|
#: social/templates/event/mentioned_shelfmember.html:3
|
||||||
|
@ -2109,7 +2207,9 @@ msgstr ""
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"replied to your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
"replied to your mark on <a href=\"%(item_url)s\">%(item_title)s</a>\n"
|
||||||
msgstr "\n回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"回应了你对 <a href=\"%(item_url)s\">%(item_title)s</a> 的标记\n"
|
||||||
|
|
||||||
#: social/templates/events.html:44
|
#: social/templates/events.html:44
|
||||||
msgid "nothing more."
|
msgid "nothing more."
|
||||||
|
@ -2167,114 +2267,118 @@ msgstr ""
|
||||||
msgid "created collection"
|
msgid "created collection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:87
|
#: users/account.py:86
|
||||||
msgid "无效的电子邮件地址"
|
msgid "无效的电子邮件地址"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:105
|
#: users/account.py:104
|
||||||
msgid "验证邮件已发送"
|
msgid "验证邮件已发送"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:106
|
#: users/account.py:105
|
||||||
msgid "请查阅收件箱"
|
msgid "请查阅收件箱"
|
||||||
msgstr ""
|
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 "认证失败😫"
|
msgid "认证失败😫"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:156
|
#: users/account.py:155
|
||||||
msgid "Mastodon服务未能返回有效认证信息"
|
msgid "Mastodon服务未能返回有效认证信息"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:163
|
#: users/account.py:162
|
||||||
msgid "无效会话信息"
|
msgid "无效会话信息"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:173
|
#: users/account.py:167
|
||||||
|
msgid "Invalid instance domain"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: users/account.py:172
|
||||||
msgid "Mastodon服务未能返回有效认证令牌"
|
msgid "Mastodon服务未能返回有效认证令牌"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:190
|
#: users/account.py:189
|
||||||
msgid "联邦宇宙访问失败😫"
|
msgid "联邦宇宙访问失败😫"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:212
|
#: users/account.py:211
|
||||||
msgid "注册失败😫"
|
msgid "注册失败😫"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:213
|
#: users/account.py:212
|
||||||
msgid "本站仅限邀请注册"
|
msgid "本站仅限邀请注册"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:279
|
#: users/account.py:273
|
||||||
msgid "This username is already in use."
|
msgid "This username is already in use."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:290
|
#: users/account.py:284
|
||||||
msgid "This email address is already in use."
|
msgid "This email address is already in use."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:342 users/account.py:351
|
#: users/account.py:336 users/account.py:345
|
||||||
msgid "无效的验证码"
|
msgid "无效的验证码"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:370
|
#: users/account.py:364
|
||||||
msgid "链接无效或已过期"
|
msgid "链接无效或已过期"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:387 users/account.py:393
|
#: users/account.py:381 users/account.py:387
|
||||||
msgid "电子邮件地址不匹配"
|
msgid "电子邮件地址不匹配"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:397
|
#: users/account.py:391
|
||||||
msgid "此电子邮件地址已被注册"
|
msgid "此电子邮件地址已被注册"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:402
|
#: users/account.py:396
|
||||||
msgid "无法完成验证"
|
msgid "无法完成验证"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:434
|
#: users/account.py:428
|
||||||
msgid "用户名已被使用"
|
msgid "用户名已被使用"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:449
|
#: users/account.py:443
|
||||||
msgid "电子邮件地址已被使用"
|
msgid "电子邮件地址已被使用"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:467
|
#: users/account.py:461
|
||||||
msgid "已发送验证邮件,请查收。"
|
msgid "已发送验证邮件,请查收。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:471
|
#: users/account.py:465
|
||||||
msgid "用户名已设置。"
|
msgid "用户名已设置。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:473
|
#: users/account.py:467
|
||||||
msgid "电子邮件地址已取消关联。"
|
msgid "电子邮件地址已取消关联。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:491
|
#: users/account.py:485
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "该身份 {username}@{site} 与当前账号相同。"
|
msgid "该身份 {username}@{site} 与当前账号相同。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:499
|
#: users/account.py:493
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "该身份 {username}@{site} 已被用于其它账号。"
|
msgid "该身份 {username}@{site} 已被用于其它账号。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:523
|
#: users/account.py:517
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "账号身份已更新为 {username}@{site}。"
|
msgid "账号身份已更新为 {username}@{site}。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:526
|
#: users/account.py:520
|
||||||
msgid "连接联邦宇宙获取身份信息失败。"
|
msgid "连接联邦宇宙获取身份信息失败。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/account.py:575
|
#: users/account.py:569
|
||||||
msgid "验证信息不符。"
|
msgid "验证信息不符。"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2534,18 +2638,5 @@ msgstr ""
|
||||||
msgid "验证电子邮件"
|
msgid "验证电子邮件"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: users/views.py:25
|
#~ msgid "Invalid tag."
|
||||||
msgid "😖哎呀,这位用户好像还没有加入本站,快去联邦宇宙呼唤TA来注册吧!"
|
#~ msgstr "无效标签"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: users/views.py:26
|
|
||||||
msgid "未找到用户"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: users/views.py:38
|
|
||||||
msgid "没有访问该用户主页的权限"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: users/views.py:49
|
|
||||||
msgid "作者已设置仅限登录用户查看"
|
|
||||||
msgstr ""
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ reportIncompatibleVariableOverride = false
|
||||||
reportUnusedImport = false
|
reportUnusedImport = false
|
||||||
|
|
||||||
[tool.djlint]
|
[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
|
indent=2
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
|
|
|
@ -32,46 +32,45 @@ from takahe.utils import Takahe
|
||||||
from .models import Preference, User
|
from .models import Preference, User
|
||||||
from .tasks import *
|
from .tasks import *
|
||||||
|
|
||||||
|
|
||||||
# the 'login' page that user can see
|
# the 'login' page that user can see
|
||||||
def login(request):
|
require_http_methods(["GET"])
|
||||||
if request.method == "GET":
|
|
||||||
selected_site = request.GET.get("site", default="")
|
|
||||||
|
|
||||||
cache_key = "login_sites"
|
|
||||||
sites = cache.get(cache_key, [])
|
def login(request):
|
||||||
if not sites:
|
selected_site = request.GET.get("site", default="")
|
||||||
sites = list(
|
|
||||||
User.objects.filter(is_active=True)
|
cache_key = "login_sites"
|
||||||
.values("mastodon_site")
|
sites = cache.get(cache_key, [])
|
||||||
.annotate(total=Count("mastodon_site"))
|
if not sites:
|
||||||
.order_by("-total")
|
sites = list(
|
||||||
.values_list("mastodon_site", flat=True)
|
User.objects.filter(is_active=True)
|
||||||
)
|
.values("mastodon_site")
|
||||||
cache.set(cache_key, sites, timeout=3600 * 8)
|
.annotate(total=Count("mastodon_site"))
|
||||||
# store redirect url in the cookie
|
.order_by("-total")
|
||||||
if request.GET.get("next"):
|
.values_list("mastodon_site", flat=True)
|
||||||
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,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
else:
|
cache.set(cache_key, sites, timeout=3600 * 8)
|
||||||
raise BadRequest()
|
# 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
|
# connect will send verification email or redirect to mastodon server
|
||||||
|
@ -165,7 +164,7 @@ def connect_redirect_back(request):
|
||||||
try:
|
try:
|
||||||
token, refresh_token = obtain_token(site, request, code)
|
token, refresh_token = obtain_token(site, request, code)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid instance domain"))
|
||||||
if not token:
|
if not token:
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -239,24 +238,19 @@ def login_existing_user(request, existing_user):
|
||||||
@mastodon_request_included
|
@mastodon_request_included
|
||||||
@login_required
|
@login_required
|
||||||
def logout(request):
|
def logout(request):
|
||||||
if request.method == "GET":
|
# revoke_token(request.user.mastodon_site, request.user.mastodon_token)
|
||||||
# revoke_token(request.user.mastodon_site, request.user.mastodon_token)
|
return auth_logout(request)
|
||||||
return auth_logout(request)
|
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
@mastodon_request_included
|
@mastodon_request_included
|
||||||
@login_required
|
@login_required
|
||||||
|
@require_http_methods(["POST"])
|
||||||
def reconnect(request):
|
def reconnect(request):
|
||||||
if request.META.get("HTTP_AUTHORIZATION"):
|
if request.META.get("HTTP_AUTHORIZATION"):
|
||||||
raise BadRequest("Only for web login")
|
raise BadRequest("Only for web login")
|
||||||
if request.method == "POST":
|
request.session["swap_login"] = True
|
||||||
request.session["swap_login"] = True
|
request.session["swap_domain"] = request.POST["domain"]
|
||||||
request.session["swap_domain"] = request.POST["domain"]
|
return connect(request)
|
||||||
return connect(request)
|
|
||||||
else:
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationForm(forms.ModelForm):
|
class RegistrationForm(forms.ModelForm):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import json
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.exceptions import BadRequest
|
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.shortcuts import redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -21,41 +21,6 @@ from .data import *
|
||||||
from .models import APIdentity
|
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):
|
def query_identity(request, handle):
|
||||||
try:
|
try:
|
||||||
i = APIdentity.get_by_handle(handle)
|
i = APIdentity.get_by_handle(handle)
|
||||||
|
@ -67,7 +32,7 @@ def query_identity(request, handle):
|
||||||
request, "users/fetch_identity_pending.html", {"handle": handle}
|
request, "users/fetch_identity_pending.html", {"handle": handle}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return render_user_not_found(request, handle)
|
raise Http404(_("User not found"))
|
||||||
|
|
||||||
|
|
||||||
def fetch_refresh(request):
|
def fetch_refresh(request):
|
||||||
|
@ -153,10 +118,10 @@ def unblock(request: AuthedHttpRequest, user_name):
|
||||||
try:
|
try:
|
||||||
target = APIdentity.get_by_handle(user_name)
|
target = APIdentity.get_by_handle(user_name)
|
||||||
except APIdentity.DoesNotExist:
|
except APIdentity.DoesNotExist:
|
||||||
return render_user_not_found(request)
|
raise Http404(_("User not found"))
|
||||||
target_user = target.user
|
target_user = target.user
|
||||||
if target_user and not target_user.is_active:
|
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)
|
request.user.identity.unblock(target)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -201,7 +166,7 @@ def set_layout(request: AuthedHttpRequest):
|
||||||
request.user.preference.discover_layout = layout
|
request.user.preference.discover_layout = layout
|
||||||
request.user.preference.save(update_fields=["discover_layout"])
|
request.user.preference.save(update_fields=["discover_layout"])
|
||||||
return redirect(reverse("catalog:discover"))
|
return redirect(reverse("catalog:discover"))
|
||||||
raise BadRequest()
|
raise BadRequest(_("Invalid parameter"))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
Loading…
Add table
Reference in a new issue