Update emails

This commit is contained in:
cuom1999 2023-08-25 15:36:38 -05:00
parent 164a712902
commit af5bee5147
18 changed files with 481 additions and 170 deletions

View file

@ -65,6 +65,7 @@ from judge.views import (
internal, internal,
resolver, resolver,
course, course,
email,
) )
from judge.views.problem_data import ( from judge.views.problem_data import (
ProblemDataView, ProblemDataView,
@ -104,19 +105,19 @@ register_patterns = [
# confusing 404. # confusing 404.
url( url(
r"^activate/(?P<activation_key>\w+)/$", r"^activate/(?P<activation_key>\w+)/$",
ActivationView.as_view(title="Activation key invalid"), ActivationView.as_view(title=_("Activation key invalid")),
name="registration_activate", name="registration_activate",
), ),
url( url(
r"^register/$", r"^register/$",
RegistrationView.as_view(title="Register"), RegistrationView.as_view(title=_("Register")),
name="registration_register", name="registration_register",
), ),
url( url(
r"^register/complete/$", r"^register/complete/$",
TitledTemplateView.as_view( TitledTemplateView.as_view(
template_name="registration/registration_complete.html", template_name="registration/registration_complete.html",
title="Registration Completed", title=_("Registration Completed"),
), ),
name="registration_complete", name="registration_complete",
), ),
@ -124,7 +125,7 @@ register_patterns = [
r"^register/closed/$", r"^register/closed/$",
TitledTemplateView.as_view( TitledTemplateView.as_view(
template_name="registration/registration_closed.html", template_name="registration/registration_closed.html",
title="Registration not allowed", title=_("Registration not allowed"),
), ),
name="registration_disallowed", name="registration_disallowed",
), ),
@ -183,6 +184,17 @@ register_patterns = [
), ),
name="password_reset_done", name="password_reset_done",
), ),
url(r"^email/change/$", email.email_change_view, name="email_change"),
url(
r"^email/change/verify/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$",
email.verify_email_view,
name="email_change_verify",
),
url(
r"^email/change/pending$",
email.email_change_pending_view,
name="email_change_pending",
),
url(r"^social/error/$", register.social_auth_error, name="social_auth_error"), url(r"^social/error/$", register.social_auth_error, name="social_auth_error"),
url(r"^2fa/$", totp.TOTPLoginView.as_view(), name="login_2fa"), url(r"^2fa/$", totp.TOTPLoginView.as_view(), name="login_2fa"),
url(r"^2fa/enable/$", totp.TOTPEnableView.as_view(), name="enable_2fa"), url(r"^2fa/enable/$", totp.TOTPEnableView.as_view(), name="enable_2fa"),

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.18 on 2023-08-25 00:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("judge", "0162_profile_image"),
]
operations = [
migrations.AddField(
model_name="profile",
name="email_change_pending",
field=models.EmailField(blank=True, max_length=254, null=True),
),
]

View file

@ -237,6 +237,7 @@ class Profile(models.Model):
help_text=_("Notes for administrators regarding this user."), help_text=_("Notes for administrators regarding this user."),
) )
profile_image = models.ImageField(upload_to=profile_image_path, null=True) profile_image = models.ImageField(upload_to=profile_image_path, null=True)
email_change_pending = models.EmailField(blank=True, null=True)
@cached_property @cached_property
def organization(self): def organization(self):

View file

@ -0,0 +1,20 @@
from django.template.loader import render_to_string
from django.contrib.sites.shortcuts import get_current_site
from django.conf import settings
def render_email_message(request, contexts):
current_site = get_current_site(request)
email_contexts = {
"username": request.user.username,
"domain": current_site.domain,
"protocol": "https" if request.is_secure() else "http",
"site_name": settings.SITE_NAME,
"message": None,
"title": None,
"button_text": "Click here",
"url_path": None,
}
email_contexts.update(contexts)
message = render_to_string("general_email.html", email_contexts)
return message

104
judge/views/email.py Normal file
View file

