set username and login via email
This commit is contained in:
parent
0f1ac51032
commit
fbb4bbc437
42 changed files with 786 additions and 287 deletions
|
@ -273,6 +273,9 @@ EXPORT_FILE_PATH_ROOT = "export/"
|
|||
# Allow user to login via any Mastodon/Pleroma sites
|
||||
MASTODON_ALLOW_ANY_SITE = False
|
||||
|
||||
# Allow user to create account with email (and link to Mastodon account later)
|
||||
ALLOW_EMAIL_ONLY_ACCOUNT = False
|
||||
|
||||
# Timeout of requests to Mastodon, in seconds
|
||||
MASTODON_TIMEOUT = 30
|
||||
|
||||
|
|
|
@ -18,12 +18,21 @@ from django.urls import path, include
|
|||
from django.conf import settings
|
||||
from users.views import login
|
||||
from common.api import api
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
urlpatterns = [
|
||||
path("api/", api.urls), # type: ignore
|
||||
path("login/", login),
|
||||
path("markdownx/", include("markdownx.urls")),
|
||||
path("users/", include("users.urls")),
|
||||
path("account/", include("users.urls")),
|
||||
path(
|
||||
"users/connect/",
|
||||
RedirectView.as_view(url="/account/connect", query_string=True),
|
||||
),
|
||||
path(
|
||||
"users/OAuth2_login/",
|
||||
RedirectView.as_view(url="/account/login/oauth", query_string=True),
|
||||
),
|
||||
path("", include("catalog.urls")),
|
||||
path("", include("journal.urls")),
|
||||
path("timeline/", include("social.urls")),
|
||||
|
|
|
@ -109,7 +109,7 @@ class Command(BaseCommand):
|
|||
else:
|
||||
self.stdout.write(f"! no season {i} : {i.absolute_url}?skipcheck=1")
|
||||
if self.fix:
|
||||
i.recast_to(TVShow)
|
||||
i.recast_to(i.merged_to_item.__class__)
|
||||
|
||||
self.stdout.write(f"Checking TVSeason is child of other class...")
|
||||
for i in TVSeason.objects.filter(show__isnull=False).exclude(
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if mark.comment.metadata.shared_link %} href="{{ mark.comment.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if mark.comment.metadata.shared_link %} href="{{ mark.comment.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
{% comment %} <span class="timestamp">{{ mark.comment.created_time|date }}</span> {% endcomment %}
|
||||
</span>
|
||||
|
@ -83,7 +83,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if comment.metadata.shared_link %} href="{{ comment.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if comment.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
{% comment %} <span class="timestamp">{{ comment.created_time|date }}</span> {% endcomment %}
|
||||
</span>
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if mark.metadata.shared_link %} href="{{ mark.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if mark.metadata.shared_link %} href="{{ mark.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
<span class="timestamp">{{ mark.created_time|date }}</span>
|
||||
</div>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
<span>
|
||||
{% liked_piece review as liked %}
|
||||
|
|
|
@ -65,10 +65,13 @@
|
|||
<ul role="listbox" style="min-width:-webkit-max-content;" dir="rtl">
|
||||
{% if request.user.is_authenticated %}
|
||||
<li>
|
||||
<a href="{% url 'users:data' %}">数据</a>
|
||||
<a href="{% url 'users:data' %}">数据管理</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'users:preferences' %}">设置</a>
|
||||
<a href="{% url 'users:preferences' %}">使用设置</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'users:info' %}">账号信息</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'users:logout' %}">登出</a>
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
<i class="fa-solid fa-puzzle-piece"></i> {{ site_name }}致力于提供一个涵盖书籍、影视、音乐、游戏、播客的自由开放互联的收藏评论空间。 你可以在这里记录你的收藏和想法,以及发现新的内容和朋友。
|
||||
</p>
|
||||
<p>
|
||||
<i class="fa-solid fa-globe"></i> 登录{{ site_name }}需要一个联邦网络(Fediverse,也被称为长毛象)实例账号,如果你还没有账号,可以<a href="https://joinmastodon.org/zh/servers" target="_blank">到这里注册</a>。
|
||||
<i class="fa-solid fa-globe"></i> 登录{{ site_name }}需要一个联邦宇宙(Fediverse,也被称为长毛象)实例账号,如果你还没有账号,可以<a href="https://joinmastodon.org/zh/servers" target="_blank">到这里注册</a>。
|
||||
</p>
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-question"></i> 如果有任何问题或建议,欢迎通过<a href="https://mastodon.social/@neodb">联邦网络</a>或<a href="https://discord.gg/uprvcH8gqD">Discord</a>和我们联系。
|
||||
<i class="fa-solid fa-circle-question"></i> 如果有任何问题或建议,欢迎通过<a href="https://mastodon.social/@neodb">联邦宇宙</a>或<a href="https://discord.gg/uprvcH8gqD">Discord</a>和我们联系。
|
||||
</p>
|
||||
<p>
|
||||
<i class="fa-solid fa-door-open"></i> <a href="{% url 'users:login' %}">点击这里</a>登录。
|
||||
|
|
29
common/templates/common/info.html
Normal file
29
common/templates/common/info.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }}</title>
|
||||
{% include "common_libs.html" with jquery=0 v2=1 %}
|
||||
</head>
|
||||
<body>
|
||||
{% include "_header.html" %}
|
||||
<main class="container">
|
||||
<article class="error">
|
||||
<header>
|
||||
<h3>{{ msg }}</h3>
|
||||
</header>
|
||||
{{ secondary_msg|default:"" }}
|
||||
</article>
|
||||
</main>
|
||||
{% include "_footer.html" %}
|
||||
</body>
|
||||
</html>
|
|
@ -10,12 +10,12 @@ most settings resides in `settings.py`, a few notable ones:
|
|||
- `SITE_INFO['site_name']` change by you need
|
||||
- `CLIENT_NAME` site name shown in Mastodon app page
|
||||
- `APP_WEBSITE` external root url for your side
|
||||
- `REDIRECT_URIS` this should be `APP_WEBSITE + "/users/OAuth2_login/"` . It can be multiple urls separated by `\n` , but not all Fediverse software support it well. Also note changing this later may invalidate app token granted previously
|
||||
- `REDIRECT_URIS` this should be `APP_WEBSITE + "/account/login/oauth"` . It can be multiple urls separated by `\n` , but not all Fediverse software support it well. Also note changing this later may invalidate app token granted previously
|
||||
- `MASTODON_ALLOW_ANY_SITE` set to `True` so that user can login via any Mastodon API compatible sites (e.g. Mastodon/Pleroma)
|
||||
- `MASTODON_CLIENT_SCOPE` change it later may invalidate app token granted previously
|
||||
- `ADMIN_URL` admin page url, keep it private
|
||||
- `SEARCH_BACKEND` should be either `TYPESENSE` or `MEILISEARCH` so that search and index can function. `None` will use default database search, which is for development only and may gets deprecated soon.
|
||||
|
||||
|
||||
|
||||
Settings for Scrapers
|
||||
---------------------
|
||||
|
|
|
@ -16,7 +16,7 @@ class ReviewForm(forms.ModelForm):
|
|||
title = forms.CharField(label=_("评论标题"))
|
||||
body = MarkdownxFormField(label=_("评论正文 (Markdown格式可参考下方语法范例)"), strip=False)
|
||||
share_to_mastodon = forms.BooleanField(
|
||||
label=_("分享到联邦网络"), initial=False, required=False
|
||||
label=_("分享到联邦宇宙"), initial=False, required=False
|
||||
)
|
||||
id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
visibility = forms.TypedChoiceField(
|
||||
|
@ -38,7 +38,7 @@ class CollectionForm(forms.ModelForm):
|
|||
# id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
title = forms.CharField(label=_("标题"))
|
||||
brief = MarkdownxFormField(label=_("介绍 (Markdown)"), strip=False)
|
||||
# share_to_mastodon = forms.BooleanField(label=_("分享到联邦网络"), initial=True, required=False)
|
||||
# share_to_mastodon = forms.BooleanField(label=_("分享到联邦宇宙"), initial=True, required=False)
|
||||
visibility = forms.TypedChoiceField(
|
||||
label=_("可见性"),
|
||||
initial=0,
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if mark.metadata.shared_link %} href="{{ mark.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if mark.metadata.shared_link %} href="{{ mark.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
<span class="timestamp">{{ mark.created_time|date }}</span>
|
||||
</div>
|
||||
|
@ -91,7 +91,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if mark.review.metadata.shared_link %} href="{{ mark.review.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if mark.review.metadata.shared_link %} href="{{ mark.review.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if mark.review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
<span class="timestamp">{{ mark.review.created_time|date }}</span>
|
||||
</span>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
hx-get="{% url 'journal:collection_share' collection.uuid %}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend"
|
||||
title="分享到联邦网络"><i class="fa-solid fa-share-nodes"></i></a>
|
||||
title="分享到联邦宇宙"><i class="fa-solid fa-share-nodes"></i></a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
id="id_share_to_mastodon"
|
||||
value="1"
|
||||
{% if not request.user.preference.default_no_share %}checked{% endif %}>
|
||||
分享到联邦网络
|
||||
分享到联邦宇宙
|
||||
</label>
|
||||
</div>
|
||||
<div class="mark-modal__option" style="width:max-content;">
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
<textarea name="text"
|
||||
rows="5"
|
||||
autofocus
|
||||
placeholder="提示: 善用 >!文字!< 标记可隐藏剧透; 超过360字可能无法分享到联邦网络实例时间线。"
|
||||
placeholder="提示: 善用 >!文字!< 标记可隐藏剧透; 超过360字可能无法分享到联邦宇宙实例时间线。"
|
||||
id="id_text">{{ mark.comment_text|default:"" }}</textarea>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
@ -114,7 +114,7 @@
|
|||
id="id_share_to_mastodon"
|
||||
value="1"
|
||||
{% if not request.user.preference.default_no_share %}checked{% endif %}>
|
||||
分享到联邦网络
|
||||
分享到联邦宇宙
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if review.metadata.shared_link %} href="{{ review.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if review.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
{% if request.user == review.owner %}{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<span>
|
||||
<a target="_blank"
|
||||
rel="noopener"
|
||||
{% if collection.metadata.shared_link %} href="{{ collection.metadata.shared_link }}" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if collection.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
{% if collection.metadata.shared_link %} href="{{ collection.metadata.shared_link }}" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if collection.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %}"></i></a>
|
||||
</span>
|
||||
<span class="timestamp">{{ collection.created_time|date }}</span>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ class CollectionTest(TestCase):
|
|||
def setUp(self):
|
||||
self.book1 = Edition.objects.create(title="Hyperion")
|
||||
self.book2 = Edition.objects.create(title="Andymion")
|
||||
self.user = User.objects.create()
|
||||
self.user = User.objects.create(email="a@b.com")
|
||||
pass
|
||||
|
||||
def test_collection(self):
|
||||
|
@ -39,7 +39,7 @@ class ShelfTest(TestCase):
|
|||
pass
|
||||
|
||||
def test_shelf(self):
|
||||
user = User.objects.create(mastodon_site="site", username="name")
|
||||
user = User.objects.create(mastodon_site="site", mastodon_username="name")
|
||||
shelf_manager = ShelfManager(user=user)
|
||||
self.assertEqual(user.shelf_set.all().count(), 3)
|
||||
book1 = Edition.objects.create(title="Hyperion")
|
||||
|
@ -102,9 +102,13 @@ class TagTest(TestCase):
|
|||
self.book1 = Edition.objects.create(title="Hyperion")
|
||||
self.book2 = Edition.objects.create(title="Andymion")
|
||||
self.movie1 = Edition.objects.create(title="Hyperion, The Movie")
|
||||
self.user1 = User.objects.create(mastodon_site="site", username="name")
|
||||
self.user2 = User.objects.create(mastodon_site="site2", username="name2")
|
||||
self.user3 = User.objects.create(mastodon_site="site2", username="name3")
|
||||
self.user1 = User.objects.create(mastodon_site="site", mastodon_username="name")
|
||||
self.user2 = User.objects.create(
|
||||
mastodon_site="site2", mastodon_username="name2"
|
||||
)
|
||||
self.user3 = User.objects.create(
|
||||
mastodon_site="site2", mastodon_username="name3"
|
||||
)
|
||||
pass
|
||||
|
||||
def test_user_tag(self):
|
||||
|
@ -120,7 +124,7 @@ class TagTest(TestCase):
|
|||
class MarkTest(TestCase):
|
||||
def setUp(self):
|
||||
self.book1 = Edition.objects.create(title="Hyperion")
|
||||
self.user1 = User.objects.create(mastodon_site="site", username="name")
|
||||
self.user1 = User.objects.create(mastodon_site="site", mastodon_username="name")
|
||||
pref = self.user1.get_preference()
|
||||
pref.default_visibility = 2
|
||||
pref.save()
|
||||
|
|
|
@ -127,9 +127,9 @@ def render_relogin(request):
|
|||
"common/error.html",
|
||||
{
|
||||
"url": reverse("users:connect") + "?domain=" + request.user.mastodon_site,
|
||||
"msg": _("信息已保存,但是未能分享到联邦网络"),
|
||||
"msg": _("信息已保存,但是未能分享到联邦宇宙"),
|
||||
"secondary_msg": _(
|
||||
"可能是你在联邦网络(Mastodon/Pleroma/...)的登录状态过期了,正在跳转到联邦网络重新登录😼"
|
||||
"可能是你在联邦宇宙(Mastodon/Pleroma/...)的登录状态过期了,正在跳转到联邦宇宙重新登录😼"
|
||||
),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -53,7 +53,7 @@ class MastodonApplicationModelAdmin(admin.ModelAdmin):
|
|||
try:
|
||||
response = create_app(request.POST.get("domain_name"))
|
||||
except (Timeout, ConnectionError):
|
||||
request.POST["domain_name"] = _("联邦网络请求超时。")
|
||||
request.POST["domain_name"] = _("联邦宇宙请求超时。")
|
||||
except Exception as e:
|
||||
request.POST["domain_name"] = str(e)
|
||||
else:
|
||||
|
|
|
@ -301,7 +301,7 @@ def get_mastodon_application(login_domain):
|
|||
|
||||
|
||||
def get_mastodon_login_url(app, login_domain, request):
|
||||
url = request.scheme + "://" + request.get_host() + reverse("users:OAuth2_login")
|
||||
url = request.scheme + "://" + request.get_host() + "/users/OAuth2_login/"
|
||||
if login_domain == TWITTER_DOMAIN:
|
||||
return f"https://twitter.com/i/oauth2/authorize?response_type=code&client_id={app.client_id}&redirect_uri={quote(url)}&scope={quote(settings.TWITTER_CLIENT_SCOPE)}&state=state&code_challenge=challenge&code_challenge_method=plain"
|
||||
version = app.server_version or ""
|
||||
|
@ -326,9 +326,7 @@ def get_mastodon_login_url(app, login_domain, request):
|
|||
def obtain_token(site, request, code):
|
||||
"""Returns token if success else None."""
|
||||
mast_app = MastodonApplication.objects.get(domain_name=site)
|
||||
redirect_uri = (
|
||||
request.scheme + "://" + request.get_host() + reverse("users:OAuth2_login")
|
||||
)
|
||||
redirect_uri = request.scheme + "://" + request.get_host() + "/users/OAuth2_login/"
|
||||
payload = {
|
||||
"client_id": mast_app.client_id,
|
||||
"client_secret": mast_app.client_secret,
|
||||
|
|
|
@ -14,7 +14,7 @@ def mastodon_request_included(func):
|
|||
return func(*args, **kwargs)
|
||||
except (Timeout, ConnectionError):
|
||||
return render(
|
||||
args[0], "common/error.html", {"msg": _("联邦网络请求超时叻_(´ཀ`」 ∠)__ ")}
|
||||
args[0], "common/error.html", {"msg": _("联邦宇宙请求超时叻_(´ཀ`」 ∠)__ ")}
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
|
|
@ -21,6 +21,7 @@ django-tz-detect
|
|||
django-bleach
|
||||
django-redis
|
||||
django-oauth-toolkit
|
||||
django-anymail
|
||||
easy-thumbnails
|
||||
lxml
|
||||
openpyxl
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
<span>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<div class="spacing">
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<a href="{{ activity.action_object.metadata.shared_link }}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="打开联邦网络分享链接"><i class="fa-solid fa-circle-nodes"></i></a>
|
||||
title="打开联邦宇宙分享链接"><i class="fa-solid fa-circle-nodes"></i></a>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
<span>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<div class="spacing">
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
<span>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦网络分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
<a {% if activity.action_object.metadata.shared_link %} href="{{ activity.action_object.metadata.shared_link }}" target="_blank" rel="noopener" title="打开联邦宇宙分享链接" {% else %} class="disabled" {% endif %}><i class="fa-solid {% if activity.action_object.visibility > 0 %} fa-lock {% else %} fa-globe {% endif %} "></i></a>
|
||||
</span>
|
||||
</span>
|
||||
<div class="spacing">
|
||||
|
|
233
users/account.py
233
users/account.py
|
@ -22,6 +22,11 @@ from journal.models import remove_data_by_user
|
|||
from django.db.models import Q
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Count
|
||||
from django import forms
|
||||
from django.core.signing import TimestampSigner
|
||||
from django.core.mail import send_mail
|
||||
from loguru import logger
|
||||
from django.core.validators import EmailValidator
|
||||
|
||||
|
||||
# the 'login' page that user can see
|
||||
|
@ -58,12 +63,37 @@ def login(request):
|
|||
raise BadRequest()
|
||||
|
||||
|
||||
# connect will redirect to mastodon server
|
||||
# connect will send verification email or redirect to mastodon server
|
||||
def connect(request):
|
||||
if request.method == "POST" and request.POST.get("method") == "email":
|
||||
login_email = request.POST.get("email", "")
|
||||
try:
|
||||
EmailValidator()(login_email)
|
||||
except:
|
||||
return render(
|
||||
request,
|
||||
"common/error.html",
|
||||
{"msg": _("无效的电子邮件地址")},
|
||||
)
|
||||
user = User.objects.filter(email=login_email).first()
|
||||
django_rq.get_queue("mastodon").enqueue(
|
||||
send_verification_link,
|
||||
user.pk if user else 0,
|
||||
"login" if user else "register",
|
||||
login_email,
|
||||
)
|
||||
return render(
|
||||
request,
|
||||
"common/info.html",
|
||||
{
|
||||
"msg": _("验证邮件已发送"),
|
||||
"secondary_msg": _("请查阅收件箱"),
|
||||
},
|
||||
)
|
||||
login_domain = (
|
||||
request.session["swap_domain"]
|
||||
if request.session.get("swap_login")
|
||||
else request.GET.get("domain")
|
||||
else (request.POST.get("domain") or request.GET.get("domain"))
|
||||
)
|
||||
if not login_domain:
|
||||
return render(
|
||||
|
@ -147,8 +177,9 @@ def OAuth2_login(request):
|
|||
else: # newly registered user
|
||||
code, user_data = verify_account(site, token)
|
||||
if code != 200 or user_data is None:
|
||||
return render(request, "common/error.html", {"msg": _("联邦网络访问失败😫")})
|
||||
new_user = User(
|
||||
return render(request, "common/error.html", {"msg": _("联邦宇宙访问失败😫")})
|
||||
return register_new_user(
|
||||
request,
|
||||
username=None,
|
||||
mastodon_username=user_data["username"],
|
||||
mastodon_id=user_data["id"],
|
||||
|
@ -157,11 +188,15 @@ def OAuth2_login(request):
|
|||
mastodon_refresh_token=refresh_token,
|
||||
mastodon_account=user_data,
|
||||
)
|
||||
new_user.save()
|
||||
Preference.objects.create(user=new_user)
|
||||
request.session["new_user"] = True
|
||||
auth_login(request, new_user)
|
||||
return redirect(reverse("users:register"))
|
||||
|
||||
|
||||
def register_new_user(request, **param):
|
||||
new_user = User(**param)
|
||||
new_user.save()
|
||||
Preference.objects.create(user=new_user)
|
||||
request.session["new_user"] = True
|
||||
auth_login(request, new_user)
|
||||
return redirect(reverse("users:register"))
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
|
@ -188,13 +223,166 @@ def reconnect(request):
|
|||
raise BadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
def register(request):
|
||||
if request.session.get("new_user"):
|
||||
del request.session["new_user"]
|
||||
return render(request, "users/register.html")
|
||||
class RegistrationForm(forms.ModelForm):
|
||||
email = forms.EmailField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["username"]
|
||||
|
||||
def clean_username(self):
|
||||
username = self.cleaned_data.get("username")
|
||||
if username and self.instance and self.instance.username:
|
||||
username = self.instance.username
|
||||
return username
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get("email")
|
||||
if (
|
||||
email
|
||||
and User.objects.filter(email=email)
|
||||
.exclude(pk=self.instance.pk if self.instance else -1)
|
||||
.exists()
|
||||
):
|
||||
raise forms.ValidationError(_("This email address is already in use."))
|
||||
return email
|
||||
|
||||
|
||||
def send_verification_link(user_id, action, email):
|
||||
s = {"i": user_id, "e": email, "a": action}
|
||||
v = TimestampSigner().sign_object(s) # type: ignore
|
||||
site = settings.SITE_INFO["site_name"]
|
||||
if action == "verify":
|
||||
subject = f'{settings.SITE_INFO["site_name"]} - {_("验证电子邮件地址")}'
|
||||
url = settings.SITE_INFO["site_url"] + "/account/verify_email?c=" + v
|
||||
msg = f"你好,\n请点击以下链接验证你的电子邮件地址 {email}\n{url}\n\n如果你没有注册过本站,请忽略此邮件。"
|
||||
elif action == "login":
|
||||
subject = f'{settings.SITE_INFO["site_name"]} - {_("登录")}'
|
||||
url = settings.SITE_INFO["site_url"] + "/account/login/email?c=" + v
|
||||
msg = f"你好,\n请点击以下链接登录{email}账号\n{url}\n\n如果你没有请求登录本站,请忽略此邮件;如果你确信账号存在安全风险,请更改注册邮件地址或与我们联系。"
|
||||
elif action == "register":
|
||||
subject = f'{settings.SITE_INFO["site_name"]} - {_("注册新账号")}'
|
||||
url = settings.SITE_INFO["site_url"] + "/account/register_email?c=" + v
|
||||
msg = f"你好,\n{site}还没有与{email}关联的账号。你希望注册一个新账号吗?\n"
|
||||
msg += f"如果你已经注册过{site}或联邦宇宙(长毛象),不必重新注册,只要用联邦宇宙身份登录{site},再关联这个电子邮件地址,未来就可以通过邮件登录。\n"
|
||||
msg += f"\n如果你还没有联邦宇宙身份,可以访问这里选择实例并创建一个: https://joinmastodon.org/zh/servers\n"
|
||||
if settings.ALLOW_EMAIL_ONLY_ACCOUNT:
|
||||
msg += f"\n如果你不便使用联邦宇宙身份,可以点击以下链接注册新的{site}账号,以后再关联到联邦宇宙。\n{url}\n"
|
||||
msg += f"\n如果你没有打算用此电子邮件地址注册或登录本站,请忽略此邮件。"
|
||||
else:
|
||||
return redirect(reverse("common:home"))
|
||||
raise ValueError("Invalid action")
|
||||
try:
|
||||
send_mail(
|
||||
subject=subject,
|
||||
message=msg,
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
recipient_list=[email],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
def verify_email(request):
|
||||
error = ""
|
||||
try:
|
||||
s = TimestampSigner().unsign_object(request.GET.get("c"), max_age=60 * 15) # type: ignore
|
||||
email = s["e"]
|
||||
action = s["a"]
|
||||
if action == "verify":
|
||||
user = User.objects.get(pk=s["i"])
|
||||
if user.pending_email == email:
|
||||
user.email = user.pending_email
|
||||
user.pending_email = None
|
||||
user.save(update_fields=["email", "pending_email"])
|
||||
return render(
|
||||
request, "users/verify_email.html", {"success": True, "user": user}
|
||||
)
|
||||
else:
|
||||
error = _("电子邮件地址不匹配")
|
||||
elif action == "login":
|
||||
user = User.objects.get(pk=s["i"])
|
||||
if user.email == email:
|
||||
auth_login(request, user)
|
||||
return redirect(reverse("common:home"))
|
||||
else:
|
||||
error = _("电子邮件地址不匹配")
|
||||
elif action == "register":
|
||||
user = User.objects.filter(email=email).first()
|
||||
if user:
|
||||
error = _("此电子邮件地址已被注册")
|
||||
else:
|
||||
return register_new_user(request, username=None, email=email)
|
||||
except Exception as e:
|
||||
error = _("链接已失效")
|
||||
return render(
|
||||
request, "users/verify_email.html", {"success": False, "error": error}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def register(request):
|
||||
form = None
|
||||
if settings.MASTODON_ALLOW_ANY_SITE:
|
||||
form = RegistrationForm(request.POST)
|
||||
form.instance = (
|
||||
User.objects.get(pk=request.user.pk)
|
||||
if request.user.is_authenticated
|
||||
else None
|
||||
)
|
||||
if request.method == "GET" or not form:
|
||||
return render(request, "users/register.html", {"form": form})
|
||||
elif request.method == "POST":
|
||||
username_changed = False
|
||||
email_cleared = False
|
||||
if not form.is_valid():
|
||||
return render(request, "users/register.html", {"form": form})
|
||||
if request.user.username is None and form.cleaned_data["username"]:
|
||||
if User.objects.filter(username=form.cleaned_data["username"]).exists():
|
||||
return render(
|
||||
request,
|
||||
"users/register.html",
|
||||
{
|
||||
"form": form,
|
||||
"error": _("用户名已被使用"),
|
||||
},
|
||||
)
|
||||
request.user.username = form.cleaned_data["username"]
|
||||
username_changed = True
|
||||
if form.cleaned_data["email"]:
|
||||
if form.cleaned_data["email"] != request.user.email:
|
||||
if User.objects.filter(email=form.cleaned_data["email"]).exists():
|
||||
return render(
|
||||
request,
|
||||
"users/register.html",
|
||||
{
|
||||
"form": form,
|
||||
"error": _("电子邮件地址已被使用"),
|
||||
},
|
||||
)
|
||||
request.user.pending_email = form.cleaned_data["email"]
|
||||
else:
|
||||
request.user.pending_email = None
|
||||
elif request.user.email or request.user.pending_email:
|
||||
request.user.pending_email = None
|
||||
request.user.email = None
|
||||
email_cleared = True
|
||||
request.user.save()
|
||||
if request.user.pending_email:
|
||||
django_rq.get_queue("mastodon").enqueue(
|
||||
send_verification_link,
|
||||
request.user.id,
|
||||
"verify",
|
||||
request.user.pending_email,
|
||||
)
|
||||
messages.add_message(request, messages.INFO, _("已发送验证邮件,请查收。"))
|
||||
if username_changed:
|
||||
messages.add_message(request, messages.INFO, _("用户名已设置。"))
|
||||
if email_cleared:
|
||||
messages.add_message(request, messages.INFO, _("电子邮件地址已取消关联。"))
|
||||
if request.session.get("new_user"):
|
||||
del request.session["new_user"]
|
||||
return redirect(request.GET.get("next", reverse("common:home")))
|
||||
|
||||
|
||||
def swap_login(request, token, site, refresh_token):
|
||||
|
@ -244,7 +432,7 @@ def swap_login(request, token, site, refresh_token):
|
|||
request, messages.INFO, _(f"账号身份已更新为 {username}@{site}。")
|
||||
)
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, _("连接联邦网络获取身份信息失败。"))
|
||||
messages.add_message(request, messages.ERROR, _("连接联邦宇宙获取身份信息失败。"))
|
||||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
|
@ -263,15 +451,22 @@ def auth_logout(request):
|
|||
auth.logout(request)
|
||||
|
||||
|
||||
def clear_data_task(user_id):
|
||||
user = User.objects.get(pk=user_id)
|
||||
user_str = str(user)
|
||||
remove_data_by_user(user)
|
||||
user.clear()
|
||||
user.save()
|
||||
logger.warning(f"User {user_str} data cleared.")
|
||||
|
||||
|
||||
@login_required
|
||||
def clear_data(request):
|
||||
if request.META.get("HTTP_AUTHORIZATION"):
|
||||
raise BadRequest("Only for web login")
|
||||
if request.method == "POST":
|
||||
if request.POST.get("verification") == request.user.mastodon_acct:
|
||||
remove_data_by_user(request.user)
|
||||
request.user.clear()
|
||||
request.user.save()
|
||||
django_rq.get_queue("mastodon").enqueue(clear_data_task, request.user.id)
|
||||
auth_logout(request)
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
|
|
|
@ -62,6 +62,18 @@ def data(request):
|
|||
)
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def account_info(request):
|
||||
return render(
|
||||
request,
|
||||
"users/account.html",
|
||||
{
|
||||
"allow_any_site": settings.MASTODON_ALLOW_ANY_SITE,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def data_import_status(request):
|
||||
return render(
|
||||
|
|
22
users/migrations/0007_user_pending_email.py
Normal file
22
users/migrations/0007_user_pending_email.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 3.2.19 on 2023-07-03 18:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("users", "0006_unique_email"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="user",
|
||||
name="pending_email",
|
||||
field=models.EmailField(
|
||||
default=None,
|
||||
max_length=254,
|
||||
null=True,
|
||||
verbose_name="email address pending verification",
|
||||
),
|
||||
),
|
||||
]
|
24
users/migrations/0008_user_at_least_one_login_method.py
Normal file
24
users/migrations/0008_user_at_least_one_login_method.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 3.2.19 on 2023-07-04 02:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("users", "0007_user_pending_email"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddConstraint(
|
||||
model_name="user",
|
||||
constraint=models.CheckConstraint(
|
||||
check=models.Q(
|
||||
("is_active", False),
|
||||
("mastodon_username__isnull", False),
|
||||
("email__isnull", False),
|
||||
_connector="OR",
|
||||
),
|
||||
name="at_least_one_login_method",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,9 +1,7 @@
|
|||
import uuid
|
||||
import re
|
||||
from django.core import validators
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.deconstruct import deconstructible
|
||||
import django.contrib.postgres.fields as postgres
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.utils import timezone
|
||||
|
@ -14,17 +12,33 @@ from django.conf import settings
|
|||
from management.models import Announcement
|
||||
from mastodon.api import *
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
RESERVED_USERNAMES = [
|
||||
"connect",
|
||||
"oauth2_login",
|
||||
"__",
|
||||
"admin",
|
||||
"api",
|
||||
"me",
|
||||
]
|
||||
|
||||
|
||||
@deconstructible
|
||||
class UsernameValidator(validators.RegexValidator):
|
||||
regex = r"^[a-zA-Z0-9_]{2,50}$"
|
||||
regex = r"^[a-zA-Z0-9_]{2,30}$"
|
||||
message = _(
|
||||
"Enter a valid username. This value may contain only unaccented lowercase a-z "
|
||||
"and uppercase A-Z letters, numbers, and _ characters."
|
||||
)
|
||||
flags = re.ASCII
|
||||
|
||||
def __call__(self, value):
|
||||
if value and value.lower() in RESERVED_USERNAMES:
|
||||
raise ValidationError(self.message, code=self.code)
|
||||
return super().__call__(value)
|
||||
|
||||
|
||||
def report_image_path(instance, filename):
|
||||
return GenerateDateUUIDMediaFilePath(
|
||||
|
@ -46,6 +60,9 @@ class User(AbstractUser):
|
|||
},
|
||||
)
|
||||
email = models.EmailField(_("email address"), unique=True, default=None, null=True)
|
||||
pending_email = models.EmailField(
|
||||
_("email address pending verification"), default=None, null=True
|
||||
)
|
||||
following = models.JSONField(default=list)
|
||||
mastodon_id = models.CharField(max_length=100, default=None, null=True)
|
||||
mastodon_username = models.CharField(max_length=100, default=None, null=True)
|
||||
|
@ -74,6 +91,14 @@ class User(AbstractUser):
|
|||
fields=["mastodon_id", "mastodon_site"],
|
||||
name="unique_mastodon_id",
|
||||
),
|
||||
models.CheckConstraint(
|
||||
check=(
|
||||
Q(is_active=False)
|
||||
| Q(mastodon_username__isnull=False)
|
||||
| Q(email__isnull=False)
|
||||
),
|
||||
name="at_least_one_login_method",
|
||||
),
|
||||
]
|
||||
|
||||
# def save(self, *args, **kwargs):
|
||||
|
|
129
users/templates/users/account.html
Normal file
129
users/templates/users/account.html
Normal file
|
@ -0,0 +1,129 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="classic-page">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }} - 账号信息</title>
|
||||
{% include "common_libs.html" with jquery=0 v2=1 %}
|
||||
</head>
|
||||
<body>
|
||||
{% include "_header.html" with current="data" %}
|
||||
<main>
|
||||
<div class="grid__main">
|
||||
{% if allow_any_site %}
|
||||
<article>
|
||||
<details open>
|
||||
<summary>{% trans '用户名、电子邮件与社交账号' %}</summary>
|
||||
<form action="{% url 'users:register' %}?next={{ request.path }}"
|
||||
method="post">
|
||||
<small>{{ error }}</small>
|
||||
<fieldset>
|
||||
<label>
|
||||
用户名
|
||||
<input name="username"
|
||||
_="on input remove [@disabled] from #save end"
|
||||
placeholder="2-30个字符,限英文字母数字下划线,确认后不可更改"
|
||||
required
|
||||
{% if request.user.username %}value="{{ request.user.username }}" aria-invalid="false" readonly{% endif %}
|
||||
pattern="^[a-zA-Z0-9_]{2,30}$" />
|
||||
</label>
|
||||
<label>
|
||||
电子邮件地址
|
||||
<input type="email"
|
||||
name="email"
|
||||
_="on input remove [@disabled] from #save then remove [@aria-invalid] end"
|
||||
{% if request.user.email %}value="{{ request.user.email }}" aria-invalid="false"{% endif %}
|
||||
placeholder="推荐,可作为备用登录方式"
|
||||
autocomplete="email" />
|
||||
{% if request.user.pending_email %}
|
||||
<small>当前待确认的电子邮件地址为{{ request.user.pending_email }},请查收邮件并点击确认链接;如长时间未收到可重新输入并保存。</small>
|
||||
{% endif %}
|
||||
</label>
|
||||
</fieldset>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans '保存' %}" disabled id="save">
|
||||
</form>
|
||||
<form action="{% url 'users:reconnect' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<label>
|
||||
社交账号
|
||||
<input type="input"
|
||||
{% if request.user.mastodon_acct %}aria-invalid="false"{% endif %}
|
||||
value="{{ request.user.mastodon_acct | default:'未关联' }}"
|
||||
readonly>
|
||||
</label>
|
||||
<label>
|
||||
如需关联到另一个社交账号,请输入新账号所在的实例域名
|
||||
<input type="input"
|
||||
name="domain"
|
||||
value=""
|
||||
placeholder="例如mastodon.online"
|
||||
_="on input remove [@disabled] from #bind end">
|
||||
</label>
|
||||
<input type="submit" value="{% trans '登录并关联新账号' %}" disabled id="bind" />
|
||||
</fieldset>
|
||||
<div>替换关联后可使用新的联邦宇宙身份来登录{{ site_name }}和控制数据可见性,已有的标记评论收藏单等数据不受影响。</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{% endif %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '更新社交关系数据' %}</summary>
|
||||
<form action="{% url 'users:sync_mastodon' %}"
|
||||
method="post"
|
||||
enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans '同步' %}" id="uploadBtn" />
|
||||
上次更新时间 {{ user.mastodon_last_refresh }}
|
||||
<div>
|
||||
为了正确高效的展示短评和评论,{{ site_name }}会缓存你在联邦宇宙的关注、屏蔽和静音列表。如果你刚刚更新过帐户的上锁状态、增减过关注、静音或屏蔽,希望立即生效,可以点击这里立刻更新;这类信息也会每天自动同步。
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '删除数据和账号信息' %}</summary>
|
||||
<form action="{% url 'users:clear_data' %}"
|
||||
method="post"
|
||||
onsubmit="return confirm('账号数据一旦删除后将无法恢复。确认删除吗?');">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
输入完整的 <code>用户名@实例名</code> 以确认删除
|
||||
<input type="input"
|
||||
name="verification"
|
||||
_="on input remove [@disabled] from #delete end"
|
||||
value=""
|
||||
required
|
||||
aria-invalid="true"
|
||||
aria-describedby="invalid-helper"
|
||||
placeholder="Gargron@mastodon.social">
|
||||
<small id="invalid-helper">账号数据一旦删除后将无法恢复</small>
|
||||
{% if import_status.douban_pending %}
|
||||
<input type="submit" value="暂时无法删除,因为有导入任务正在进行" disabled />
|
||||
{% else %}
|
||||
<input type="submit"
|
||||
value="{% trans '永久删除' %}"
|
||||
class="contrast"
|
||||
disabled
|
||||
id="delete" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
</div>
|
||||
{% include "_sidebar.html" with show_profile=1 %}
|
||||
</main>
|
||||
{% include "_footer.html" %}
|
||||
</body>
|
||||
</html>
|
|
@ -83,7 +83,7 @@
|
|||
</article>
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '导入Goodreads帐号或书单' %}</summary>
|
||||
<summary>{% trans '导入Goodreads账号或书单' %}</summary>
|
||||
<form action="{% url 'users:import_goodreads' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
|
@ -176,59 +176,6 @@
|
|||
</form>
|
||||
</details>
|
||||
</article>
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '更新社交关系数据' %}</summary>
|
||||
<form action="{% url 'users:sync_mastodon' %}"
|
||||
method="post"
|
||||
enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans '同步' %}" id="uploadBtn" />
|
||||
上次更新时间 {{ user.mastodon_last_refresh }}
|
||||
<div>
|
||||
为了正确高效的展示短评和评论,{{ site_name }}会缓存你在联邦网络的关注、屏蔽和静音列表。如果你刚刚更新过帐户的上锁状态、增减过关注、静音或屏蔽,希望立即生效,可以点击这里立刻更新;这类信息也会每天自动同步。
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{% if allow_any_site %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '替换社交账号' %}</summary>
|
||||
<form action="{% url 'users:reconnect' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
输入新社交账号所在的实例域名
|
||||
<input type="input" name="domain" value="" placeholder="例如mastodon.online">
|
||||
<input type="submit" value="{% trans '登录新账号' %}" id="uploadBtn" />
|
||||
</div>
|
||||
<div>替换后可使用新的联邦网络身份来登录{{ site_name }}和控制数据可见性,已有的标记评论收藏单等数据不受影响。</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{% endif %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '删除数据和帐号信息' %}</summary>
|
||||
<form action="{% url 'users:clear_data' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
输入完整的 用户名@实例名 以确认删除
|
||||
<input type="input"
|
||||
name="verification"
|
||||
value=""
|
||||
required
|
||||
placeholder="Gargron@mastodon.social">
|
||||
{% if import_status.douban_pending %}
|
||||
<input type="submit" value="暂时无法删除,因为有导入任务正在进行" disabled />
|
||||
{% else %}
|
||||
<input type="submit" value="{% trans '永久删除' %}" id="uploadBtn" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>删除将无法撤销</div>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
</div>
|
||||
{% include "_sidebar.html" with show_profile=1 %}
|
||||
</main>
|
||||
|
|
|
@ -50,43 +50,82 @@
|
|||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'common:home' %}" class="button">{% trans '前往首页' %}</a>
|
||||
{% else %}
|
||||
<form action="{% url 'users:connect' %}">
|
||||
<form action="{% url 'users:connect' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{% if allow_any_site %}
|
||||
<input required
|
||||
name="email"
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="电子邮件地址"
|
||||
disabled
|
||||
autocomplete="email"
|
||||
style="display:none" />
|
||||
<input required
|
||||
name="domain"
|
||||
id="domain"
|
||||
autofocus
|
||||
pattern="(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,})"
|
||||
pattern="(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,})"
|
||||
title="实例域名(不含@和@之前的部分),如mastodon.social"
|
||||
placeholder="实例域名(不含@和@之前的部分),如mastodon.social"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false" />
|
||||
<input type='submit' value="{% trans '授权登录' %}" id="loginButton" disabled />
|
||||
<div role="group" style="width:100%">
|
||||
<select style="width:max-content"
|
||||
required
|
||||
onchange="switch_login()"
|
||||
name="method">
|
||||
<option disabled value="">选择登录方式</option>
|
||||
<option selected value="fedi">通过联邦宇宙</option>
|
||||
<option value="email">通过电子邮件</option>
|
||||
</select>
|
||||
<input style="width:100%"
|
||||
type='submit'
|
||||
value="{% trans '授权登录' %}"
|
||||
id="loginButton"
|
||||
disabled />
|
||||
</div>
|
||||
<script type="text/javascript">if (Cookies.get('mastodon_domain')) $('#domain').val(Cookies.get('mastodon_domain'));</script>
|
||||
{{ sites|json_script:"sites-data" }}
|
||||
<script>
|
||||
const sites = JSON.parse(document.getElementById('sites-data').textContent);
|
||||
const autoCompleteJS = new autoComplete({ placeHolder: "输入或选择实例域名(不含@和@之前的部分)",
|
||||
selector: "#domain",
|
||||
data: {
|
||||
src: sites
|
||||
},
|
||||
submit: true,
|
||||
resultsList: {
|
||||
tabSelect: true,
|
||||
maxResults: 10
|
||||
},
|
||||
events: {
|
||||
input: {
|
||||
selection: (event) => {
|
||||
const selection = event.detail.selection.value;
|
||||
autoCompleteJS.input.value = selection;
|
||||
function switch_login(){
|
||||
if ($('select').val() == 'email') {
|
||||
$('#domain').prop("disabled", true);
|
||||
$('#domain').hide();
|
||||
$('#email').prop("disabled", false);
|
||||
$('#email').show();
|
||||
$('#email')[0].focus();
|
||||
//$('#domain').val('');
|
||||
} else {
|
||||
$('#email').prop("disabled", true);
|
||||
$('#email').hide();
|
||||
$('#domain').prop("disabled", false);
|
||||
$('#domain').show();
|
||||
$('#domain')[0].focus();
|
||||
//$('#email').val('');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const sites = JSON.parse(document.getElementById('sites-data').textContent);
|
||||
const autoCompleteJS = new autoComplete({ placeHolder: "输入或选择实例域名(不含@和@之前的部分)",
|
||||
selector: "#domain",
|
||||
data: {
|
||||
src: sites
|
||||
},
|
||||
submit: true,
|
||||
resultsList: {
|
||||
tabSelect: true,
|
||||
maxResults: 10
|
||||
},
|
||||
events: {
|
||||
input: {
|
||||
selection: (event) => {
|
||||
const selection = event.detail.selection.value;
|
||||
autoCompleteJS.input.value = selection;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<select name="domain" placeholder="test">
|
||||
|
@ -101,9 +140,10 @@
|
|||
<div class="delayed">部分模块加载超时,请检查网络(翻墙)设置。</div>
|
||||
</div>
|
||||
</article>
|
||||
{% comment %} </main> {% endcomment %}
|
||||
<footer>
|
||||
<small>本站使用cookie提供必要的功能,继续访问视为同意。</small>
|
||||
<small>如果你还没有注册过<em data-tooltip="联邦宇宙(Fediverse 亦称长毛象)是一种分布式社交网络">联邦宇宙</em>,可先<a href="https://joinmastodon.org/zh/servers" target="_blank">选择实例并注册</a></small>
|
||||
<br>
|
||||
<small>继续访问或注册视为同意本站<a href="{% url 'management:retrieve_slug' 'data-policy' %}">数据方针</a>及使用cookie提供必要功能</small>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -18,112 +18,104 @@
|
|||
<main>
|
||||
<div class="grid__main">
|
||||
<article>
|
||||
<form action="{% url 'users:preferences' %}" method="post">
|
||||
<section>
|
||||
<details>
|
||||
<summary>{% trans '使用偏好设置' %}</summary>
|
||||
{% csrf_token %}
|
||||
<span>{% trans '新标记默认可见性:' %}</span>
|
||||
<div>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="0"
|
||||
required=""
|
||||
id="id_visibility_0"
|
||||
{% if request.user.preference.default_visibility == 0 %}checked{% endif %}>
|
||||
<label for="id_visibility_0">公开</label>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="1"
|
||||
required=""
|
||||
id="id_visibility_1"
|
||||
{% if request.user.preference.default_visibility == 1 %}checked{% endif %}>
|
||||
<label for="id_visibility_1">仅关注者</label>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="2"
|
||||
required=""
|
||||
id="id_visibility_2"
|
||||
{% if request.user.preference.default_visibility == 2 %}checked{% endif %}>
|
||||
<label for="id_visibility_2">仅自己</label>
|
||||
</div>
|
||||
<br>
|
||||
<span>{% trans '登录后显示:' %}</span>
|
||||
<div>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="0"
|
||||
id="classic_homepage0"
|
||||
{% if request.user.preference.classic_homepage == 0 %}checked{% endif %}>
|
||||
<label for="classic_homepage0">内容发现</label>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="2"
|
||||
id="classic_homepage2"
|
||||
{% if request.user.preference.classic_homepage == 2 %}checked{% endif %}>
|
||||
<label for="classic_homepage2">好友动态</label>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="1"
|
||||
id="classic_homepage1"
|
||||
{% if request.user.preference.classic_homepage == 1 %}checked{% endif %}>
|
||||
<label for="classic_homepage1">个人主页</label>
|
||||
</div>
|
||||
<br style="margin-bottom:1.5em">
|
||||
<div>
|
||||
<details open>
|
||||
<summary>
|
||||
<b>{% trans '使用偏好' %}</b>
|
||||
</summary>
|
||||
<form action="{% url 'users:preferences' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans '登录后显示:' %}</legend>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="0"
|
||||
id="classic_homepage0"
|
||||
{% if request.user.preference.classic_homepage == 0 %}checked{% endif %}>
|
||||
<label for="classic_homepage0">内容发现</label>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="2"
|
||||
id="classic_homepage2"
|
||||
{% if request.user.preference.classic_homepage == 2 %}checked{% endif %}>
|
||||
<label for="classic_homepage2">好友动态</label>
|
||||
<input type="radio"
|
||||
name="classic_homepage"
|
||||
value="1"
|
||||
id="classic_homepage1"
|
||||
{% if request.user.preference.classic_homepage == 1 %}checked{% endif %}>
|
||||
<label for="classic_homepage1">个人主页</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans '新标记默认可见性:' %}</legend>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="0"
|
||||
required=""
|
||||
id="id_visibility_0"
|
||||
{% if request.user.preference.default_visibility == 0 %}checked{% endif %}>
|
||||
<label for="id_visibility_0">公开</label>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="1"
|
||||
required=""
|
||||
id="id_visibility_1"
|
||||
{% if request.user.preference.default_visibility == 1 %}checked{% endif %}>
|
||||
<label for="id_visibility_1">仅关注者</label>
|
||||
<input type="radio"
|
||||
name="default_visibility"
|
||||
value="2"
|
||||
required=""
|
||||
id="id_visibility_2"
|
||||
{% if request.user.preference.default_visibility == 2 %}checked{% endif %}>
|
||||
<label for="id_visibility_2">仅自己</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="no_anonymous_view"
|
||||
id="no_anonymous_view"
|
||||
{% if request.user.preference.no_anonymous_view %}checked{% endif %}>
|
||||
<label for="no_anonymous_view">{% trans '不允许未登录用户访问你的个人主页' %}</label>
|
||||
</div>
|
||||
<br style="margin-bottom:1.5em">
|
||||
<div>
|
||||
{% trans '仅允许已登录用户查看你的个人主页' %}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="show_last_edit"
|
||||
id="show_last_edit"
|
||||
{% if request.user.preference.show_last_edit %}checked{% endif %}>
|
||||
<label for="show_last_edit">{% trans '显示你是某条目的最近编辑者' %}</label>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
<section>
|
||||
<details>
|
||||
<summary>{% trans '社交网络分享相关设置' %}</summary>
|
||||
<div>
|
||||
{% trans '显示你是某条目的最近编辑者' %}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="mastodon_publish_public"
|
||||
id="mastodon_publish_public"
|
||||
{% if request.user.preference.mastodon_publish_public %}checked{% endif %}>
|
||||
<label for="mastodon_publish_public">
|
||||
以公开方式分享的帖文发布到<em data-tooltip="选中时为public,未选中时为unlisted">公共时间轴</em>
|
||||
</label>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<span>{% trans '在联邦网络上分享帖文时在结尾附加标签:' %}</span>
|
||||
<div>
|
||||
<input name="mastodon_append_tag"
|
||||
id="tag"
|
||||
placeholder="#我的书影音"
|
||||
value="{{ request.user.preference.mastodon_append_tag }}">
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
标记时以公开方式分享的帖文发布到<em data-tooltip="选中时为public,未选中时为unlisted">公共时间轴</em>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="default_no_share"
|
||||
id="default_no_share"
|
||||
{% if request.user.preference.default_no_share %}checked{% endif %}>
|
||||
<label for="default_no_share">标记时默认不分享到联邦网络</label>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
<input type="submit" value="{% trans '保存' %}">
|
||||
</form>
|
||||
<hr>
|
||||
<section>
|
||||
<details>
|
||||
<summary>{% trans '当前设备设置' %}</summary>
|
||||
标记时默认不分享到联邦宇宙
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label for="mastodon_append_tag">{% trans '在联邦宇宙分享帖文时在结尾附加标签:' %}</label>
|
||||
<input name="mastodon_append_tag"
|
||||
id="mastodon_append_tag"
|
||||
placeholder="例如 #我的书影音"
|
||||
value="{{ request.user.preference.mastodon_append_tag }}">
|
||||
</fieldset>
|
||||
<input type="submit" value="{% trans '保存' %}">
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '当前设备设置' %}</summary>
|
||||
<form onsubmit="return false;">
|
||||
<h6>专注模式 (实验功能)</h6>
|
||||
<p>
|
||||
<input type="checkbox" id="solo_mode">
|
||||
|
@ -132,9 +124,10 @@
|
|||
<h6>自定义样式代码 (实验功能)</h6>
|
||||
<textarea id="user_style"></textarea>
|
||||
<br>
|
||||
<button onclick="save_local();">保存</button>
|
||||
</details>
|
||||
<script>
|
||||
<input type="button" onclick="save_local();" value="保存">
|
||||
</form>
|
||||
</details>
|
||||
<script>
|
||||
$("#user_style").val(localStorage.getItem("user_style")||"");
|
||||
$("#solo_mode").prop("checked", localStorage.getItem("solo_mode")=="1");
|
||||
function save_local() {
|
||||
|
@ -142,17 +135,15 @@
|
|||
localStorage.setItem("solo_mode", $("#solo_mode").prop("checked")?"1":"0");
|
||||
alert("本地设置已保存");
|
||||
}
|
||||
</script>
|
||||
</section>
|
||||
<hr>
|
||||
<section>
|
||||
<details>
|
||||
<summary>{% trans '应用管理' %}</summary>
|
||||
<p>
|
||||
<a href="{% url 'oauth2_provider:authorized-token-list' %}">查看已授权的应用程序</a>
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
</script>
|
||||
</article>
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans '应用管理' %}</summary>
|
||||
<p>
|
||||
<a href="{% url 'oauth2_provider:authorized-token-list' %}">查看已授权的应用程序</a>
|
||||
</p>
|
||||
</details>
|
||||
</article>
|
||||
</div>
|
||||
{% include "_sidebar.html" with show_profile=1 %}
|
||||
|
|
|
@ -1,34 +1,67 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="login-page">
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }} - {% trans '注册完成' %}</title>
|
||||
<title>{{ site_name }} - {% trans '注册信息' %}</title>
|
||||
{% include "common_libs.html" with jquery=0 v2=1 %}
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<header style="text-align: center;">
|
||||
<img src="{% static 'img/logo.svg' %}" class="logo" alt="logo">
|
||||
</header>
|
||||
<p>欢迎来到{{ site_name }}!</p>
|
||||
<p>
|
||||
{{ site_name }}还在不断完善中,丰富的内容需要大家共同创造。
|
||||
试图添加垃圾数据(如添加信息混乱或缺失的书籍、以推广为主要目的的评论)将会受到严肃处理。
|
||||
{{ site_name }}继承了联邦宇宙的用户关系,比如您在联邦宇宙屏蔽了某人,那您将不会在书影音的公共区域看到TA的痕迹。
|
||||
本站为非盈利站点,cookie和其它数据保管使用原则请参阅<a href="{% url 'management:retrieve_slug' 'data-policy' %}">站内公告</a>。
|
||||
</p>
|
||||
<p>
|
||||
此外,{{ site_name }}现处于测试阶段,疏漏在所难免,请妥善备份您的数据。
|
||||
使用过程中遇到的问题或者错误欢迎向<a href="{{ support_link }}">维护者</a>提出。感谢理解和支持!
|
||||
</p>
|
||||
<form action="{% url 'common:home' %}">
|
||||
<input type="submit"
|
||||
class="button"
|
||||
value="{% trans 'Cut the sh*t and get me in!' %}">
|
||||
</form>
|
||||
</article>
|
||||
<div class="container">
|
||||
<article>
|
||||
<header style="text-align: center;">
|
||||
<img src="{% static 'img/logo.svg' %}" class="logo" alt="logo">
|
||||
</header>
|
||||
{% if request.session.new_user %}
|
||||
<h4>欢迎来到{{ site_name }},{{ request.user.mastodon_acct }}!</h4>
|
||||
<p>
|
||||
{{ site_name }}还在不断完善中。
|
||||
丰富的内容需要大家共同创造,试图添加垃圾数据(如添加信息混乱或缺失的书籍、以推广为主要目的的评论)将会受到严肃处理。
|
||||
本站为非盈利站点,cookie和其它数据保管使用原则请参阅<a href="{% url 'management:retrieve_slug' 'data-policy' %}">站内公告</a>。
|
||||
本站提供API和导出功能,请妥善备份您的数据,使用过程中遇到的问题或者错误欢迎向<a href="{{ support_link }}">维护者</a>提出。感谢理解和支持!
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if form %}
|
||||
<form action="{% url 'users:register' %}" method="post">
|
||||
<small>{{ error }}</small>
|
||||
<fieldset>
|
||||
<label>
|
||||
请输入你想在{{ site_name }}使用的用户名
|
||||
<input name="username"
|
||||
placeholder="2-30个字符,限英文字母数字下划线,确认后不可更改"
|
||||
value="{{ form.username.value|default:request.user.username|default:'' }}"
|
||||
required
|
||||
_="on input remove [@aria-invalid] end"
|
||||
{% if request.user.username and not form.username.errors %}aria-invalid="false" readonly{% endif %}
|
||||
{% if form.username.errors %}aria-invalid="true"{% endif %}
|
||||
pattern="^[a-zA-Z0-9_]{2,30}$" />
|
||||
{% for error in form.username.errors %}<small>{{ error }}</small>{% endfor %}
|
||||
</label>
|
||||
<label>
|
||||
以及作为备用登录方式的电子邮件地址(推荐)
|
||||
<input type="email"
|
||||
name="email"
|
||||
{% if request.user.email and not request.user.mastodon_acct %}readonly{% endif %}
|
||||
{% if request.user.email %}value="{{ request.user.email }}" aria-invalid="false"{% endif %}
|
||||
placeholder="设置后请查收邮件点击其中的确认链接"
|
||||
autocomplete="email" />
|
||||
{% if request.user.pending_email %}
|
||||
<small>当前待确认的电子邮件地址为{{ request.user.pending_email }},请查收邮件并点击确认链接;如长时间未收到可重新输入并保存。</small>
|
||||
{% endif %}
|
||||
{% for error in form.email.errors %}<small>{{ error }}</small>{% endfor %}
|
||||
</label>
|
||||
</fieldset>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% trans '确认并保存' %}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{% url 'common:home' %}" method="get">
|
||||
<input type="submit" value="{% trans 'Cut the sh*t and get me in!' %}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
30
users/templates/users/verify_email.html
Normal file
30
users/templates/users/verify_email.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }} - {% trans '验证电子邮件' %}</title>
|
||||
{% include "common_libs.html" with jquery=0 v2=1 %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<article>
|
||||
<header style="text-align: center;">
|
||||
<img src="{% static 'img/logo.svg' %}" class="logo" alt="logo">
|
||||
</header>
|
||||
<h4>验证电子邮件</h4>
|
||||
{% if success %}
|
||||
<p>
|
||||
{{ request.user.email }} 验证成功,<a href="{% url 'common:home' %}">点击这里返回首页</a>。
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
链接无效或已过期,<a href="{% url 'users:login' %}">点击这里重新登录</a>。
|
||||
</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -4,10 +4,15 @@ from .views import *
|
|||
app_name = "users"
|
||||
urlpatterns = [
|
||||
path("login/", login, name="login"),
|
||||
path("register/", register, name="register"),
|
||||
path("connect/", connect, name="connect"),
|
||||
path("reconnect/", reconnect, name="reconnect"),
|
||||
path("data/", data, name="data"),
|
||||
path("login/oauth", OAuth2_login, name="login_oauth"),
|
||||
path("login/email", verify_email, name="login_email"),
|
||||
path("verify_email", verify_email, name="verify_email"),
|
||||
path("register_email", verify_email, name="register_email"),
|
||||
path("register", register, name="register"),
|
||||
path("connect", connect, name="connect"),
|
||||
path("reconnect", reconnect, name="reconnect"),
|
||||
path("data", data, name="data"),
|
||||
path("info", account_info, name="info"),
|
||||
path("data/import/status", data_import_status, name="import_status"),
|
||||
path("data/import/goodreads", import_goodreads, name="import_goodreads"),
|
||||
path("data/import/douban", import_douban, name="import_douban"),
|
||||
|
@ -17,14 +22,13 @@ urlpatterns = [
|
|||
path("data/sync_mastodon", sync_mastodon, name="sync_mastodon"),
|
||||
path("data/reset_visibility", reset_visibility, name="reset_visibility"),
|
||||
path("data/clear_data", clear_data, name="clear_data"),
|
||||
path("preferences/", preferences, name="preferences"),
|
||||
path("logout/", logout, name="logout"),
|
||||
path("layout/", set_layout, name="set_layout"),
|
||||
path("OAuth2_login/", OAuth2_login, name="OAuth2_login"),
|
||||
path("<str:id>/followers/", followers, name="followers"),
|
||||
path("<str:id>/following/", following, name="following"),
|
||||
path("report/", report, name="report"),
|
||||
path("manage_report/", manage_report, name="manage_report"),
|
||||
path("preferences", preferences, name="preferences"),
|
||||
path("logout", logout, name="logout"),
|
||||
path("layout", set_layout, name="set_layout"),
|
||||
path("<str:id>/followers", followers, name="followers"),
|
||||
path("<str:id>/following", following, name="following"),
|
||||
path("report", report, name="report"),
|
||||
path("manage_report", manage_report, name="manage_report"),
|
||||
path(
|
||||
"mark_announcements_read/",
|
||||
mark_announcements_read,
|
||||
|
|
Loading…
Add table
Reference in a new issue