diff --git a/dmoj/urls.py b/dmoj/urls.py index 54a7f12..402a569 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -65,6 +65,7 @@ from judge.views import ( internal, resolver, course, + email, ) from judge.views.problem_data import ( ProblemDataView, @@ -104,19 +105,19 @@ register_patterns = [ # confusing 404. url( r"^activate/(?P\w+)/$", - ActivationView.as_view(title="Activation key invalid"), + ActivationView.as_view(title=_("Activation key invalid")), name="registration_activate", ), url( r"^register/$", - RegistrationView.as_view(title="Register"), + RegistrationView.as_view(title=_("Register")), name="registration_register", ), url( r"^register/complete/$", TitledTemplateView.as_view( template_name="registration/registration_complete.html", - title="Registration Completed", + title=_("Registration Completed"), ), name="registration_complete", ), @@ -124,7 +125,7 @@ register_patterns = [ r"^register/closed/$", TitledTemplateView.as_view( template_name="registration/registration_closed.html", - title="Registration not allowed", + title=_("Registration not allowed"), ), name="registration_disallowed", ), @@ -183,6 +184,17 @@ register_patterns = [ ), name="password_reset_done", ), + url(r"^email/change/$", email.email_change_view, name="email_change"), + url( + r"^email/change/verify/(?P[0-9A-Za-z]+)-(?P.+)/$", + 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"^2fa/$", totp.TOTPLoginView.as_view(), name="login_2fa"), url(r"^2fa/enable/$", totp.TOTPEnableView.as_view(), name="enable_2fa"), diff --git a/judge/migrations/0163_email_change.py b/judge/migrations/0163_email_change.py new file mode 100644 index 0000000..985121d --- /dev/null +++ b/judge/migrations/0163_email_change.py @@ -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), + ), + ] diff --git a/judge/models/profile.py b/judge/models/profile.py index 8c8a31d..eb3572a 100644 --- a/judge/models/profile.py +++ b/judge/models/profile.py @@ -237,6 +237,7 @@ class Profile(models.Model): help_text=_("Notes for administrators regarding this user."), ) profile_image = models.ImageField(upload_to=profile_image_path, null=True) + email_change_pending = models.EmailField(blank=True, null=True) @cached_property def organization(self): diff --git a/judge/utils/email_render.py b/judge/utils/email_render.py new file mode 100644 index 0000000..5d790f5 --- /dev/null +++ b/judge/utils/email_render.py @@ -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 diff --git a/judge/views/email.py b/judge/views/email.py new file mode 100644 index 0000000..ede173a --- /dev/null +++ b/judge/views/email.py @@ -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"), + }, + ) diff --git a/judge/views/register.py b/judge/views/register.py index 76e8267..58d1d6f 100644 --- a/judge/views/register.py +++ b/judge/views/register.py @@ -15,7 +15,7 @@ from registration.backends.default.views import ( from registration.forms import RegistrationForm 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.widgets import Select2MultipleWidget, Select2Widget @@ -43,29 +43,10 @@ class CustomRegistrationForm(RegistrationForm): empty_label=None, 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: 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): if User.objects.filter(email=self.cleaned_data["email"]).exists(): raise forms.ValidationError( @@ -116,7 +97,6 @@ class RegistrationView(OldRegistrationView): cleaned_data = form.cleaned_data profile.timezone = cleaned_data["timezone"] profile.language = cleaned_data["language"] - profile.organizations.add(*cleaned_data["organizations"]) profile.save() return user diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 425241f..223d184 100644 --- a/locale/vi/LC_MESSAGES/django.po +++ b/locale/vi/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: lqdoj2\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" "Last-Translator: Icyene\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 #: judge/admin/interface.py:150 judge/models/contest.py:636 #: 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" msgstr "người dùng" @@ -49,11 +49,27 @@ msgstr "Tiếng Việt" msgid "English" 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" 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 msgid "Home" msgstr "Trang chủ" @@ -293,13 +309,13 @@ msgid "User" msgstr "Thành viên" #: 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" msgstr "Email" #: judge/admin/profile.py:138 judge/views/register.py:36 #: templates/registration/registration_form.html:68 -#: templates/user/edit-profile.html:119 +#: templates/user/edit-profile.html:140 msgid "Timezone" msgstr "Múi giờ" @@ -497,7 +513,7 @@ msgstr "Tên đăng nhập" #: judge/forms.py:428 templates/registration/registration_form.html:46 #: 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" 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." #: 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" msgstr "" @@ -2078,35 +2094,35 @@ msgstr "ghi chú nội bộ" msgid "Notes for administrators regarding this user." msgstr "Ghi chú riêng cho quản trị viên." -#: judge/models/profile.py:367 +#: judge/models/profile.py:368 msgid "user profile" msgstr "thông tin người dùng" -#: judge/models/profile.py:368 +#: judge/models/profile.py:369 msgid "user profiles" msgstr "thông tin người dùng" -#: judge/models/profile.py:384 +#: judge/models/profile.py:385 msgid "request time" msgstr "thời gian đăng ký" -#: judge/models/profile.py:387 +#: judge/models/profile.py:388 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:394 +#: judge/models/profile.py:395 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:397 +#: judge/models/profile.py:398 msgid "organization join request" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:398 +#: judge/models/profile.py:399 msgid "organization join requests" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:464 +#: judge/models/profile.py:465 #, fuzzy #| msgid "last seen" msgid "last visit" @@ -2839,6 +2855,55 @@ msgstr "Mô tả vấn đề" msgid "New clarification for %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 msgid "404 error" 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." #: judge/views/organization.py:193 judge/views/organization.py:337 -#, fuzzy -#| msgid "Can't edit 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 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." -#: judge/views/organization.py:230 judge/views/register.py:48 -#: judge/views/stats.py:184 templates/contest/list.html:89 -#: templates/problem/list-base.html:97 templates/stats/site.html:33 -#: templates/user/user-left-sidebar.html:4 templates/user/user-list-tabs.html:6 +#: judge/views/organization.py:230 judge/views/stats.py:184 +#: templates/contest/list.html:89 templates/problem/list-base.html:97 +#: templates/stats/site.html:33 templates/user/user-left-sidebar.html:4 +#: templates/user/user-list-tabs.html:6 msgid "Groups" msgstr "Nhóm" @@ -2941,7 +3004,7 @@ msgstr "Bạn đã ở trong nhóm." msgid "This group is not open." 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 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." @@ -3205,11 +3268,11 @@ msgstr "Các bài nộp tốt nhất cho {0}" 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" -#: 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" msgstr "Ngôn ngữ ưa thích" -#: judge/views/register.py:73 +#: judge/views/register.py:54 #, python-format msgid "" "The email address \"%s\" is already taken. Only one registration is allowed " @@ -3217,7 +3280,7 @@ msgid "" msgstr "" "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 "" "Your email provider is not allowed due to history of abuse. Please use a " "reputable email provider." @@ -3225,11 +3288,11 @@ msgstr "" "Your email provider is not allowed due to history of abuse. Please use a " "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" msgstr "Đăng ký" -#: judge/views/register.py:145 +#: judge/views/register.py:125 msgid "Authentication failure" msgstr "Xác thực thất bại" @@ -4185,11 +4248,11 @@ msgstr "Khôi phục kết quả" msgid "Disqualify" 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" 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 msgid "School" msgstr "Trường" @@ -4262,6 +4325,26 @@ msgstr "Còn" msgid "Upcoming contests" 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 msgid "View more" msgstr "Xem thêm" @@ -4290,6 +4373,10 @@ msgstr "Dừng" msgid "Continue" msgstr "Tiếp tục" +#: templates/general_email.html:15 +msgid "Dear" +msgstr "Xin chào" + #: templates/internal/left-sidebar.html:3 msgid "Average speed" msgstr "Tốc độ trung bình" @@ -4505,6 +4592,10 @@ msgstr "Từ chối" msgid "Kick" msgstr "Đuổi" +#: templates/pagedown.html:9 +msgid "Update Preview" +msgstr "Cập nhật xem trước" + #: templates/problem/clone.html:37 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:" @@ -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." 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 msgid "Invalid username or password." 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" #: templates/registration/password_reset_confirm.html:9 +#: templates/registration/password_reset_email.html:5 msgid "Reset Password" msgstr "Reset mật khẩu" @@ -5039,7 +5162,7 @@ msgstr "Reset mật khẩu" msgid "" "We've emailed you instructions for setting your password. You should be " "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 msgid "" @@ -5047,13 +5170,25 @@ msgid "" "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)." +#: 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 #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." 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 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 #, python-format 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/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 msgid "" "You have successfully been registered. An email has been sent to the email " -"address you provided to confirm your registration." -msgstr "Bạn đã đăng ký thành công. Kiểm tra email để hoàn thành việc xác thực." +"address you provided to confirm your registration. If you don't see it, " +"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 msgid "(again, for confirmation)" @@ -5106,21 +5242,9 @@ msgstr "chọn từ bản đồ" #: templates/registration/registration_form.html:78 msgid "Default language" -msgstr "Ngôn ngữ ưa thích" +msgstr "Ngôn ngữ mặc định" -#: templates/registration/registration_form.html:81 -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 +#: templates/registration/registration_form.html:89 msgid "Register!" msgstr "Đăng ký!" @@ -5128,7 +5252,7 @@ msgstr "Đăng ký!" #: templates/registration/totp_disable.html:45 #: templates/registration/totp_enable.html:83 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 #, python-format @@ -5516,43 +5640,51 @@ msgstr "Top Score" msgid "Rank" msgstr "Rank" -#: templates/user/edit-profile.html:104 -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 +#: templates/user/edit-profile.html:111 msgid "Change your password" 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." msgstr "Two Factor Authentication đã được kích hoạt." -#: templates/user/edit-profile.html:157 -msgid "Two Factor Authentication is disabled." -msgstr "Two Factor Authentication đã được hủy kích hoạt." +#: templates/user/edit-profile.html:168 +msgid "Disable" +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" msgstr "" -#: templates/user/edit-profile.html:166 +#: templates/user/edit-profile.html:180 msgid "Update profile" msgstr "Cập nhật thông tin" @@ -5768,6 +5900,15 @@ msgstr "Thông tin" msgid "Check all" 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" #~ msgstr "Đổi ảnh đại diện" @@ -5875,9 +6016,6 @@ msgstr "Chọn tất cả" #~ msgid "Color theme" #~ msgstr "Chủ đề màu sắc" -#~ msgid "Change color theme" -#~ msgstr "Đổi chủ đề màu sắc" - #~ msgid "commented on {time}" #~ msgstr "bình luận vào {time}" diff --git a/templates/email_change/email_change.html b/templates/email_change/email_change.html new file mode 100644 index 0000000..6033d18 --- /dev/null +++ b/templates/email_change/email_change.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block media %} + +{% endblock %} + +{% block body %} +
{% csrf_token %} + {{ form.as_table() }}
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/templates/email_change/email_change_failure.html b/templates/email_change/email_change_failure.html new file mode 100644 index 0000000..aa3a348 --- /dev/null +++ b/templates/email_change/email_change_failure.html @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block body %} +

{{ _('Invalid reset link.') }}

+{% endblock %} \ No newline at end of file diff --git a/templates/email_change/email_change_pending.html b/templates/email_change/email_change_pending.html new file mode 100644 index 0000000..3254fab --- /dev/null +++ b/templates/email_change/email_change_pending.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block body %} + {% if request.profile.email_change_pending %} +