@ -0,0 +1,104 @@
from django.contrib.auth.tokens import default_token_generator
from django.core.mail import send_mail
from django.shortcuts import render, redirect
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_text
from django.conf import settings
from django import forms
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from urllib.parse import urlencode, urlunparse, urlparse
from judge.models import Profile
from judge.utils.email_render import render_email_message
class EmailChangeForm(forms.Form):
new_email = forms.EmailField(label=_("New Email"))
def clean_new_email(self):
new_email = self.cleaned_data.get("new_email")
if User.objects.filter(email=new_email).exists():
raise forms.ValidationError(_("An account with this email already exists."))
return new_email
@login_required
def email_change_view(request):
form = EmailChangeForm(request.POST or None)
if request.method == "POST" and form.is_valid():
new_email = request.POST.get("new_email")
user = request.user
profile = request.profile
# Generate a token for email verification
token = default_token_generator.make_token(user)
uid = urlsafe_base64_encode(force_bytes(user.pk))
# Send the email to the user
subject = _(f"{settings.SITE_NAME} - Email Change Request")
email_contexts = {
"message": _(
"We have received a request to change your email to this email. Click the button below to change your email:"
),
"title": _("Email Change"),
"button_text": _("Change Email"),
"url_path": reverse(
"email_change_verify", kwargs={"uidb64": uid, "token": token}
),
}
message = render_email_message(request, email_contexts)
send_mail(subject, message, settings.EMAIL_HOST_USER, [new_email])
profile.email_change_pending = new_email
profile.save()
return redirect("email_change_pending")
return render(
request,
"email_change/email_change.html",
{
"form": form,
"title": _("Change email"),
},
)
def verify_email_view(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and default_token_generator.check_token(user, token):
# Update the user's email address
profile = Profile.objects.get(user=user)
new_email = profile.email_change_pending
if new_email:
user.email = new_email
profile.email_change_pending = None
user.save()
profile.save()
return render(
request,
"email_change/email_change_success.html",
{"title": _("Success"), "user": user},
)
return render(
request, "email_change/email_change_failure.html", {"title": _("Invalid")}
)
def email_change_pending_view(request):
return render(
request,
"email_change/email_change_pending.html",
{
"title": _("Email change pending"),
},
)

View file

@ -15,7 +15,7 @@ from registration.backends.default.views import (
from registration.forms import RegistrationForm from registration.forms import RegistrationForm
from sortedm2m.forms import SortedMultipleChoiceField from sortedm2m.forms import SortedMultipleChoiceField
from judge.models import Language, Organization, Profile, TIMEZONE from judge.models import Language, Profile, TIMEZONE
from judge.utils.recaptcha import ReCaptchaField, ReCaptchaWidget from judge.utils.recaptcha import ReCaptchaField, ReCaptchaWidget
from judge.widgets import Select2MultipleWidget, Select2Widget from judge.widgets import Select2MultipleWidget, Select2Widget
@ -43,29 +43,10 @@ class CustomRegistrationForm(RegistrationForm):
empty_label=None, empty_label=None,
widget=Select2Widget(attrs={"style": "width:100%"}), widget=Select2Widget(attrs={"style": "width:100%"}),
) )
organizations = SortedMultipleChoiceField(
queryset=Organization.objects.filter(is_open=True),
label=_("Groups"),
required=False,
widget=Select2MultipleWidget(attrs={"style": "width:100%"}),
)
if ReCaptchaField is not None: if ReCaptchaField is not None:
captcha = ReCaptchaField(widget=ReCaptchaWidget()) captcha = ReCaptchaField(widget=ReCaptchaWidget())
def clean_organizations(self):
organizations = self.cleaned_data.get("organizations") or []
max_orgs = settings.DMOJ_USER_MAX_ORGANIZATION_COUNT
if sum(org.is_open for org in organizations) > max_orgs:
raise forms.ValidationError(
_("You may not be part of more than {count} public groups.").format(
count=max_orgs
)
)
return self.cleaned_data["organizations"]
def clean_email(self): def clean_email(self):
if User.objects.filter(email=self.cleaned_data["email"]).exists(): if User.objects.filter(email=self.cleaned_data["email"]).exists():
raise forms.ValidationError( raise forms.ValidationError(
@ -116,7 +97,6 @@ class RegistrationView(OldRegistrationView):
cleaned_data = form.cleaned_data cleaned_data = form.cleaned_data
profile.timezone = cleaned_data["timezone"] profile.timezone = cleaned_data["timezone"]
profile.language = cleaned_data["language"] profile.language = cleaned_data["language"]
profile.organizations.add(*cleaned_data["organizations"])
profile.save() profile.save()
return user return user

View file

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: lqdoj2\n" "Project-Id-Version: lqdoj2\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-24 23:09+0700\n" "POT-Creation-Date: 2023-08-26 03:32+0700\n"
"PO-Revision-Date: 2021-07-20 03:44\n" "PO-Revision-Date: 2021-07-20 03:44\n"
"Last-Translator: Icyene\n" "Last-Translator: Icyene\n"
"Language-Team: Vietnamese\n" "Language-Team: Vietnamese\n"
@ -21,7 +21,7 @@ msgstr ""
#: chat_box/models.py:31 chat_box/models.py:54 chat_box/models.py:68 #: chat_box/models.py:31 chat_box/models.py:54 chat_box/models.py:68
#: judge/admin/interface.py:150 judge/models/contest.py:636 #: judge/admin/interface.py:150 judge/models/contest.py:636
#: judge/models/contest.py:845 judge/models/course.py:115 #: judge/models/contest.py:845 judge/models/course.py:115
#: judge/models/profile.py:374 judge/models/profile.py:452 #: judge/models/profile.py:375 judge/models/profile.py:453
msgid "user" msgid "user"
msgstr "người dùng" msgstr "người dùng"
@ -49,11 +49,27 @@ msgstr "Tiếng Việt"
msgid "English" msgid "English"
msgstr "" msgstr ""
#: dmoj/urls.py:135 #: dmoj/urls.py:108
msgid "Activation key invalid"
msgstr "Mã kích hoạt không hợp lệ"
#: dmoj/urls.py:113
msgid "Register"
msgstr "Đăng ký"
#: dmoj/urls.py:120
msgid "Registration Completed"
msgstr "Đăng ký hoàn thành"
#: dmoj/urls.py:128
msgid "Registration not allowed"
msgstr "Đăng ký không thành công"
#: dmoj/urls.py:136
msgid "Login" msgid "Login"
msgstr "Đăng nhập" msgstr "Đăng nhập"
#: dmoj/urls.py:212 templates/base.html:209 #: dmoj/urls.py:216 templates/base.html:209
#: templates/organization/org-left-sidebar.html:2 #: templates/organization/org-left-sidebar.html:2
msgid "Home" msgid "Home"
msgstr "Trang chủ" msgstr "Trang chủ"
@ -293,13 +309,13 @@ msgid "User"
msgstr "Thành viên" msgstr "Thành viên"
#: judge/admin/profile.py:132 templates/registration/registration_form.html:40 #: judge/admin/profile.py:132 templates/registration/registration_form.html:40
#: templates/user/import/table_csv.html:8 #: templates/user/edit-profile.html:116 templates/user/import/table_csv.html:8
msgid "Email" msgid "Email"
msgstr "Email" msgstr "Email"
#: judge/admin/profile.py:138 judge/views/register.py:36 #: judge/admin/profile.py:138 judge/views/register.py:36
#: templates/registration/registration_form.html:68 #: templates/registration/registration_form.html:68
#: templates/user/edit-profile.html:119 #: templates/user/edit-profile.html:140
msgid "Timezone" msgid "Timezone"
msgstr "Múi giờ" msgstr "Múi giờ"
@ -497,7 +513,7 @@ msgstr "Tên đăng nhập"
#: judge/forms.py:428 templates/registration/registration_form.html:46 #: judge/forms.py:428 templates/registration/registration_form.html:46
#: templates/registration/registration_form.html:60 #: templates/registration/registration_form.html:60
#: templates/user/import/table_csv.html:5 #: templates/user/edit-profile.html:108 templates/user/import/table_csv.html:5
msgid "Password" msgid "Password"
msgstr "Mật khẩu" msgstr "Mật khẩu"
@ -1972,7 +1988,7 @@ msgid ""
msgstr "Ảnh này sẽ thay thế logo mặc định khi ở trong tổ chức." msgstr "Ảnh này sẽ thay thế logo mặc định khi ở trong tổ chức."
#: judge/models/profile.py:136 judge/models/profile.py:165 #: judge/models/profile.py:136 judge/models/profile.py:165
#: judge/models/profile.py:380 judge/models/profile.py:459 #: judge/models/profile.py:381 judge/models/profile.py:460
msgid "organization" msgid "organization"
msgstr "" msgstr ""
@ -2078,35 +2094,35 @@ msgstr "ghi chú nội bộ"
msgid "Notes for administrators regarding this user." msgid "Notes for administrators regarding this user."
msgstr "Ghi chú riêng cho quản trị viên." msgstr "Ghi chú riêng cho quản trị viên."
#: judge/models/profile.py:367 #: judge/models/profile.py:368
msgid "user profile" msgid "user profile"
msgstr "thông tin người dùng" msgstr "thông tin người dùng"
#: judge/models/profile.py:368 #: judge/models/profile.py:369
msgid "user profiles" msgid "user profiles"
msgstr "thông tin người dùng" msgstr "thông tin người dùng"
#: judge/models/profile.py:384 #: judge/models/profile.py:385
msgid "request time" msgid "request time"
msgstr "thời gian đăng ký" msgstr "thời gian đăng ký"
#: judge/models/profile.py:387 #: judge/models/profile.py:388
msgid "state" msgid "state"
msgstr "trạng thái" msgstr "trạng thái"
#: judge/models/profile.py:394 #: judge/models/profile.py:395
msgid "reason" msgid "reason"
msgstr "lý do" msgstr "lý do"
#: judge/models/profile.py:397 #: judge/models/profile.py:398
msgid "organization join request" msgid "organization join request"
msgstr "đơn đăng ký tham gia" msgstr "đơn đăng ký tham gia"
#: judge/models/profile.py:398 #: judge/models/profile.py:399
msgid "organization join requests" msgid "organization join requests"
msgstr "đơn đăng ký tham gia" msgstr "đơn đăng ký tham gia"
#: judge/models/profile.py:464 #: judge/models/profile.py:465
#, fuzzy #, fuzzy
#| msgid "last seen" #| msgid "last seen"
msgid "last visit" msgid "last visit"
@ -2839,6 +2855,55 @@ msgstr "Mô tả vấn đề"
msgid "New clarification for %s" msgid "New clarification for %s"
msgstr "Thông báo mới cho %s" msgstr "Thông báo mới cho %s"
#: judge/views/email.py:19
#, fuzzy
#| msgid "Email"
msgid "New Email"
msgstr "Email"
#: judge/views/email.py:23
#, fuzzy
#| msgid "Contest with key already exists."
msgid "An account with this email already exists."
msgstr "Mã kỳ thi đã tồn tại."
#: judge/views/email.py:41
#, python-brace-format
msgid "{settings.SITE_NAME} - Email Change Request"
msgstr ""
#: judge/views/email.py:43
msgid ""
"We have received a request to change your email to this email. Click the "
"button below to change your email:"
msgstr ""
"Chúng tôi đã nhận được yêu cầu thay đổi địa chỉ email của bạn thành địa chỉ "
"email này. Vui lòng nhấp vào nút bên dưới để thay đổi địa chỉ email của bạn:"
#: judge/views/email.py:44
msgid "Email Change"
msgstr "Thay đổi Email"
#: judge/views/email.py:45
msgid "Change Email"
msgstr "Thay đổi Email"
#: judge/views/email.py:56 templates/user/edit-profile.html:120
msgid "Change email"
msgstr "Thay đổi email"
#: judge/views/email.py:76
msgid "Success"
msgstr "Thành công"
#: judge/views/email.py:78
msgid "Invalid"
msgstr "Không hợp lệ"
#: judge/views/email.py:83
msgid "Email change pending"
msgstr "Yêu cầu thay đổi email đang đợi xác thực."
#: judge/views/error.py:17 #: judge/views/error.py:17
msgid "404 error" msgid "404 error"
msgstr "Lỗi 404" msgstr "Lỗi 404"
@ -2902,19 +2967,17 @@ msgid "You are not allowed to edit this organization."
msgstr "Bạn không được phép chỉnh sửa tổ chức này." msgstr "Bạn không được phép chỉnh sửa tổ chức này."
#: judge/views/organization.py:193 judge/views/organization.py:337 #: judge/views/organization.py:193 judge/views/organization.py:337
#, fuzzy
#| msgid "Can't edit organization"
msgid "Can't access organization" msgid "Can't access organization"
msgstr "Không thể chỉnh sửa tổ chức" msgstr "Không thể truy cập nhóm"
#: judge/views/organization.py:194 judge/views/organization.py:338 #: judge/views/organization.py:194 judge/views/organization.py:338
msgid "You are not allowed to access this organization." msgid "You are not allowed to access this organization."
msgstr "Bạn không được phép chỉnh sửa tổ chức này." msgstr "Bạn không được phép chỉnh sửa tổ chức này."
#: judge/views/organization.py:230 judge/views/register.py:48 #: judge/views/organization.py:230 judge/views/stats.py:184
#: judge/views/stats.py:184 templates/contest/list.html:89 #: templates/contest/list.html:89 templates/problem/list-base.html:97
#: templates/problem/list-base.html:97 templates/stats/site.html:33 #: templates/stats/site.html:33 templates/user/user-left-sidebar.html:4
#: templates/user/user-left-sidebar.html:4 templates/user/user-list-tabs.html:6 #: templates/user/user-list-tabs.html:6
msgid "Groups" msgid "Groups"
msgstr "Nhóm" msgstr "Nhóm"
@ -2941,7 +3004,7 @@ msgstr "Bạn đã ở trong nhóm."
msgid "This group is not open." msgid "This group is not open."
msgstr "Nhóm này là nhóm kín." msgstr "Nhóm này là nhóm kín."
#: judge/views/organization.py:510 judge/views/register.py:62 #: judge/views/organization.py:510
#, python-brace-format #, python-brace-format
msgid "You may not be part of more than {count} public groups." msgid "You may not be part of more than {count} public groups."
msgstr "Bạn không thể tham gia nhiều hơn {count} nhóm công khai." msgstr "Bạn không thể tham gia nhiều hơn {count} nhóm công khai."
@ -3205,11 +3268,11 @@ msgstr "Các bài nộp tốt nhất cho <a href=\"{1}\">{0}</a>"
msgid "A username must contain letters, numbers, or underscores" msgid "A username must contain letters, numbers, or underscores"
msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới"
#: judge/views/register.py:42 templates/user/edit-profile.html:123 #: judge/views/register.py:42 templates/user/edit-profile.html:144
msgid "Preferred language" msgid "Preferred language"
msgstr "Ngôn ngữ ưa thích" msgstr "Ngôn ngữ ưa thích"
#: judge/views/register.py:73 #: judge/views/register.py:54
#, python-format #, python-format
msgid "" msgid ""
"The email address \"%s\" is already taken. Only one registration is allowed " "The email address \"%s\" is already taken. Only one registration is allowed "
@ -3217,7 +3280,7 @@ msgid ""
msgstr "" msgstr ""
"Email \"%s\" đã được sử dụng. Mỗi email chỉ có thể đăng ký một tài khoản." "Email \"%s\" đã được sử dụng. Mỗi email chỉ có thể đăng ký một tài khoản."
#: judge/views/register.py:85 #: judge/views/register.py:66
msgid "" msgid ""
"Your email provider is not allowed due to history of abuse. Please use a " "Your email provider is not allowed due to history of abuse. Please use a "
"reputable email provider." "reputable email provider."
@ -3225,11 +3288,11 @@ msgstr ""
"Your email provider is not allowed due to history of abuse. Please use a " "Your email provider is not allowed due to history of abuse. Please use a "
"reputable email provider." "reputable email provider."
#: judge/views/register.py:93 judge/views/register.py:131 #: judge/views/register.py:74 judge/views/register.py:111
msgid "Registration" msgid "Registration"
msgstr "Đăng ký" msgstr "Đăng ký"
#: judge/views/register.py:145 #: judge/views/register.py:125
msgid "Authentication failure" msgid "Authentication failure"
msgstr "Xác thực thất bại" msgstr "Xác thực thất bại"
@ -4185,11 +4248,11 @@ msgstr "Khôi phục kết quả"
msgid "Disqualify" msgid "Disqualify"
msgstr "Hủy kết quả" msgstr "Hủy kết quả"
#: templates/contest/ranking-table.html:54 templates/user/edit-profile.html:96 #: templates/contest/ranking-table.html:54 templates/user/edit-profile.html:100
msgid "Fullname" msgid "Fullname"
msgstr "Tên đầy đủ" msgstr "Tên đầy đủ"
#: templates/contest/ranking-table.html:55 templates/user/edit-profile.html:100 #: templates/contest/ranking-table.html:55 templates/user/edit-profile.html:104
#: templates/user/import/table_csv.html:7 #: templates/user/import/table_csv.html:7
msgid "School" msgid "School"
msgstr "Trường" msgstr "Trường"
@ -4262,6 +4325,26 @@ msgstr "Còn"
msgid "Upcoming contests" msgid "Upcoming contests"
msgstr "Kỳ thi sắp diễn ra" msgstr "Kỳ thi sắp diễn ra"
#: templates/email_change/email_change.html:19
msgid "Verify Email"
msgstr "Xác thực Email"
#: templates/email_change/email_change_failure.html:3
msgid "Invalid reset link."
msgstr "Đường dẫn không hợp lệ"
#: templates/email_change/email_change_pending.html:4
msgid "An email was sent to"
msgstr "Email đã được gửi đến"
#: templates/email_change/email_change_pending.html:4
msgid "If you don't see it, kindly check your spam folder as well."
msgstr "Nếu bạn không tìm thấy nó, vui lòng kiểm tra thư mục spam của bạn."
#: templates/email_change/email_change_success.html:3
msgid "Your email was sucessfully changed to"
msgstr "Bạn đã đổi email thành công."
#: templates/feed/has_next.html:3 #: templates/feed/has_next.html:3
msgid "View more" msgid "View more"
msgstr "Xem thêm" msgstr "Xem thêm"
@ -4290,6 +4373,10 @@ msgstr "Dừng"
msgid "Continue" msgid "Continue"
msgstr "Tiếp tục" msgstr "Tiếp tục"
#: templates/general_email.html:15
msgid "Dear"
msgstr "Xin chào"
#: templates/internal/left-sidebar.html:3 #: templates/internal/left-sidebar.html:3
msgid "Average speed" msgid "Average speed"
msgstr "Tốc độ trung bình" msgstr "Tốc độ trung bình"
@ -4505,6 +4592,10 @@ msgstr "Từ chối"
msgid "Kick" msgid "Kick"
msgstr "Đuổi" msgstr "Đuổi"
#: templates/pagedown.html:9
msgid "Update Preview"
msgstr "Cập nhật xem trước"
#: templates/problem/clone.html:37 #: templates/problem/clone.html:37
msgid "Enter a new code for the cloned problem:" msgid "Enter a new code for the cloned problem:"
msgstr "Nhập mã bài mới cho bài tập được nhân bản:" msgstr "Nhập mã bài mới cho bài tập được nhân bản:"
@ -4994,6 +5085,37 @@ msgstr "%(key)s không phải mã xác thực hợp lệ."
msgid "Your account has been successfully activated." msgid "Your account has been successfully activated."
msgstr "Tài khoản được kích hoạt thành công." msgstr "Tài khoản được kích hoạt thành công."
#: templates/registration/activation_email.html:2
msgid "Account activation"
msgstr "Kích hoạt tài khoản"
#: templates/registration/activation_email.html:3
#, python-format
msgid ""
"Thanks for registering! We're glad to have you. The last step is activating "
"your account. Please activate your account in the next %(expiration_days)d "
"days."
msgstr ""
"Cảm ơn bạn đã đăng ký! Chúng tôi rất vui được chào đón bạn. Bước cuối cùng "
"là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản trong vòng "
"%(expiration_days)d ngày."
#: templates/registration/activation_email.html:5
msgid "Activate"
msgstr "Kích hoạt"
#: templates/registration/activation_email.html:10
msgid ""
"Alternatively, you can reply to this message to activate your account. Your "
"reply must keep the following text intact for this to work:"
msgstr ""
"Hoặc bạn có thể trả lời tin nhắn này để kích hoạt tài khoản của bạn. Email "
"trả lời của bạn phải giữ nguyên đoạn văn sau đây:"
#: templates/registration/activation_email.html:16
msgid "See you soon!"
msgstr "Hẹn sớm gặp lại bạn!"
#: templates/registration/login.html:9 #: templates/registration/login.html:9
msgid "Invalid username or password." msgid "Invalid username or password."
msgstr "Tên đăng nhập hoặc mật khẩu không hợp lệ." msgstr "Tên đăng nhập hoặc mật khẩu không hợp lệ."
@ -5032,6 +5154,7 @@ msgid "Your password has been set. You may go ahead and log in now"
msgstr "Mật khẩu đã được cập nhật. Hãy thử đăng nhập lại" msgstr "Mật khẩu đã được cập nhật. Hãy thử đăng nhập lại"
#: templates/registration/password_reset_confirm.html:9 #: templates/registration/password_reset_confirm.html:9
#: templates/registration/password_reset_email.html:5
msgid "Reset Password" msgid "Reset Password"
msgstr "Reset mật khẩu" msgstr "Reset mật khẩu"
@ -5039,7 +5162,7 @@ msgstr "Reset mật khẩu"
msgid "" msgid ""
"We've emailed you instructions for setting your password. You should be " "We've emailed you instructions for setting your password. You should be "
"receiving them shortly." "receiving them shortly."
msgstr "Kiểm tra email để xem hướng dẫn đặt mật khẩu." msgstr "Chúng tôi đã gửi email cho bạn để đặt lại mật khẩu."
#: templates/registration/password_reset_done.html:5 #: templates/registration/password_reset_done.html:5
msgid "" msgid ""
@ -5047,13 +5170,25 @@ msgid ""
"you registered with, and check your spam folder." "you registered with, and check your spam folder."
msgstr "Nếu bạn không nhận được email, hãy kiểm tra hộp thư rác (spam)." msgstr "Nếu bạn không nhận được email, hãy kiểm tra hộp thư rác (spam)."
#: templates/registration/password_reset_email.html:2
msgid "Password Reset"
msgstr "Đặt lại mật khẩu"
#: templates/registration/password_reset_email.html:3
msgid ""
"We have received a request to reset your password. Click the button below to "
"reset your password:"
msgstr ""
"Chúng tôi đã nhận được yêu cầu đặt lại mật khẩu của bạn. Nhấn vào nút bên "
"dưới để đặt lại mật khẩu của bạn:"
#: templates/registration/password_reset_email.txt:1 #: templates/registration/password_reset_email.txt:1
#, python-format #, python-format
msgid "" msgid ""
"You're receiving this email because you requested a password reset for your " "You're receiving this email because you requested a password reset for your "
"user account at %(site_name)s." "user account at %(site_name)s."
msgstr "" msgstr ""
"Bạn nhận được email này vì bạn đã yêu cầu reset mật khẩu tại %(site_name)s." "Bạn nhận được email này vì bạn đã yêu cầu đặt lại mật khẩu tại %(site_name)s."
#: templates/registration/password_reset_email.txt:3 #: templates/registration/password_reset_email.txt:3
msgid "Please go to the following page and choose a new password:" msgid "Please go to the following page and choose a new password:"
@ -5075,7 +5210,7 @@ msgstr "%(site_name)s team"
#: templates/registration/password_reset_subject.txt:1 #: templates/registration/password_reset_subject.txt:1
#, python-format #, python-format
msgid "Password reset on %(site_name)s" msgid "Password reset on %(site_name)s"
msgstr "Reset mật khẩu trên %(site_name)s" msgstr "Đặt lại mật khẩu trên %(site_name)s"
#: templates/registration/profile_creation.html:36 #: templates/registration/profile_creation.html:36
#: templates/registration/username_select.html:7 #: templates/registration/username_select.html:7
@ -5089,8 +5224,9 @@ msgstr "Đăng ký hiện tại đã bị dừng. Hãy liên hệ admin."
#: templates/registration/registration_complete.html:3 #: templates/registration/registration_complete.html:3
msgid "" msgid ""
"You have successfully been registered. An email has been sent to the email " "You have successfully been registered. An email has been sent to the email "
"address you provided to confirm your registration." "address you provided to confirm your registration. If you don't see it, "
msgstr "Bạn đã đăng ký thành công. Kiểm tra email để hoàn thành việc xác thực." "kindly check your spam folder as well."
msgstr "Bạn đã đăng ký thành công. Kiểm tra email để hoàn thành việc xác thực. Nếu bạn không tìm thấy nó, vui lòng kiểm tra thư mục spam của bạn."
#: templates/registration/registration_form.html:61 #: templates/registration/registration_form.html:61
msgid "(again, for confirmation)" msgid "(again, for confirmation)"
@ -5106,21 +5242,9 @@ msgstr "chọn từ bản đồ"
#: templates/registration/registration_form.html:78 #: templates/registration/registration_form.html:78
msgid "Default language" msgid "Default language"
msgstr "Ngôn ngữ ưa thích" msgstr "Ngôn ngữ mặc định"
#: templates/registration/registration_form.html:81 #: templates/registration/registration_form.html:89
msgid "Affiliated organizations"
msgstr "Tổ chức bạn muốn tham gia"
#: templates/registration/registration_form.html:97
msgid "By registering, you agree to our"
msgstr "Bạn đồng ý với"
#: templates/registration/registration_form.html:98
msgid "Terms & Conditions"
msgstr "Điều khoản của chúng tôi"
#: templates/registration/registration_form.html:101
msgid "Register!" msgid "Register!"
msgstr "Đăng ký!" msgstr "Đăng ký!"
@ -5128,7 +5252,7 @@ msgstr "Đăng ký!"
#: templates/registration/totp_disable.html:45 #: templates/registration/totp_disable.html:45
#: templates/registration/totp_enable.html:83 #: templates/registration/totp_enable.html:83
msgid "Enter the 6-digit code generated by your app:" msgid "Enter the 6-digit code generated by your app:"
msgstr "" msgstr "Nhập mã xác thực gồm 6 chữ số từ app bạn chọn"
#: templates/registration/totp_auth.html:41 #: templates/registration/totp_auth.html:41
#, python-format #, python-format
@ -5516,43 +5640,51 @@ msgstr "Top Score"
msgid "Rank" msgid "Rank"
msgstr "Rank" msgstr "Rank"
#: templates/user/edit-profile.html:104 #: templates/user/edit-profile.html:111
msgid "Avatar"
msgstr "Ảnh đại diện"
#: templates/user/edit-profile.html:110
msgid "Self-description"
msgstr "Tự giới thiệu"
#: templates/user/edit-profile.html:118
msgid "Select your closest major city"
msgstr "Chọn thành phố gần nhất"
#: templates/user/edit-profile.html:127
msgid "Editor theme"
msgstr "Giao diện cho code editor"
#: templates/user/edit-profile.html:132
msgid "Math engine"
msgstr ""
#: templates/user/edit-profile.html:139
msgid "Change your password" msgid "Change your password"
msgstr "Đổi mật khẩu" msgstr "Đổi mật khẩu"
#: templates/user/edit-profile.html:150 #: templates/user/edit-profile.html:125
msgid "Avatar"
msgstr "Ảnh đại diện"
#: templates/user/edit-profile.html:131
msgid "Self-description"
msgstr "Tự giới thiệu"
#: templates/user/edit-profile.html:139
msgid "Select your closest major city"
msgstr "Chọn thành phố gần nhất"
#: templates/user/edit-profile.html:148
msgid "Editor theme"
msgstr "Giao diện cho code editor"
#: templates/user/edit-profile.html:153
msgid "Math engine"
msgstr ""
#: templates/user/edit-profile.html:164
msgid "Two Factor Authentication is enabled." msgid "Two Factor Authentication is enabled."
msgstr "Two Factor Authentication đã được kích hoạt." msgstr "Two Factor Authentication đã được kích hoạt."
#: templates/user/edit-profile.html:157 #: templates/user/edit-profile.html:168
msgid "Two Factor Authentication is disabled." msgid "Disable"
msgstr "Two Factor Authentication đã được hủy kích hoạt." msgstr "Tắt"
#: templates/user/edit-profile.html:162 #: templates/user/edit-profile.html:171
msgid "Two Factor Authentication is disabled."
msgstr "Two Factor Authentication chưa kích hoạt."
#: templates/user/edit-profile.html:172
msgid "Enable"
msgstr "Bật"
#: templates/user/edit-profile.html:176
msgid "User-script" msgid "User-script"
msgstr "" msgstr ""
#: templates/user/edit-profile.html:166 #: templates/user/edit-profile.html:180
msgid "Update profile" msgid "Update profile"
msgstr "Cập nhật thông tin" msgstr "Cập nhật thông tin"
@ -5768,6 +5900,15 @@ msgstr "Thông tin"
msgid "Check all" msgid "Check all"
msgstr "Chọn tất cả" msgstr "Chọn tất cả"
#~ msgid "Affiliated organizations"
#~ msgstr "Tổ chức bạn muốn tham gia"
#~ msgid "By registering, you agree to our"
#~ msgstr "Bạn đồng ý với"
#~ msgid "Terms & Conditions"
#~ msgstr "Điều khoản của chúng tôi"
#~ msgid "Change your avatar" #~ msgid "Change your avatar"
#~ msgstr "Đổi ảnh đại diện" #~ msgstr "Đổi ảnh đại diện"
@ -5875,9 +6016,6 @@ msgstr "Chọn tất cả"
#~ msgid "Color theme" #~ msgid "Color theme"
#~ msgstr "Chủ đề màu sắc" #~ msgstr "Chủ đề màu sắc"
#~ msgid "Change color theme"
#~ msgstr "Đổi chủ đề màu sắc"
#~ msgid "commented on {time}" #~ msgid "commented on {time}"
#~ msgstr "bình luận vào {time}" #~ msgstr "bình luận vào {time}"

View file

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block media %}
<style type="text/css">
.errorlist {
margin: 0;
padding: 0;
list-style: none;
color: red;
font-size: 0.8em;
}
</style>
{% endblock %}
{% block body %}
<form action="" method="post" class="form-area">{% csrf_token %}
<table border="0">{{ form.as_table() }}</table>
<hr>
<button style="float:right;" type="submit">{{ _('Verify Email') }}</button>
</form>
{% endblock %}

View file

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block body %}
<p>{{ _('Invalid reset link.') }}</p>
{% endblock %}

