From d4961f9e2580f648ad5b9de8759bf4d24fd48f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Tue, 18 Aug 2020 00:37:04 +0800 Subject: [PATCH] Moved the local part of the application from the Mia core application to the Mia Womb local application. --- .../management/commands/accounting_sample.py | 2 +- accounting/urls.py | 2 +- accounting/views.py | 2 +- mia_core/converters.py | 54 ---- mia_core/digest_auth.py | 179 ------------ mia_core/forms.py | 183 ------------ mia_core/locale/zh_Hant/LC_MESSAGES/django.po | 268 ++---------------- .../locale/zh_Hant/LC_MESSAGES/djangojs.po | 50 ---- mia_core/models.py | 189 ------------ mia_core/static/mia_core/js/user-form.js | 264 ----------------- mia_core/templates/mia_core/user_detail.html | 183 ------------ mia_core/templates/mia_core/user_form.html | 105 ------- mia_core/templates/mia_core/user_list.html | 78 ----- mia_core/urls.py | 44 --- mia_core/views.py | 160 +---------- 15 files changed, 26 insertions(+), 1737 deletions(-) delete mode 100644 mia_core/converters.py delete mode 100644 mia_core/digest_auth.py delete mode 100644 mia_core/forms.py delete mode 100644 mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po delete mode 100644 mia_core/models.py delete mode 100644 mia_core/static/mia_core/js/user-form.js delete mode 100644 mia_core/templates/mia_core/user_detail.html delete mode 100644 mia_core/templates/mia_core/user_form.html delete mode 100644 mia_core/templates/mia_core/user_list.html delete mode 100644 mia_core/urls.py diff --git a/accounting/management/commands/accounting_sample.py b/accounting/management/commands/accounting_sample.py index 903f58d..1e26a75 100644 --- a/accounting/management/commands/accounting_sample.py +++ b/accounting/management/commands/accounting_sample.py @@ -26,7 +26,7 @@ from django.db import transaction from django.utils import timezone from accounting.utils import Populator -from mia_core.models import User +from mia_womb.models import User class Command(BaseCommand): diff --git a/accounting/urls.py b/accounting/urls.py index 57d9d21..bbfa883 100644 --- a/accounting/urls.py +++ b/accounting/urls.py @@ -23,7 +23,7 @@ from django.urls import path, register_converter from django.views.decorators.http import require_GET from django.views.generic import RedirectView -from mia_core.digest_auth import login_required +from mia_womb.digest_auth import login_required from . import converters, views register_converter(converters.PeriodConverter, "period") diff --git a/accounting/views.py b/accounting/views.py index a8a155a..f8958c7 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -38,7 +38,7 @@ from django.utils.translation import gettext as _, gettext_noop from django.views.decorators.http import require_GET, require_POST from django.views.generic import RedirectView, ListView, DetailView -from mia_core.digest_auth import login_required +from mia_womb.digest_auth import login_required from mia_core.period import Period from mia_core.utils import Pagination, get_multi_lingual_search, \ PaginationException diff --git a/mia_core/converters.py b/mia_core/converters.py deleted file mode 100644 index 44cc952..0000000 --- a/mia_core/converters.py +++ /dev/null @@ -1,54 +0,0 @@ -# The core application of the Mia project. -# by imacat , 2020/8/9 - -# Copyright (c) 2020 imacat. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The URL converters. - -""" -from .models import User - - -class UserConverter: - """The path converter for the user accounts.""" - regex = ".*" - - def to_python(self, value: str) -> User: - """Returns the user by her log in ID. - - Args: - value: The log in ID. - - Returns: - The user. - - Raises: - ValueError: When the value is invalid - """ - try: - return User.objects.get(login_id=value) - except User.DoesNotExist: - raise ValueError - - def to_url(self, value: User) -> str: - """Returns the log in ID of a user. - - Args: - value: The user. - - Returns: - The log in ID. - """ - return value.login_id diff --git a/mia_core/digest_auth.py b/mia_core/digest_auth.py deleted file mode 100644 index 65570ef..0000000 --- a/mia_core/digest_auth.py +++ /dev/null @@ -1,179 +0,0 @@ -# The core application of the Mia project. -# by imacat , 2020/7/5 - -# Copyright (c) 2020 imacat. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The HTTP digest authentication utilities of the Mia core -application. - -""" -import ipaddress -import socket -from functools import wraps -from typing import Optional - -from django.conf import settings -from django.db.models import F -from django.db.models.functions import Now -from django.http import HttpResponse, HttpRequest -from geoip import geolite2 - -from .models import User, Country - - -class AccountBackend: - """The account backend for the django-digest module.""" - - def get_partial_digest(self, username: str) -> Optional[str]: - """Returns the HTTP digest authentication password digest hash - of a user. - - Args: - username: The log in user name. - - Return: - The HTTP digest authentication password hash of the user, or None - if the user does not exist. - """ - user = User.objects.filter(login_id=username).first() - if user is None: - return None - return user.password - - def get_user(self, username: str) -> Optional[User]: - """Returns the user by her log in user name. - - Args: - username: The log in user name. - - Return: - The user, or None if the user does not exist. - """ - return User.objects.filter(login_id=username).first() - - -def login_required(function=None): - """The decorator to check if the user has logged in, and send - HTTP 401 if the user has not logged in. - """ - def decorator(view_func): - @wraps(view_func) - def _wrapped_view(request, *args, **kwargs): - if request.user.is_anonymous: - return HttpResponse(status=401) - if "logout" in request.session: - del request.session["logout"] - if "visit_logged" in request.session: - del request.session["visit_logged"] - return HttpResponse(status=401) - if not settings.DEBUG: - _log_visit(request) - return view_func(request, *args, **kwargs) - return _wrapped_view - if function: - return decorator(function) - return decorator - - -def _log_visit(request: HttpRequest) -> None: - """Logs the visit information for the logged-in user. - - Args: - request (HttpRequest): The request. - """ - if "visit_logged" in request.session: - return - user = request.user - ip = _get_remote_ip(request) - User.objects.filter(pk=user.pk).update( - visits=F("visits") + 1, - visited_at=Now(), - visited_ip=ip, - visited_host=_get_host(ip), - visited_country=_get_country(ip), - ) - request.session["visit_logged"] = True - - -def _get_remote_ip(request: HttpRequest) -> str: - """Returns the IP of the remote client. - - Args: - request: The request. - - Returns: - The IP of the remote client. - """ - x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") - if x_forwarded_for: - return x_forwarded_for.split(",")[0] - return request.META.get('REMOTE_ADDR') - - -def _get_host(ip: str) -> Optional[str]: - """Look-up the host name by its IP. - - Args: - ip: The IP - - Returns: - The host name, or None if the look-up fails. - """ - try: - return socket.gethostbyaddr(ip)[0] - except socket.herror: - return None - except socket.gaierror: - return None - - -def _get_country(ip: str) -> Optional[Country]: - """Look-up the country by its IP. - - Args: - ip: The IP - - Returns: - The country. - """ - code = _get_country_code(ip) - try: - return Country.objects.get(code=code) - except Country.DoesNotExist: - return None - - -def _get_country_code(ip: str) -> Optional[str]: - """Look-up the country code by its IP. - - Args: - ip: The IP - - Returns: - The country code, or None if the look-up fails. - """ - try: - return geolite2.lookup(ip).country - except ValueError: - pass - except AttributeError: - pass - try: - ipaddr = ipaddress.ip_address(ip) - if ipaddr.is_private: - return "AA" - except ValueError: - pass - return None diff --git a/mia_core/forms.py b/mia_core/forms.py deleted file mode 100644 index 83b3041..0000000 --- a/mia_core/forms.py +++ /dev/null @@ -1,183 +0,0 @@ -# The core application of the Mia project. -# by imacat , 2020/8/9 - -# Copyright (c) 2020 imacat. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The forms of the Mia core application. - -""" -from django import forms -from django.core.validators import RegexValidator -from django.db.models import Q -from django.utils.translation import gettext as _ - -from mia_core.models import User - - -class UserForm(forms.Form): - """A user account form.""" - login_id = forms.CharField( - max_length=32, - error_messages={ - "required": _("Please fill in the log in ID."), - "max_length": _("This log in ID is too long (max 32 characters)."), - }, - validators=[ - RegexValidator( - regex="^[^/]+$", - message=_("You cannot use slash (/) in the log in ID.")), - ]) - password = forms.CharField(required=False) - password2 = forms.CharField(required=False) - name = forms.CharField( - max_length=32, - error_messages={ - "required": _("Please fill in the name."), - "max_length": _("This name is too long (max 32 characters)."), - }) - is_disabled = forms.BooleanField(required=False) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.user = None - self.current_user = None - - def clean(self): - """Validates the form globally. - - Raises: - ValidationError: When the validation fails. - """ - errors = [] - validators = [self._validate_login_id_unique, - self._validate_password_new_required, - self._validate_password_login_id_changed_required, - self._validate_password2_required, - self._validate_passwords_equal, - self._validate_is_disabled_not_oneself] - for validator in validators: - try: - validator() - except forms.ValidationError as e: - errors.append(e) - if errors: - raise forms.ValidationError(errors) - - def _validate_login_id_unique(self) -> None: - """Validates whether the log in ID is unique. - - Raises: - forms.ValidationError: When the validation fails. - """ - if "login_id" not in self.data: - return - condition = Q(login_id=self.data["login_id"]) - if self.user is not None: - condition = condition & ~Q(pk=self.user.pk) - if User.objects.filter(condition).first() is None: - return - error = forms.ValidationError(_("This log in ID is already in use."), - code="login_id_unique") - self.add_error("login_id", error) - raise error - - def _validate_password_new_required(self) -> None: - """Validates whether the password is entered for newly-created users. - - Raises: - forms.ValidationError: When the validation fails. - """ - if self.user is not None: - return - if "password" in self.data: - return - error = forms.ValidationError(_("Please fill in the password."), - code="password_required") - self.add_error("password", error) - raise error - - def _validate_password_login_id_changed_required(self) -> None: - """Validates whether the password is entered for users whose login ID - changed. - - Raises: - forms.ValidationError: When the validation fails. - """ - if self.user is None: - return - if "login_id" not in self.data: - return - if self.data["login_id"] == self.user.login_id: - return - if "password" in self.data: - return - error = forms.ValidationError( - _("Please fill in the password to change the log in ID."), - code="password_required") - self.add_error("password", error) - raise error - - def _validate_password2_required(self) -> None: - """Validates whether the second password is entered. - - Raises: - forms.ValidationError: When the validation fails. - """ - if "password" not in self.data: - return - if "password2" in self.data: - return - error = forms.ValidationError( - _("Please enter the password again to verify it."), - code="password2_required") - self.add_error("password2", error) - raise error - - def _validate_passwords_equal(self) -> None: - """Validates whether the two passwords are equal. - - Raises: - forms.ValidationError: When the validation fails. - """ - if "password" not in self.data: - return - if "password2" not in self.data: - return - if self.data["password"] == self.data["password2"]: - return - error = forms.ValidationError(_("The two passwords do not match."), - code="passwords_equal") - self.add_error("password2", error) - raise error - - def _validate_is_disabled_not_oneself(self) -> None: - """Validates whether the user tries to disable herself - - Raises: - forms.ValidationError: When the validation fails. - """ - if "is_disabled" not in self.data: - return - if self.user is None: - return - if self.current_user is None: - return - if self.user.pk != self.current_user.pk: - return - error = forms.ValidationError( - _("You cannot disable your own account."), - code="not_oneself") - self.add_error("is_disabled", error) - raise error diff --git a/mia_core/locale/zh_Hant/LC_MESSAGES/django.po b/mia_core/locale/zh_Hant/LC_MESSAGES/django.po index ed5eeca..1953a67 100644 --- a/mia_core/locale/zh_Hant/LC_MESSAGES/django.po +++ b/mia_core/locale/zh_Hant/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Traditional Chinese PO file for the Mia Website +# Traditional Chinese PO file for the Mia core application # Copyright (C) 2020 imacat # This file is distributed under the same license as the Mia package. # imacat , 2020. @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: mia-core 3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-11 21:41+0800\n" -"PO-Revision-Date: 2020-08-11 21:44+0800\n" +"POT-Creation-Date: 2020-08-17 23:56+0800\n" +"PO-Revision-Date: 2020-08-18 00:02+0800\n" "Last-Translator: imacat \n" "Language-Team: Traditional Chinese \n" "Language: Traditional Chinese\n" @@ -16,101 +16,57 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: mia_core/forms.py:34 -msgid "Please fill in the log in ID." -msgstr "請填寫登入帳號。" - -#: mia_core/forms.py:35 -msgid "This log in ID is too long (max 32 characters)." -msgstr "登入帳號太長了(最長32個字)。" - -#: mia_core/forms.py:40 -msgid "You cannot use slash (/) in the log in ID." -msgstr "登入帳號不可以包含斜線 (/) 。" - -#: mia_core/forms.py:47 -msgid "Please fill in the name." -msgstr "請填寫姓名。" - -#: mia_core/forms.py:48 -msgid "This name is too long (max 32 characters)." -msgstr "姓名太長了(最長32個字)。" - -#: mia_core/forms.py:91 -msgid "This log in ID is already in use." -msgstr "登入帳號和其他人重複。" - -#: mia_core/forms.py:106 -msgid "Please fill in the password." -msgstr "請填寫密碼。" - -#: mia_core/forms.py:127 -msgid "Please fill in the password to change the log in ID." -msgstr "變更登入帳號時,請填寫密碼。" - -#: mia_core/forms.py:143 -msgid "Please enter the password again to verify it." -msgstr "請再次確認密碼。" - -#: mia_core/forms.py:160 -msgid "The two passwords do not match." -msgstr "兩次密碼不符,請重新輸入。" - -#: mia_core/forms.py:180 mia_core/templates/mia_core/user_form.html:85 -msgid "You cannot disable your own account." -msgstr "不能停用自己的帳號。" - -#: mia_core/period.py:447 mia_core/period.py:482 mia_core/period.py:500 -#: mia_core/period.py:513 mia_core/period.py:559 +#: mia_core/period.py:452 mia_core/period.py:487 mia_core/period.py:505 +#: mia_core/period.py:518 mia_core/period.py:564 #, python-format msgid "In %s" msgstr "%s" -#: mia_core/period.py:457 +#: mia_core/period.py:462 #, python-format msgid "Since %s" msgstr "%s至今" -#: mia_core/period.py:470 mia_core/period.py:491 mia_core/period.py:570 +#: mia_core/period.py:475 mia_core/period.py:496 mia_core/period.py:575 #, python-format msgid "Until %s" msgstr "至%s前" -#: mia_core/period.py:499 +#: mia_core/period.py:504 msgid "All Time" msgstr "全部" -#: mia_core/period.py:583 mia_core/period.py:616 +#: mia_core/period.py:588 mia_core/period.py:621 #: mia_core/templates/mia_core/include/period-chooser.html:60 -#: mia_core/templatetags/mia_core.py:170 +#: mia_core/templatetags/mia_core.py:173 msgid "This Month" msgstr "這個月" -#: mia_core/period.py:624 +#: mia_core/period.py:629 #: mia_core/templates/mia_core/include/period-chooser.html:63 -#: mia_core/templatetags/mia_core.py:177 +#: mia_core/templatetags/mia_core.py:180 msgid "Last Month" msgstr "上個月" -#: mia_core/period.py:639 +#: mia_core/period.py:644 #: mia_core/templates/mia_core/include/period-chooser.html:76 msgid "This Year" msgstr "今年" -#: mia_core/period.py:641 +#: mia_core/period.py:646 #: mia_core/templates/mia_core/include/period-chooser.html:79 msgid "Last Year" msgstr "去年" -#: mia_core/period.py:656 +#: mia_core/period.py:661 #: mia_core/templates/mia_core/include/period-chooser.html:95 -#: mia_core/templatetags/mia_core.py:150 +#: mia_core/templatetags/mia_core.py:153 msgid "Today" msgstr "今天" -#: mia_core/period.py:658 +#: mia_core/period.py:663 #: mia_core/templates/mia_core/include/period-chooser.html:98 -#: mia_core/templatetags/mia_core.py:152 +#: mia_core/templatetags/mia_core.py:155 msgid "Yesterday" msgstr "昨天" @@ -144,7 +100,6 @@ msgstr "日期:" #: mia_core/templates/mia_core/include/period-chooser.html:107 #: mia_core/templates/mia_core/include/period-chooser.html:125 -#: mia_core/templates/mia_core/user_detail.html:69 msgid "Confirm" msgstr "確定" @@ -160,198 +115,17 @@ msgstr "從:" msgid "To:" msgstr "到:" -#: mia_core/templates/mia_core/user_detail.html:39 -#: mia_core/templates/mia_core/user_detail.html:84 -msgid "Settings" -msgstr "設定" - -#: mia_core/templates/mia_core/user_detail.html:46 -msgid "The account is not in use." -msgstr "帳號未使用。" - -#: mia_core/templates/mia_core/user_detail.html:60 -msgid "User Deletion Confirmation" -msgstr "帳號刪除確認" - -#: mia_core/templates/mia_core/user_detail.html:65 -msgid "Do you really want to delete this user?" -msgstr "您真的要刪掉這個帳號嗎?" - -#: mia_core/templates/mia_core/user_detail.html:70 -msgid "Cancel" -msgstr "取消" - -#: mia_core/templates/mia_core/user_detail.html:80 -#: mia_core/templates/mia_core/user_form.html:41 -msgid "Back" -msgstr "回上頁" - -#: mia_core/templates/mia_core/user_detail.html:87 mia_core/views.py:163 -msgid "You cannot delete your own account." -msgstr "不能刪除自己的帳號。" - -#: mia_core/templates/mia_core/user_detail.html:89 -#: mia_core/templates/mia_core/user_detail.html:94 -#: mia_core/templates/mia_core/user_detail.html:99 -#: mia_core/templates/mia_core/user_detail.html:104 -msgid "Delete" -msgstr "刪除" - -#: mia_core/templates/mia_core/user_detail.html:92 mia_core/views.py:166 -msgid "You cannot delete this account because it is in use." -msgstr "帳號使用中,不可刪除。" - -#: mia_core/templates/mia_core/user_detail.html:97 mia_core/views.py:168 -msgid "This account is already deleted." -msgstr "帳號已刪除。" - -#: mia_core/templates/mia_core/user_detail.html:111 -#: mia_core/templates/mia_core/user_form.html:50 -msgid "Log in ID.:" -msgstr "登入帳號:" - -#: mia_core/templates/mia_core/user_detail.html:116 -#: mia_core/templates/mia_core/user_form.html:74 -msgid "Name:" -msgstr "姓名:" - -#: mia_core/templates/mia_core/user_detail.html:122 -#: mia_core/templates/mia_core/user_form.html:89 -msgid "This account is disabled." -msgstr "帳號停用。" - -#: mia_core/templates/mia_core/user_detail.html:128 -msgid "This account is deleted." -msgstr "帳號已刪。" - -#: mia_core/templates/mia_core/user_detail.html:134 -msgid "This user has not logged in yet." -msgstr "使用者還沒登入過。" - -#: mia_core/templates/mia_core/user_detail.html:138 -msgid "# of visits:" -msgstr "登入次數:" - -#: mia_core/templates/mia_core/user_detail.html:143 -msgid "Last visit at:" -msgstr "上次登入時間:" - -#: mia_core/templates/mia_core/user_detail.html:148 -msgid "IP:" -msgstr "IP:" - -#: mia_core/templates/mia_core/user_detail.html:153 -msgid "Host:" -msgstr "主機名稱:" - -#: mia_core/templates/mia_core/user_detail.html:158 -msgid "Country:" -msgstr "國家:" - -#: mia_core/templates/mia_core/user_detail.html:159 -msgid "(Not Available)" -msgstr "(不可考)" - -#: mia_core/templates/mia_core/user_detail.html:164 -msgid "Created at:" -msgstr "建檔時間:" - -#: mia_core/templates/mia_core/user_detail.html:169 -msgid "Created by:" -msgstr "建檔人:" - -#: mia_core/templates/mia_core/user_detail.html:174 -msgid "Updated at:" -msgstr "更新時間:" - -#: mia_core/templates/mia_core/user_detail.html:179 -msgid "Updated by:" -msgstr "更新人:" - -#: mia_core/templates/mia_core/user_form.html:31 -msgid "Add a New Account" -msgstr "建立帳號" - -#: mia_core/templates/mia_core/user_form.html:58 -msgid "Password:" -msgstr "密碼:" - -#: mia_core/templates/mia_core/user_form.html:66 -msgid "Confirm password:" -msgstr "確認密碼:" - -#: mia_core/templates/mia_core/user_form.html:99 -msgid "Submit" -msgstr "傳送" - -#: mia_core/templates/mia_core/user_list.html:27 -msgid "Account Management" -msgstr "帳號管理" - -#: mia_core/templates/mia_core/user_list.html:35 -msgid "New" -msgstr "新增" - -#: mia_core/templates/mia_core/user_list.html:43 -msgid "Log in ID." -msgstr "登入帳號" - -#: mia_core/templates/mia_core/user_list.html:44 -msgid "Name" -msgstr "姓名" - -#: mia_core/templates/mia_core/user_list.html:45 -#: mia_core/templates/mia_core/user_list.html:67 -msgid "View" -msgstr "查閱" - -#: mia_core/templates/mia_core/user_list.html:55 -msgid "Disabled" -msgstr "停用" - -#: mia_core/templates/mia_core/user_list.html:58 -msgid "Deleted" -msgstr "已刪" - -#: mia_core/templates/mia_core/user_list.html:61 -msgid "Not In Use" -msgstr "未使用" - -#: mia_core/templates/mia_core/user_list.html:75 -msgid "There is currently no data." -msgstr "目前沒有資料。" - -#: mia_core/utils.py:343 +#: mia_core/utils.py:347 msgctxt "Pagination|" msgid "Previous" msgstr "上一頁" -#: mia_core/utils.py:371 mia_core/utils.py:392 +#: mia_core/utils.py:375 mia_core/utils.py:396 msgctxt "Pagination|" msgid "..." msgstr "…" -#: mia_core/utils.py:411 +#: mia_core/utils.py:415 msgctxt "Pagination|" msgid "Next" msgstr "下一頁" - -#: mia_core/views.py:141 -msgid "This user account was not changed." -msgstr "帳號未異動。" - -#: mia_core/views.py:144 -msgid "This user account was saved successfully." -msgstr "帳號已儲存。" - -#: mia_core/views.py:173 -msgid "This user account was deleted successfully." -msgstr "帳號已刪除。" - -#: mia_core/views.py:229 -msgid "Your user account was not changed." -msgstr "你的帳號未異動。" - -#: mia_core/views.py:232 -msgid "Your user account was saved successfully." -msgstr "你的帳號已儲存。" diff --git a/mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po b/mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po deleted file mode 100644 index 5bcfb61..0000000 --- a/mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po +++ /dev/null @@ -1,50 +0,0 @@ -# Traditional Chinese PO file for the JavaScript on the Mia Website -# Copyright (C) 2020 imacat -# This file is distributed under the same license as the Mia package. -# imacat , 2020. -# -msgid "" -msgstr "" -"Project-Id-Version: mia-core-js 3.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-11 21:42+0800\n" -"PO-Revision-Date: 2020-08-11 21:44+0800\n" -"Last-Translator: imacat \n" -"Language-Team: Traditional Chinese \n" -"Language: Traditional Chinese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -#: mia_core/static/mia_core/js/user-form.js:154 -msgid "Please fill in the log in ID." -msgstr "請填寫登入帳號。" - -#: mia_core/static/mia_core/js/user-form.js:159 -msgid "You cannot use slash (/) in the log in ID." -msgstr "登入帳號不可以包含斜線 (/) 。" - -#: mia_core/static/mia_core/js/user-form.js:179 -msgid "This log in ID is already in use." -msgstr "登入帳號和其他人重複。" - -#: mia_core/static/mia_core/js/user-form.js:204 -msgid "Please fill in the password to change the log in ID." -msgstr "變更登入帳號時,請填寫密碼。" - -#: mia_core/static/mia_core/js/user-form.js:206 -msgid "Please fill in the password." -msgstr "請填寫密碼。" - -#: mia_core/static/mia_core/js/user-form.js:231 -msgid "Please enter the password again to verify it." -msgstr "請再次確認密碼。" - -#: mia_core/static/mia_core/js/user-form.js:237 -msgid "The two passwords do not match." -msgstr "兩次密碼不符,請重新輸入。" - -#: mia_core/static/mia_core/js/user-form.js:258 -msgid "Please fill in the name." -msgstr "請填寫姓名。" diff --git a/mia_core/models.py b/mia_core/models.py deleted file mode 100644 index eb91fa5..0000000 --- a/mia_core/models.py +++ /dev/null @@ -1,189 +0,0 @@ -# The core application of the Mia project. -# by imacat , 2020/6/29 - -# Copyright (c) 2020 imacat. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The data models of the Mia core application. - -""" -import hashlib - -from dirtyfields import DirtyFieldsMixin -from django.conf import settings -from django.db import models, connection, OperationalError, transaction, \ - ProgrammingError -from django.db.models.functions import Now - -from mia_core.utils import get_multi_lingual_attr, set_multi_lingual_attr, \ - new_pk - - -class Country(DirtyFieldsMixin, models.Model): - """A country.""" - id = models.PositiveIntegerField(primary_key=True, db_column="sn") - code = models.CharField(max_length=2, unique=True, db_column="id") - name_en = models.CharField(max_length=64) - name_zh_hant = models.CharField( - max_length=32, null=True, db_column="name_zhtw") - name_zh_hans = models.CharField( - max_length=32, null=True, db_column="name_zhcn") - is_special = models.BooleanField( - default=False, db_column="special") - created_at = models.DateTimeField( - auto_now_add=True, db_column="created") - created_by = models.ForeignKey( - "User", on_delete=models.PROTECT, - db_column="createdby", related_name="created_countries") - updated_at = models.DateTimeField( - auto_now=True, db_column="updated") - updated_by = models.ForeignKey( - "User", on_delete=models.PROTECT, - db_column="updatedby", related_name="updated_countries") - - def __str__(self): - """Returns the string representation of this country.""" - return self.code.__str__() + " " + self.name.__str__() - - @property - def name(self) -> str: - """The country name in the current language.""" - return get_multi_lingual_attr(self, "name", "en") - - @name.setter - def name(self, value: str) -> None: - set_multi_lingual_attr(self, "name", value) - - class Meta: - db_table = "country" - - -class User(DirtyFieldsMixin, models.Model): - """A user.""" - id = models.PositiveIntegerField(primary_key=True, db_column="sn") - login_id = models.CharField(max_length=32, unique=True, db_column="id") - password = models.CharField(max_length=32, db_column="passwd") - name = models.CharField(max_length=32) - is_disabled = models.BooleanField( - default=False, db_column="disabled") - is_deleted = models.BooleanField( - default=False, db_column="deleted") - language = models.CharField(max_length=6, null=True, db_column="lang") - visits = models.PositiveSmallIntegerField(default=0) - visited_at = models.DateTimeField(null=True, db_column="visited") - visited_ip = models.GenericIPAddressField(null=True, db_column="ip") - visited_host = models.CharField( - max_length=128, null=True, db_column="host") - visited_country = models.ForeignKey( - Country, on_delete=models.PROTECT, null=True, - db_column="ct", to_field="code", related_name="users") - created_at = models.DateTimeField( - auto_now_add=True, db_column="created") - created_by = models.ForeignKey( - "self", on_delete=models.PROTECT, - db_column="createdby", related_name="created_users") - updated_at = models.DateTimeField( - auto_now_add=True, db_column="updated") - updated_by = models.ForeignKey( - "self", on_delete=models.PROTECT, - db_column="updatedby", related_name="updated_users") - REQUIRED_FIELDS = ["id", "name"] - USERNAME_FIELD = "login_id" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.current_user = None - - @property - def is_anonymous(self) -> bool: - return False - - @property - def is_authenticated(self) -> bool: - return True - - def set_password(self): - pass - - def check_password(self): - pass - - def __str__(self): - """Returns the string representation of this user.""" - return "%s (%s)" % ( - self.name.__str__(), self.login_id.__str__()) - - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): - if self.pk is None: - self.pk = new_pk(User) - if self.current_user is not None: - self.created_by = self.current_user - if self.current_user is not None: - self.updated_by = self.current_user - with transaction.atomic(): - super(User, self).save( - force_insert=force_insert, force_update=force_update, - using=using, update_fields=update_fields) - User.objects.filter(pk=self.pk).update(updated_at=Now()) - - class Meta: - db_table = "users" - app_label = "mia_core" - - def set_digest_password(self, login_id, password): - self.password = self.md5( - F"{login_id}:{settings.DIGEST_REALM}:{password}") - - @staticmethod - def md5(value: str) -> str: - m = hashlib.md5() - m.update(value.encode("utf-8")) - return m.hexdigest() - - def is_in_use(self) -> bool: - """Returns whether this user is in use. - - Returns: - True if this user is in use, or False otherwise. - """ - for table in connection.introspection.table_names(): - if self._is_in_use_with(F"SELECT * FROM {table}" - " WHERE createdby=%s OR updatedby=%s"): - return True - if self._is_in_use_with( - F"SELECT * FROM {table}" - " WHERE created_by_id=%s OR updated_by_id=%s"): - return True - return False - - def _is_in_use_with(self, sql: str) -> bool: - """Returns whether this user is in use with a specific SQL statement. - - Args: - sql: The SQL query statement - - Returns: - True if this user is in use, or False otherwise. - """ - with connection.cursor() as cursor: - try: - cursor.execute(sql, [self.pk, self.pk]) - except OperationalError: - return False - except ProgrammingError: - return False - if cursor.fetchone() is None: - return False - return True diff --git a/mia_core/static/mia_core/js/user-form.js b/mia_core/static/mia_core/js/user-form.js deleted file mode 100644 index d24b699..0000000 --- a/mia_core/static/mia_core/js/user-form.js +++ /dev/null @@ -1,264 +0,0 @@ -/* The Mia Website - * edit.js: The JavaScript to edit the user data - */ - -/* Copyright (c) 2019-2020 imacat. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Author: imacat@mail.imacat.idv.tw (imacat) - * First written: 2020/3/26 - */ - -// Initializes the page JavaScript. -$(function () { - $("#user-login-id") - .on("blur", function () { - validateLoginId(); - updatePasswordRequirement(); - }) - $("#user-password") - .on("blur", function () { - validatePassword(); - }); - $("#user-password2") - .on("blur", function () { - validatePassword2(); - }); - $("#user-name") - .on("blur", function () { - validateName(); - }); - $("#user-form") - .on("submit", function () { - return validateForm(); - }); -}); - -/** - * Updates the password required when the log in ID is changed. - * - * The HTTP digest authentication requires both the log in ID and the - * password to compose and store the hash. When the log in ID is - * changed, we will also need the password in order to update the - * hash. - * - * @private - */ -function updatePasswordRequirement() { - const originalId = $("#user-login-id-original").val(); - if (originalId === "") { - return; - } - $("#user-password")[0].required = ($("#user-login-id").val() !== originalId); - validatePassword(); -} - - -/******************* - * Form Validation * - *******************/ - -/** - * The validation result - * @type {object} - * @private - */ -let isValidated; - -/** - * Validates the form. - * - * @returns {boolean} true if the validation succeed, or false - * otherwise - * @private - */ -function validateForm() { - isValidated = { - "id": null, - "_sync": true, - }; - validateLoginIdAsync().then(); - validateSyncColumns(); - return false; -} - -/** - * Validates the form on synchronous validations. - * - * @private - */ -function validateSyncColumns() { - let isSyncValidated = true; - isSyncValidated = isSyncValidated && validatePassword(); - isSyncValidated = isSyncValidated && validatePassword2(); - isSyncValidated = isSyncValidated && validateName(); - isValidated["_sync"] = isSyncValidated; - validateFormAsync(); -} - -/** - * Validates the form for the asynchronous validation. - * - * @private - */ -function validateFormAsync() { - let isFormValidated = true; - const keys = Object.keys(isValidated); - for (let i = 0; i < keys.length; i++) { - if (isValidated[keys[i]] === null) { - return; - } - isFormValidated = isFormValidated && isValidated[keys[i]]; - } - if (isFormValidated) { - $("#user-form")[0].submit(); - } -} - -/** - * Validates the log in ID for asynchronous form validation. - * - * @returns {Promise} - * @private - */ -async function validateLoginIdAsync() { - isValidated["id"] = await validateLoginId(); - validateFormAsync(); -} - -/** - * Validates the log in ID. - * - * @returns {boolean} true if the validation succeed, or false - * otherwise - * @private - */ -async function validateLoginId() { - const id = $("#user-login-id")[0]; - const errorMessage = $("#user-login-id-error"); - id.value = id.value.trim(); - if (id.value === "") { - id.classList.add("is-invalid"); - errorMessage.text(gettext("Please fill in the log in ID.")); - return false; - } - if (id.value.match(/\//)) { - id.classList.add("is-invalid"); - errorMessage.text(gettext("You cannot use slash (/) in the log in ID.")); - return false; - } - const originalId = $("#user-login-id-original").val(); - if (originalId === "" || id.value !== originalId) { - let exists = null; - const request = new XMLHttpRequest(); - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - exists = JSON.parse(this.responseText); - } - }; - const url = $("#exists-url").val().replace("ID", id.value); - request.open("GET", url, true); - request.send(); - while (exists === null) { - await new Promise(r => setTimeout(r, 200)); - } - if (exists) { - id.classList.add("is-invalid"); - errorMessage.text(gettext("This log in ID is already in use.")); - return false; - } - } - id.classList.remove("is-invalid"); - errorMessage.text(""); - return true; -} - -/** - * Validates the password. - * - * @returns {boolean} true if the validation succeed, or false - * otherwise - * @private - */ -async function validatePassword() { - const password = $("#user-password")[0]; - const errorMessage = $("#user-password-error"); - password.value = password.value.trim(); - if (password.required) { - if (password.value === "") { - password.classList.add("is-invalid"); - const originalId = $("#user-login-id-original").val(); - if (originalId === "" || $("#user-login-id").val() !== originalId) { - errorMessage.text(gettext("Please fill in the password to change the log in ID.")); - } else { - errorMessage.text(gettext("Please fill in the password.")); - } - return false; - } - } - password.classList.remove("is-invalid"); - errorMessage.text(""); - return true; -} - -/** - * Validates the password verification. - * - * @returns {boolean} true if the validation succeed, or false - * otherwise - * @private - */ -function validatePassword2() { - const password2 = $("#user-password2")[0]; - const errorMessage = $("#user-password2-error"); - password2.value = password2.value.trim(); - const password = $("#user-password").val(); - if (password !== "") { - if (password2.value === "") { - password2.classList.add("is-invalid"); - errorMessage.text(gettext("Please enter the password again to verify it.")); - return false; - } - } - if (password2.value !== password) { - password2.classList.add("is-invalid"); - errorMessage.text(gettext("The two passwords do not match.")); - return false; - } - password2.classList.remove("is-invalid"); - errorMessage.text(""); - return true; -} - -/** - * Validates the name. - * - * @returns {boolean} true if the validation succeed, or false - * otherwise - * @private - */ -function validateName() { - const name = $("#user-name")[0]; - const errorMessage = $("#user-name-error"); - name.value = name.value.trim(); - if (name.value === "") { - name.classList.add("is-invalid"); - errorMessage.text(gettext("Please fill in the name.")); - return false; - } - name.classList.remove("is-invalid"); - errorMessage.text(""); - return true; -} diff --git a/mia_core/templates/mia_core/user_detail.html b/mia_core/templates/mia_core/user_detail.html deleted file mode 100644 index aafe645..0000000 --- a/mia_core/templates/mia_core/user_detail.html +++ /dev/null @@ -1,183 +0,0 @@ -{% extends "base.html" %} -{% comment %} -The Mia Core Application -user_detail.html: The template for the user details - - Copyright (c) 2020 imacat. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Author: imacat@mail.imacat.idv.tw (imacat) -First written: 2020/8/9 -{% endcomment %} -{% load i18n %} -{% load mia_core %} -{% if request|is_in_section:"mia_core:my-account" %} - {% setvar "user" request.user %} -{% endif %} - -{% block settings %} - {% setvar "title" user.name %} -{% endblock %} - -{% block content %} - -{% if request|is_in_section:"mia_core:my-account" %} - -{% else %} - {% if not user.is_in_use %} -
- - {{ _("The account is not in use.")|force_escape }} -
- {% endif %} - - -
- {% csrf_token %} - - -
- -
- - - {{ _("Back")|force_escape }} - - - - {{ _("Settings")|force_escape }} - - {% if user.login_id == request.user.login_id %} - - {% elif user.is_in_use %} - - {% elif user.is_deleted %} - - {% else %} - - {% endif %} -
-{% endif %} - -
-
{{ _("Log in ID.:")|force_escape }}
-
{{ user.login_id }}
-
- -
-
{{ _("Name:")|force_escape }}
-
{{ user.name }}
-
- -{% if user.is_disabled %} -
-
{{ _("This account is disabled.")|force_escape }}
-
-{% endif %} - -{% if user.is_deleted %} -
-
{{ _("This account is deleted.")|force_escape }}
-
-{% endif %} - -{% if not user.visits %} -
-
{{ _("This user has not logged in yet.")|force_escape }}
-
-{% else %} -
-
{{ _("# of visits:")|force_escape }}
-
{{ user.visits }}
-
- -
-
{{ _("Last visit at:")|force_escape }}
-
{{ user.visited_at }}
-
- -
-
{{ _("IP:")|force_escape }}
-
{{ user.visited_ip }}
-
- -
-
{{ _("Host:")|force_escape }}
-
{{ user.visited_host }}
-
- -
-
{{ _("Country:")|force_escape }}
-
{{ user.visited_country.name|default:_("(Not Available)")|force_escape }}
-
-{% endif %} - -
-
{{ _("Created at:")|force_escape }}
-
{{ user.created_at }}
-
- -
-
{{ _("Created by:")|force_escape }}
-
{{ user.created_by }}
-
- -
-
{{ _("Updated at:")|force_escape }}
-
{{ user.updated_at }}
-
- -
-
{{ _("Updated by:")|force_escape }}
-
{{ user.updated_by }}
-
- -{% endblock %} diff --git a/mia_core/templates/mia_core/user_form.html b/mia_core/templates/mia_core/user_form.html deleted file mode 100644 index 6d1a8ff..0000000 --- a/mia_core/templates/mia_core/user_form.html +++ /dev/null @@ -1,105 +0,0 @@ -{% extends "base.html" %} -{% comment %} -The Mia Core Application -user_form.html: The template for the form of a user - - Copyright (c) 2020 imacat. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Author: imacat@mail.imacat.idv.tw (imacat) -First written: 2020/8/9 -{% endcomment %} -{% load static %} -{% load i18n %} -{% load mia_core %} - -{% block settings %} - {% if form.user %} - {% setvar "title" user.name %} - {% else %} - {% setvar "title" _("Add a New Account") %} - {% endif %} - {% static "mia_core/js/user-form.js" as file %}{% add_js file %} -{% endblock %} - -{% block content %} - - - -
- {% csrf_token %} - - -
- -
- -
{{ form.login_id.errors.0 }}
-
-
- -
- -
- -
{{ form.password.errors.0 }}
-
-
- -
- -
- -
{{ form.password2.errors.0 }}
-
-
- -
- -
- -
{{ form.name.errors.0 }}
-
-
- - {% if not request|is_in_section:"mia_core:my-account" %} -
-
- {% if form.user and form.user.pk == request.user.pk %} - {{ _("You cannot disable your own account.")|force_escape }} - {% else %} - - - {% endif %} -
-
- {% endif %} - -
-
- -
-
-
- -{% endblock %} diff --git a/mia_core/templates/mia_core/user_list.html b/mia_core/templates/mia_core/user_list.html deleted file mode 100644 index c45c773..0000000 --- a/mia_core/templates/mia_core/user_list.html +++ /dev/null @@ -1,78 +0,0 @@ -{% extends "base.html" %} -{% comment %} -The Mia Core Application -user_list.html: The template for the user list - - Copyright (c) 2020 imacat. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Author: imacat@mail.imacat.idv.tw (imacat) -First written: 2020/8/9 -{% endcomment %} -{% load i18n %} -{% load mia_core %} - -{% block settings %} - {% setvar "title" _("Account Management") %} -{% endblock %} - -{% block content %} - - - -{% if user_list %} - - - - - - - - - - {% for user in user_list %} - - - - - - {% endfor %} - -
{{ _("Log in ID.")|force_escape }}{{ _("Name")|force_escape }}{{ _("View")|force_escape }}
{{ user.login_id }} - {{ user.name }} - {% if user.is_disabled %} - {{ _("Disabled")|force_escape }} - {% endif %} - {% if user.is_deleted %} - {{ _("Deleted")|force_escape }} - {% endif %} - {% if not user.is_in_use %} - {{ _("Not In Use")|force_escape }} - {% endif %} - - - - {{ _("View")|force_escape }} - -
-{% else %} -