{{ _('An email was sent to') }} {{request.profile.email_change_pending}}. {{_('If you don\'t see it, kindly check your spam folder as well.')}}

+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/email_change/email_change_success.html b/templates/email_change/email_change_success.html new file mode 100644 index 0000000..80e41e1 --- /dev/null +++ b/templates/email_change/email_change_success.html @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block body %} +

{{ _('Your email was sucessfully changed to') }} {{user.email}}

+{% endblock %} \ No newline at end of file diff --git a/templates/general_email.html b/templates/general_email.html new file mode 100644 index 0000000..0fcdbbb --- /dev/null +++ b/templates/general_email.html @@ -0,0 +1,21 @@ + + +
+
+ {{site_name}} +
+
+

{{title}}

+

{{_('Dear')}} {{username}},

+

{{message}}

+
+
+ {{button_text}} +
+
\ No newline at end of file diff --git a/templates/pagedown.html b/templates/pagedown.html index ab5867a..294b77d 100644 --- a/templates/pagedown.html +++ b/templates/pagedown.html @@ -6,7 +6,7 @@ {% if show_preview %}
-
Update Preview
+
{{_('Update Preview')}}
{% endif %} diff --git a/templates/registration/activation_email.html b/templates/registration/activation_email.html index f5e4eaa..25bf421 100644 --- a/templates/registration/activation_email.html +++ b/templates/registration/activation_email.html @@ -1,23 +1,16 @@ -Thanks for registering on the {{ site.name }}! We're glad to have you. -