View file

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block body %}
{% if request.profile.email_change_pending %}
<p>{{ _('An email was sent to') }} {{request.profile.email_change_pending}}. {{_('If you don\'t see it, kindly check your spam folder as well.')}}</p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block body %}
<p>{{ _('Your email was sucessfully changed to') }} {{user.email}}</p>
{% endblock %}

View file

@ -0,0 +1,21 @@
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f7f7f7;
}
</style>
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #ffffff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{{ protocol }}://{{ domain }}{{static('icons/logo.png')}}" alt="{{site_name}}" style="max-width: 150px;">
</div>
<div style="margin-bottom: 20px; text-align: center;">
<h2>{{title}}</h2>
<p>{{_('Dear')}} {{username}},</p>
<p>{{message}}</p>
</div>
<div style="text-align: center;">
<a href="{{ protocol }}://{{ domain }}{{ url_path }}" style="display: inline-block; padding: 10px 20px; background-color: #007bff; color: #ffffff; text-decoration: none; border-radius: 4px;">{{button_text}}</a>
</div>
</div>

View file

@ -6,7 +6,7 @@
{% if show_preview %} {% if show_preview %}
<div id="{{ postfix|safe }}-preview" data-preview-url="{{ preview_url }}" data-textarea-id="wmd-input-{{ postfix }}" <div id="{{ postfix|safe }}-preview" data-preview-url="{{ preview_url }}" data-textarea-id="wmd-input-{{ postfix }}"
data-timeout="{{ preview_timeout or '' }}" class="wmd-panel wmd-preview dmmd-preview {{ extra_classes }}"> data-timeout="{{ preview_timeout or '' }}" class="wmd-panel wmd-preview dmmd-preview {{ extra_classes }}">
<div class="dmmd-preview-update"><i class="fa fa-refresh"></i> Update Preview</div> <div class="dmmd-preview-update"><i class="fa fa-refresh"></i> {{_('Update Preview')}}</div>
<div class="dmmd-preview-content content-description"></div> <div class="dmmd-preview-content content-description"></div>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,23 +1,16 @@
<b>Thanks for registering on the {{ site.name }}! We're glad to have you.</b> {% set url_path = "/accounts/activate/" + activation_key %}
<br><br> {% set title = _("Account activation") %}
The last step is activating your account. Please activate your {{ SITE_NAME }} account in the next {{ expiration_days }} days. {% set message = _("Thanks for registering! We're glad to have you. The last step is activating your account. Please activate your account in the next %(expiration_days)d days.", expiration_days=expiration_days) %}
<br><br> {% set username = user.get_username() %}
Please click on the following link to activate your account: {% set button_text = _("Activate") %}
<p style="margin-left:1em"> {% set domain = site.domain %}
<a href="http://{{ site.domain }}/accounts/activate/{{ activation_key }}/">http://{{ site.domain }}/accounts/activate/{{ activation_key }}</a> {% set protocol = "http" %}
</p> {% include "general_email.html" %}
<br>
Alternatively, you can reply to this message to activate your account. {{_("Alternatively, you can reply to this message to activate your account. Your reply must keep the following text intact for this to work:")}}
Your reply must keep the following text intact for this to work:
<pre style="margin-left:1em"> <pre style="margin-left:1em">
{{ activation_key }} {{ activation_key }}
</pre> </pre>
{% if SITE_ADMIN_EMAIL %} {{_("See you soon!")}}
See you soon!
<br>
If you have problems activating your account, feel free to send us an email at <a href="mailto:{{ SITE_ADMIN_EMAIL }}">{{ SITE_ADMIN_EMAIL }}</a>.
{% else %}
See you soon!
{% endif %}