{{ _("There is currently no data.")|force_escape }}

-{% endif %} - -{% endblock %} diff --git a/mia_core/urls.py b/mia_core/urls.py deleted file mode 100644 index 3071c06..0000000 --- a/mia_core/urls.py +++ /dev/null @@ -1,44 +0,0 @@ -# The Mia core application of the Mia project. -# by imacat , 2020/8/9 - -# Copyright (c) 2020 imacat. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The route settings of the Mia core application. - -""" -from django.urls import path, register_converter -from django.views.decorators.http import require_GET -from django.views.generic import TemplateView - -from . import views, converters -from .digest_auth import login_required - -register_converter(converters.UserConverter, "user") - -app_name = "mia_core" -urlpatterns = [ - path("logout", views.logout, name="logout"), - path("users", views.UserListView.as_view(), name="users"), - path("users/create", views.UserFormView.as_view(), name="users.create"), - path("users/", views.UserView.as_view(), name="users.detail"), - path("users//update", views.UserFormView.as_view(), name="users.update"), - path("users//delete", views.user_delete, name="users.delete"), - path("api/users//exists", views.api_users_exists, - name="api.users.exists"), - path("my-account", require_GET(login_required(TemplateView.as_view( - template_name="mia_core/user_detail.html"))), name="my-account"), - path("my-account/update", views.MyAccountFormView.as_view(), - name="my-account.update"), -] diff --git a/mia_core/views.py b/mia_core/views.py index 62648cf..b4cfcf6 100644 --- a/mia_core/views.py +++ b/mia_core/views.py @@ -23,24 +23,15 @@ from typing import Dict, Type, Optional, Any from dirtyfields import DirtyFieldsMixin from django import forms from django.contrib import messages -from django.contrib.auth import logout as logout_user from django.contrib.messages.views import SuccessMessageMixin from django.db.models import Model -from django.http import HttpResponse, JsonResponse, HttpRequest, \ +from django.http import HttpResponse, HttpRequest, \ HttpResponseRedirect, Http404 from django.shortcuts import redirect, render -from django.urls import reverse -from django.utils.decorators import method_decorator -from django.utils.translation import gettext_noop -from django.views.decorators.http import require_POST, require_GET -from django.views.generic import DeleteView as CoreDeleteView, ListView, \ - DetailView +from django.views.generic import DeleteView as CoreDeleteView from django.views.generic.base import View from . import stored_post, utils -from .digest_auth import login_required -from .forms import UserForm -from .models import User from .utils import UrlBuilder @@ -232,150 +223,3 @@ class DeleteView(SuccessMessageMixin, CoreDeleteView): return response -@require_POST -def logout(request: HttpRequest) -> HttpResponseRedirect: - """The view to log out a user. - - Args: - request: The request. - - Returns: - The redirect response. - """ - logout_user(request) - if "next" in request.POST: - request.session["logout"] = True - return redirect(request.POST["next"]) - return redirect("home") - - -@method_decorator(require_GET, name="dispatch") -@method_decorator(login_required, name="dispatch") -class UserListView(ListView): - """The view to list the users.""" - queryset = User.objects.order_by("login_id") - - -@method_decorator(require_GET, name="dispatch") -@method_decorator(login_required, name="dispatch") -class UserView(DetailView): - """The view of a user.""" - def get_object(self, queryset=None): - return self.kwargs["user"] - - -@method_decorator(login_required, name="dispatch") -class UserFormView(FormView): - """The form to create or update a user.""" - model = User - form_class = UserForm - not_modified_message = gettext_noop("This user account was not changed.") - success_message = gettext_noop("This user account was saved successfully.") - - def make_form_from_post(self, post: Dict[str, str]) -> UserForm: - """Creates and returns the form from the POST data.""" - form = UserForm(post) - form.user = self.get_object() - form.current_user = self.request.user - return form - - def make_form_from_model(self, obj: User) -> UserForm: - """Creates and returns the form from a data model.""" - form = UserForm({ - "login_id": obj.login_id, - "name": obj.name, - "is_disabled": obj.is_disabled, - }) - form.user = self.get_object() - form.current_user = self.request.user - return form - - def fill_model_from_form(self, obj: User, form: UserForm) -> None: - """Fills in the data model from the form.""" - obj.login_id = form["login_id"].value() - if form["password"].value() is not None: - obj.set_digest_password( - form["login_id"].value(), form["password"].value()) - obj.name = form["name"].value() - obj.is_disabled = form["is_disabled"].value() - obj.current_user = self.request.user - - def get_success_url(self) -> str: - """Returns the URL on success.""" - return reverse("mia_core:users.detail", args=[self.get_object()], - current_app=self.request.resolver_match.namespace) - - def get_object(self) -> Optional[Model]: - """Returns the current object, or None on a create form.""" - return self.kwargs.get("user") - - -@require_POST -@login_required -def user_delete(request: HttpRequest, user: User) -> HttpResponseRedirect: - """The view to delete an user. - - Args: - request: The request. - user: The user. - - Returns: - The response. - """ - message = None - if user.pk == request.user.pk: - message = gettext_noop("You cannot delete your own account.") - elif user.is_in_use(): - message = gettext_noop( - "You cannot delete this account because it is in use.") - elif user.is_deleted: - message = gettext_noop("This account is already deleted.") - if message is not None: - messages.error(request, message) - return redirect("mia_core:users.detail", user) - user.delete() - message = gettext_noop("This user account was deleted successfully.") - messages.success(request, message) - return redirect("mia_core:users") - - -@method_decorator(login_required, name="dispatch") -class MyAccountFormView(UserFormView): - """The form to update the information of the currently logged-in user.""" - not_modified_message = gettext_noop("Your user account was not changed.") - success_message = gettext_noop("Your user account was saved successfully.") - - def fill_model_from_form(self, obj: User, form: UserForm) -> None: - """Fills in the data model from the form.""" - obj.login_id = form["login_id"].value() - if form["password"].value() is not None: - obj.set_digest_password( - form["login_id"].value(), form["password"].value()) - obj.name = form["name"].value() - obj.current_user = self.request.user - - def get_success_url(self) -> str: - """Returns the URL on success.""" - return reverse("mia_core:my-account", - current_app=self.request.resolver_match.namespace) - - def get_object(self) -> Optional[Model]: - """Finds and returns the current object, or None on a create form.""" - return self.request.user - - -def api_users_exists(request: HttpRequest, login_id: str) -> JsonResponse: - """The view to check whether a user with a log in ID exists. - - Args: - request: The request. - login_id: The log in ID. - - Returns: - The response. - """ - try: - User.objects.get(login_id=login_id) - except User.DoesNotExist: - return JsonResponse(False, safe=False) - return JsonResponse(True, safe=False)