-The last step is activating your account. Please activate your {{ SITE_NAME }} account in the next {{ expiration_days }} days. -

-Please click on the following link to activate your account: -

- http://{{ site.domain }}/accounts/activate/{{ activation_key }} -

- -Alternatively, you can reply to this message to activate your account. -Your reply must keep the following text intact for this to work: +{% set url_path = "/accounts/activate/" + activation_key %} +{% set title = _("Account activation") %} +{% 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) %} +{% set username = user.get_username() %} +{% set button_text = _("Activate") %} +{% set domain = site.domain %} +{% set protocol = "http" %} +{% include "general_email.html" %} +
+{{_("Alternatively, you can reply to this message to activate your account. Your reply must keep the following text intact for this to work:")}}
 {{ activation_key }}
 
-{% if SITE_ADMIN_EMAIL %} - See you soon! -
- If you have problems activating your account, feel free to send us an email at {{ SITE_ADMIN_EMAIL }}. -{% else %} - See you soon! -{% endif %} +{{_("See you soon!")}} diff --git a/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html index 30ae59d..0b30eb3 100644 --- a/templates/registration/password_reset_email.html +++ b/templates/registration/password_reset_email.html @@ -1,19 +1,6 @@ - -
-

LQDOJ -

-
-
-
- - Forgot your password on the {{ site_name }}? Don't worry!

