Added the user form, and revised the text in the user list and user detail in the Mia core application.
This commit is contained in:
parent
7596935ca2
commit
d7ddee340b
161
mia_core/forms.py
Normal file
161
mia_core/forms.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# The core application of the Mia project.
|
||||||
|
# by imacat <imacat@mail.imacat.idv.tw>, 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()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(UserForm, self).__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_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):
|
||||||
|
"""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_required(self):
|
||||||
|
"""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_password2_required(self):
|
||||||
|
"""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):
|
||||||
|
"""Validates whether the two passwords are equa.
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""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
|
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: mia 1.0\n"
|
"Project-Id-Version: mia 1.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-08-09 20:48+0800\n"
|
"POT-Creation-Date: 2020-08-09 22:03+0800\n"
|
||||||
"PO-Revision-Date: 2020-08-09 20:48+0800\n"
|
"PO-Revision-Date: 2020-08-09 22:05+0800\n"
|
||||||
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
||||||
"Language-Team: Traditional Chinese <imacat@mail.imacat.idv.tw>\n"
|
"Language-Team: Traditional Chinese <imacat@mail.imacat.idv.tw>\n"
|
||||||
"Language: Traditional Chinese\n"
|
"Language: Traditional Chinese\n"
|
||||||
@ -16,6 +16,46 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\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:90
|
||||||
|
msgid "This log in ID is already in use."
|
||||||
|
msgstr "登入帳號和其他人重複。"
|
||||||
|
|
||||||
|
#: mia_core/forms.py:105
|
||||||
|
msgid "Please fill in the password."
|
||||||
|
msgstr "請填寫密碼。"
|
||||||
|
|
||||||
|
#: mia_core/forms.py:121
|
||||||
|
msgid "Please enter the password again to verify it."
|
||||||
|
msgstr "請再次確認密碼。"
|
||||||
|
|
||||||
|
#: mia_core/forms.py:138
|
||||||
|
msgid "The two passwords do not match."
|
||||||
|
msgstr "兩次密碼不符,請重新輸入。"
|
||||||
|
|
||||||
|
#: mia_core/forms.py:158 mia_core/templates/mia_core/user_form.html:84
|
||||||
|
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: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:513 mia_core/period.py:559
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -133,6 +173,7 @@ msgid "Cancel"
|
|||||||
msgstr "取消"
|
msgstr "取消"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_detail.html:69
|
#: mia_core/templates/mia_core/user_detail.html:69
|
||||||
|
#: mia_core/templates/mia_core/user_form.html:41
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr "回上頁"
|
msgstr "回上頁"
|
||||||
|
|
||||||
@ -160,14 +201,16 @@ msgid "This account is already deleted."
|
|||||||
msgstr "帳號已刪除。"
|
msgstr "帳號已刪除。"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_detail.html:99
|
#: mia_core/templates/mia_core/user_detail.html:99
|
||||||
msgid "Login ID.:"
|
msgid "Log in ID.:"
|
||||||
msgstr "帳號"
|
msgstr "登入帳號:"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_detail.html:104
|
#: mia_core/templates/mia_core/user_detail.html:104
|
||||||
|
#: mia_core/templates/mia_core/user_form.html:74
|
||||||
msgid "Name:"
|
msgid "Name:"
|
||||||
msgstr "姓名"
|
msgstr "姓名:"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_detail.html:110
|
#: mia_core/templates/mia_core/user_detail.html:110
|
||||||
|
#: mia_core/templates/mia_core/user_form.html:88
|
||||||
msgid "This account is disabled."
|
msgid "This account is disabled."
|
||||||
msgstr "帳號停用。"
|
msgstr "帳號停用。"
|
||||||
|
|
||||||
@ -219,6 +262,26 @@ msgstr "更新時間:"
|
|||||||
msgid "Updated by:"
|
msgid "Updated by:"
|
||||||
msgstr "更新人:"
|
msgstr "更新人:"
|
||||||
|
|
||||||
|
#: mia_core/templates/mia_core/user_form.html:31
|
||||||
|
msgid "Add a New Account"
|
||||||
|
msgstr "建立帳號"
|
||||||
|
|
||||||
|
#: mia_core/templates/mia_core/user_form.html:50
|
||||||
|
msgid "Log in ID:"
|
||||||
|
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:97
|
||||||
|
msgid "Submit"
|
||||||
|
msgstr "傳送"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_list.html:27
|
#: mia_core/templates/mia_core/user_list.html:27
|
||||||
msgid "Account Management"
|
msgid "Account Management"
|
||||||
msgstr "帳號管理"
|
msgstr "帳號管理"
|
||||||
@ -228,8 +291,8 @@ msgid "New"
|
|||||||
msgstr "新增"
|
msgstr "新增"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_list.html:43
|
#: mia_core/templates/mia_core/user_list.html:43
|
||||||
msgid "Login ID"
|
msgid "Log in ID"
|
||||||
msgstr "帳號"
|
msgstr "登入帳號"
|
||||||
|
|
||||||
#: mia_core/templates/mia_core/user_list.html:44
|
#: mia_core/templates/mia_core/user_list.html:44
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
|
46
mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po
Normal file
46
mia_core/locale/zh_Hant/LC_MESSAGES/djangojs.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# 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 <imacat@mail.imacat.idv.tw>, 2020.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: core 1.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-08-09 21:54+0800\n"
|
||||||
|
"PO-Revision-Date: 2020-08-09 22:05+0800\n"
|
||||||
|
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
||||||
|
"Language-Team: Traditional Chinese <imacat@mail.imacat.idv.tw>\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:129
|
||||||
|
msgid "Please fill in the log in ID."
|
||||||
|
msgstr "請填寫登入帳號。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:134
|
||||||
|
msgid "You cannot use slash (/) in the log in ID."
|
||||||
|
msgstr "登入帳號不可以包含斜線 (/) 。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:154
|
||||||
|
msgid "This log in ID is already in use."
|
||||||
|
msgstr "登入帳號和其他人重複。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:177
|
||||||
|
msgid "Please fill in the password."
|
||||||
|
msgstr "請填寫密碼。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:201
|
||||||
|
msgid "Please enter the password again to verify it."
|
||||||
|
msgstr "請再次確認密碼。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:207
|
||||||
|
msgid "The two passwords do not match."
|
||||||
|
msgstr "兩次密碼不符,請重新輸入。"
|
||||||
|
|
||||||
|
#: mia_core/static/mia_core/js/user-form.js:228
|
||||||
|
msgid "Please fill in the name."
|
||||||
|
msgstr "請填寫姓名。"
|
234
mia_core/static/mia_core/js/user-form.js
Normal file
234
mia_core/static/mia_core/js/user-form.js
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/* 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();
|
||||||
|
});
|
||||||
|
$("#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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
* 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<void>}
|
||||||
|
* @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");
|
||||||
|
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;
|
||||||
|
}
|
@ -96,7 +96,7 @@ First written: 2020/8/9
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="col-sm-2">{{ _("Login ID.:")|force_escape }}</div>
|
<div class="col-sm-2">{{ _("Log in ID.:")|force_escape }}</div>
|
||||||
<div class="col-sm-10">{{ user.login_id }}</div>
|
<div class="col-sm-10">{{ user.login_id }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
103
mia_core/templates/mia_core/user_form.html
Normal file
103
mia_core/templates/mia_core/user_form.html
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
{% 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 %}
|
||||||
|
|
||||||
|
<div class="btn-group btn-actions">
|
||||||
|
<a class="btn btn-primary" role="button" href="{% if form.user %}{% url "mia_core:users.detail" user %}{% else %}{% url "mia_core:users" %}{% endif %}">
|
||||||
|
<i class="fas fa-chevron-circle-left"></i>
|
||||||
|
{{ _("Back")|force_escape }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="user-form" action="{% if form.user %}{% url "mia_core:users.update" user %}{% else %}{% url "mia_core:users.store" %}{% endif %}" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input id="exists-url" type="hidden" value="{% url "mia_core:api.users.exists" "ID" %}" />
|
||||||
|
<input id="user-login-id-original" type="hidden" value="{{ form.user.login_id }}" />
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-sm-2 col-form-label" for="user-login-id">{{ _("Log in ID:")|force_escape }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="user-login-id" class="form-control {% if form.login_id.errors %} is-invalid {% endif %}" type="text" name="login-id" value="{{ form.login_id.value|default:"" }}" maxlength="32" required="required" />
|
||||||
|
<div id="user-login-id-error" class="invalid-feedback">{{ form.login_id.errors.0 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-sm-2 col-form-label" for="user-password">{{ _("Password:")|force_escape }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="user-password" class="form-control {% if form.password.errors %} is-invalid {% endif %}" type="password" name="password" value="" {% if not form.user %} required="required" {% endif %} />
|
||||||
|
<div id="user-password-error" class="invalid-feedback">{{ form.password.errors.0 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-sm-2 col-form-label" for="user-password2">{{ _("Confirm password:")|force_escape }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="user-password2" class="form-control {% if form.password2.errors %} is-invalid {% endif %}" type="password" name="password2" value="" {% if not form.user %} required="required" {% endif %} />
|
||||||
|
<div id="user-password2-error" class="invalid-feedback">{{ form.password2.errors.0 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<label class="col-sm-2 col-form-label" for="user-name">{{ _("Name:")|force_escape }}</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="user-name" class="form-control {% if form.name.errors %} is-invalid {% endif %}" type="text" name="name" value="{{ form.name.value|default:"" }}" maxlength="32" required="required" />
|
||||||
|
<div id="user-name-error" class="invalid-feedback">{{ form.name.errors.0 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group form-check">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
{% if form.user and form.user.pk == request.user.pk %}
|
||||||
|
{{ _("You cannot disable your own account.")|force_escape }}
|
||||||
|
{% else %}
|
||||||
|
<input id="user-is-disabled" class="form-check-input" type="checkbox" name="is_disabled" value="1" {% if form.is_disabled.value %} checked="checked" {% endif %} />
|
||||||
|
<label class="form-check-label" for="user-is-disabled">
|
||||||
|
{{ _("This account is disabled.")|force_escape }}
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
{{ _("Submit")|force_escape }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -40,7 +40,7 @@ First written: 2020/8/9
|
|||||||
<table id="users" class="table table-striped table-hover">
|
<table id="users" class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">{{ _("Login ID")|force_escape }}</th>
|
<th scope="col">{{ _("Log in ID")|force_escape }}</th>
|
||||||
<th scope="col">{{ _("Name")|force_escape }}</th>
|
<th scope="col">{{ _("Name")|force_escape }}</th>
|
||||||
<th class="actions" scope="col">{{ _("View")|force_escape }}</th>
|
<th class="actions" scope="col">{{ _("View")|force_escape }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -27,19 +27,17 @@ register_converter(converters.UserConverter, "user")
|
|||||||
app_name = "mia_core"
|
app_name = "mia_core"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("users", views.UserListView.as_view(), name="users"),
|
path("users", views.UserListView.as_view(), name="users"),
|
||||||
# TODO: To be done.
|
path("users/create", views.user_form, name="users.create"),
|
||||||
path("users/create", views.todo, name="users.create"),
|
|
||||||
# TODO: To be done.
|
# TODO: To be done.
|
||||||
path("users/store", views.todo, name="users.store"),
|
path("users/store", views.todo, name="users.store"),
|
||||||
path("users/<user:user>", views.UserView.as_view(), name="users.detail"),
|
path("users/<user:user>", views.UserView.as_view(), name="users.detail"),
|
||||||
# TODO: To be done.
|
path("users/<user:user>/edit", views.user_form, name="users.edit"),
|
||||||
path("users/<user:user>/edit", views.todo, name="users.edit"),
|
|
||||||
# TODO: To be done.
|
# TODO: To be done.
|
||||||
path("users/<user:user>/update", views.todo, name="users.update"),
|
path("users/<user:user>/update", views.todo, name="users.update"),
|
||||||
# TODO: To be done.
|
# TODO: To be done.
|
||||||
path("users/<user:user>/delete", views.todo, name="users.delete"),
|
path("users/<user:user>/delete", views.todo, name="users.delete"),
|
||||||
# TODO: To be done.
|
# TODO: To be done.
|
||||||
path("api/users/<str:login_id>/exists", views.todo,
|
path("api/users/<str:login_id>/exists", views.api_users_exists,
|
||||||
name="api.users.exists"),
|
name="api.users.exists"),
|
||||||
# TODO: To be done.
|
# TODO: To be done.
|
||||||
path("my-account", views.todo, name="my-account"),
|
path("my-account", views.todo, name="my-account"),
|
||||||
|
@ -21,13 +21,16 @@
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import logout as logout_user
|
from django.contrib.auth import logout as logout_user
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, render
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST, require_GET
|
||||||
from django.views.generic import DeleteView as CoreDeleteView, ListView, \
|
from django.views.generic import DeleteView as CoreDeleteView, ListView, \
|
||||||
DetailView
|
DetailView
|
||||||
|
|
||||||
from mia_core.models import User
|
from . import stored_post
|
||||||
|
from .digest_auth import login_required
|
||||||
|
from .forms import UserForm
|
||||||
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
class DeleteView(SuccessMessageMixin, CoreDeleteView):
|
class DeleteView(SuccessMessageMixin, CoreDeleteView):
|
||||||
@ -67,6 +70,54 @@ class UserView(DetailView):
|
|||||||
return self.request.resolver_match.kwargs["user"]
|
return self.request.resolver_match.kwargs["user"]
|
||||||
|
|
||||||
|
|
||||||
|
@require_GET
|
||||||
|
@login_required
|
||||||
|
def user_form(request, user=None):
|
||||||
|
"""The view to edit an accounting transaction.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The request.
|
||||||
|
user (User): The account.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HttpResponse: The response.
|
||||||
|
"""
|
||||||
|
previous_post = stored_post.get_previous_post(request)
|
||||||
|
if previous_post is not None:
|
||||||
|
form = UserForm(previous_post)
|
||||||
|
elif user is not None:
|
||||||
|
form = UserForm({
|
||||||
|
"login_id": user.login_id,
|
||||||
|
"name": user.name,
|
||||||
|
"is_disabled": user.is_disabled,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
form = UserForm()
|
||||||
|
form.user = user
|
||||||
|
form.current_user = request.user
|
||||||
|
return render(request, "mia_core/user_form.html", {
|
||||||
|
"form": form,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def api_users_exists(request, login_id):
|
||||||
|
"""The view to check whether a user with a log in ID exists.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The request.
|
||||||
|
login_id (str): The log in ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JsonResponse: The response.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
User.objects.get(login_id=login_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return JsonResponse(False, safe=False)
|
||||||
|
return JsonResponse(True, safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: To be removed.
|
# TODO: To be removed.
|
||||||
def todo(request, **kwargs):
|
def todo(request, **kwargs):
|
||||||
"""A dummy placeholder view for the URL settings that are not
|
"""A dummy placeholder view for the URL settings that are not
|
||||||
|
Loading…
Reference in New Issue
Block a user