View file

@ -1,19 +1,6 @@
<span style="display:block;margin: 0 2px;padding: 1em;border: 3px solid #FFDE05;background-color: #000;border-radius: 6px;font-size: .95em;color: #444;margin-bottom:0.75em"> {% set url_path = url('password_reset_confirm', uidb64=uid, token=token) %}
<div style="display:table;margin-left:2em"><img src="https://avatars1.githubusercontent.com/u/6934864?v=3&s=101" style="display:inline;vertical-align: middle"> {% set title = _("Password Reset") %}
<h1 style="font-size:4em;display:inline;vertical-align: middle"><a href="//{{ domain }}" style="text-decoration:none;color:gray"><span style="color: #FFDE05">LQD</span>OJ</a> {% set message = _("We have received a request to reset your password. Click the button below to reset your password:") %}
</h1> {% set username = user.get_username() %}
</div> {% set button_text = _("Reset Password") %}
</span> {% include "general_email.html" %}
<div style="display:block;margin: 0 2px;padding: 1em;border: 3px solid #2980B9;background-color: #f8f8f8;border-radius: 6px;font-size: .95em;color: #444;">
<b>Forgot your password on the {{ site_name }}? Don't worry!</b><br><br>
To reset the password for your account "{{ user.get_username() }}", click the below button.
<p align="center">
<a href="{{ protocol }}://{{ domain }}{{ url('password_reset_confirm', uidb64=uid, token=token) }}" style="cursor: pointer;display:block;text-align: center;padding: 4px 2px 5px;color: white;border: 1px solid #666;border-radius: 1px;background: #2980b9;background: linear-gradient(180deg, #00aee0, #2980b9);text-decoration: none;line-height:2em;font-size:1emm;width:12em;">Reset password</a>
</p>
{% if SITE_ADMIN_EMAIL %}
See you soon! If you have problems resetting your email, feel free to shoot us an email at <a href="mailto:{{ SITE_ADMIN_EMAIL }}">{{ SITE_ADMIN_EMAIL }}</a>
{% else %}
See you soon!
{% endif %}
</div>

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<p>{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration.') }}</p> <p>{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration. If you don\'t see it, kindly check your spam folder as well.') }}</p>
{% endblock %} {% endblock %}