- To reset the password for your account "{{ user.get_username() }}", click the below button. -

- Reset password -

- {% if SITE_ADMIN_EMAIL %} - See you soon! If you have problems resetting your email, feel free to shoot us an email at {{ SITE_ADMIN_EMAIL }} - {% else %} - See you soon! - {% endif %} -
+{% set url_path = url('password_reset_confirm', uidb64=uid, token=token) %} +{% set title = _("Password Reset") %} +{% set message = _("We have received a request to reset your password. Click the button below to reset your password:") %} +{% set username = user.get_username() %} +{% set button_text = _("Reset Password") %} +{% include "general_email.html" %} \ No newline at end of file diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html index 6d2b39a..b91a32d 100644 --- a/templates/registration/registration_complete.html +++ b/templates/registration/registration_complete.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block body %} -

{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration.') }}

+

{{ _('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.') }}

{% endblock %} \ No newline at end of file diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html index 58840b1..cf5bc30 100644 --- a/templates/registration/registration_form.html +++ b/templates/registration/registration_form.html @@ -78,12 +78,6 @@
{{ _('Default language') }}
{{ form.language }} -
{{ _('Affiliated organizations') }}
- {{ form.organizations }} - {% if form.organizations.errors %} -
{{ form.organizations.errors }}
- {% endif %} - {% if form.captcha %}
{{ form.captcha }}
{% if form.captcha.errors %} @@ -92,12 +86,6 @@ {% endif %}
- {% if tos_url %} - - {{ _('By registering, you agree to our') }} - {{ _('Terms & Conditions') }}. - - {% endif %} diff --git a/templates/user/edit-profile.html b/templates/user/edit-profile.html index 0433ffd..5ca5a88 100644 --- a/templates/user/edit-profile.html +++ b/templates/user/edit-profile.html @@ -47,6 +47,10 @@ display: flex; justify-content: center; } + + .main-info tr td { + padding-bottom: 1em; + } {% endblock %} @@ -91,7 +95,7 @@ {% csrf_token %} - +
@@ -101,8 +105,25 @@ - - + + + + + + + + + +
{{ _('Fullname') }}: {{ form_user.first_name }} {{ form_user.last_name }}
{{ _('Avatar') }}: {{ form.profile_image }} {{ _('Password') }}: + + {{ _('Change your password') }} + +
{{ _('Email') }}: + {{ request.user.email }} + + ({{ _('Change email') }}) + +
{{ _('Avatar') }}: {{ form.profile_image }}

@@ -133,29 +154,22 @@ {{ form.math_engine }} {% endif %} - - - - {{ _('Change your password') }} - - - - +
{% if profile.is_totp_enabled %} {{ _('Two Factor Authentication is enabled.') }} {% if require_staff_2fa and request.user.is_staff %} Disable {% else %} - Disable + {{_('Disable')}} {% endif %} {% else %} {{ _('Two Factor Authentication is disabled.') }} - Enable + {{_('Enable')}} {% endif %}