View file

@ -78,12 +78,6 @@
<div class="block-header">{{ _('Default language') }}</div> <div class="block-header">{{ _('Default language') }}</div>
<span class="fullwidth">{{ form.language }}</span> <span class="fullwidth">{{ form.language }}</span>
<div class="block-header">{{ _('Affiliated organizations') }}</div>
{{ form.organizations }}
{% if form.organizations.errors %}
<div class="form-field-error">{{ form.organizations.errors }}</div>
{% endif %}
{% if form.captcha %} {% if form.captcha %}
<div style="margin-top: 0.5em">{{ form.captcha }}</div> <div style="margin-top: 0.5em">{{ form.captcha }}</div>
{% if form.captcha.errors %} {% if form.captcha.errors %}
@ -92,12 +86,6 @@
{% endif %} {% endif %}
<hr> <hr>
{% if tos_url %}
<span class="tos-section">
{{ _('By registering, you agree to our') }}
<a href="{{ tos_url }}">{{ _('Terms & Conditions') }}</a>.
</span>
{% endif %}
<button style="float:right;" type="submit">{{ _('Register!') }}</button> <button style="float:right;" type="submit">{{ _('Register!') }}</button>
</form> </form>
</div> </div>

View file

@ -47,6 +47,10 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.main-info tr td {
padding-bottom: 1em;
}
</style> </style>
{% endblock %} {% endblock %}
@ -91,7 +95,7 @@
{% csrf_token %} {% csrf_token %}
<table class="block-header grayed"> <table class="block-header grayed main-info">
<tr> <tr>
<td> {{ _('Fullname') }}: </td> <td> {{ _('Fullname') }}: </td>
<td> {{ form_user.first_name }} </td> <td> {{ form_user.first_name }} </td>
@ -101,8 +105,25 @@
<td> {{ form_user.last_name }} </td> <td> {{ form_user.last_name }} </td>
</tr> </tr>
<tr> <tr>
<td style="padding-top: 1em">{{ _('Avatar') }}: </td> <td> {{ _('Password') }}: </td>
<td style="padding-top: 1em">{{ form.profile_image }}</td> <td>
<a href="{{ url('password_change') }}">
{{ _('Change your password') }}
</a>
</td>
</tr>
<tr>
<td> {{ _('Email') }}: </td>
<td>
{{ request.user.email }}
<a href="{{ url('email_change') }}">
({{ _('Change email') }})
</a>
</td>
</tr>
<tr>
<td>{{ _('Avatar') }}: </td>
<td>{{ form.profile_image }}</td>
</tr> </tr>
</table> </table>
<hr> <hr>
@ -133,29 +154,22 @@
<td><span class="fullwidth">{{ form.math_engine }}</span></td> <td><span class="fullwidth">{{ form.math_engine }}</span></td>
</tr> </tr>
{% endif %} {% endif %}
<tr>
<td colspan="2">
<a href="{{ url('password_change') }}" class="inline-header">
{{ _('Change your password') }}
</a>
</td>
</tr>
</table> </table>
</td> </td>
</tr> </tr>
</table> </table>
<br>
<div> <div>
{% if profile.is_totp_enabled %} {% if profile.is_totp_enabled %}
{{ _('Two Factor Authentication is enabled.') }} {{ _('Two Factor Authentication is enabled.') }}
{% if require_staff_2fa and request.user.is_staff %} {% if require_staff_2fa and request.user.is_staff %}
<a id="disable-2fa-button" class="button inline-button">Disable</a> <a id="disable-2fa-button" class="button inline-button">Disable</a>
{% else %} {% else %}
<a href="{{ url('disable_2fa') }}" class="button inline-button">Disable</a> <a href="{{ url('disable_2fa') }}" class="button inline-button">{{_('Disable')}}</a>
{% endif %} {% endif %}
{% else %} {% else %}
{{ _('Two Factor Authentication is disabled.') }} {{ _('Two Factor Authentication is disabled.') }}
<a href="{{ url('enable_2fa') }}" class="button inline-button">Enable</a> <a href="{{ url('enable_2fa') }}" class="button inline-button">{{_('Enable')}}</a>
{% endif %} {% endif %}
</div> </div>
<br><hr> <br><hr>