-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SS-643 Users can edit their account details #235
Changes from 24 commits
d321a3f
2b5cadc
2d769be
ab7d54f
46f589c
c958069
7c8099b
4e3de76
d38f4ce
d86a443
32b49eb
bba6f8b
0048d66
cf33450
cded668
8194013
a05bf15
ee23c4e
b3481fd
6bb12aa
41b2862
491b6f8
da00974
f78edf6
7f13f1e
a96fdfd
5a7fa52
0d50532
2d11a65
ce5e4a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -321,3 +321,65 @@ class Meta: | |
fields = [ | ||
"token", | ||
] | ||
|
||
|
||
# SS-643 We've created a new form because UserForm above | ||
# is a UserCreationForm, | ||
# which means 'exclude' in Meta or change in | ||
# initialization won't work | ||
class UserEditForm(BootstrapErrorFormMixin, forms.ModelForm): | ||
first_name = forms.CharField( | ||
min_length=1, | ||
max_length=30, | ||
label="First name", | ||
widget=forms.TextInput(attrs={"class": "form-control"}), | ||
) | ||
last_name = forms.CharField( | ||
min_length=1, | ||
max_length=30, | ||
label="Last name", | ||
widget=forms.TextInput(attrs={"class": "form-control"}), | ||
) | ||
email = forms.EmailField( | ||
max_length=254, | ||
label="Email address", | ||
widget=forms.TextInput(attrs={"class": "form-control"}), | ||
help_text=mark_safe("Email address can not be changed. Please email [email protected] with any questions."), | ||
disabled=True, | ||
) | ||
|
||
required_css_class = "required" | ||
|
||
class Meta: | ||
model = User | ||
fields = [ | ||
"username", | ||
"first_name", | ||
"last_name", | ||
"email", | ||
"password1", | ||
"password2", | ||
] | ||
exclude = [ | ||
"username", | ||
"password1", | ||
"password2", | ||
] | ||
|
||
def __repr__(self) -> str: | ||
return f"{self.__class__.__name__}({self.data})" | ||
|
||
|
||
class ProfileEditForm(ProfileForm): | ||
class Meta(ProfileForm.Meta): | ||
exclude = [ | ||
"note", | ||
"why_account_needed", | ||
] | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.fields["affiliation"].disabled = True | ||
self.fields[ | ||
"affiliation" | ||
].help_text = "Affiliation can not be changed. Please email [email protected] with any questions." |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,15 +1,29 @@ | ||||||
from django.conf import settings | ||||||
from django.contrib import messages | ||||||
from django.contrib.auth import authenticate, login, logout | ||||||
from django.contrib.auth.decorators import login_required | ||||||
from django.core.exceptions import ObjectDoesNotExist | ||||||
from django.core.mail import send_mail | ||||||
from django.db import transaction | ||||||
from django.http.response import HttpResponseRedirect | ||||||
from django.shortcuts import redirect, render | ||||||
from django.shortcuts import HttpResponse, redirect, render | ||||||
from django.urls import reverse_lazy | ||||||
from django.utils.decorators import method_decorator | ||||||
from django.views.generic import CreateView, TemplateView | ||||||
|
||||||
from .forms import ProfileForm, SignUpForm, TokenVerificationForm, UserForm | ||||||
from .models import EmailVerificationTable | ||||||
from studio.utils import get_logger | ||||||
|
||||||
from .forms import ( | ||||||
ProfileEditForm, | ||||||
ProfileForm, | ||||||
SignUpForm, | ||||||
TokenVerificationForm, | ||||||
UserEditForm, | ||||||
UserForm, | ||||||
) | ||||||
from .models import EmailVerificationTable, UserProfile | ||||||
|
||||||
logger = get_logger(__name__) | ||||||
|
||||||
|
||||||
# Create your views here. | ||||||
|
@@ -129,3 +143,119 @@ def post(self, request, *args, **kwargs): | |||||
messages.error(request, "Invalid token!") | ||||||
return redirect("portal:home") | ||||||
return render(request, self.template_name, {"form": form}) | ||||||
|
||||||
|
||||||
@method_decorator(login_required, name="post") | ||||||
class EditProfileView(TemplateView): | ||||||
template_name = "user/profile_edit_form.html" | ||||||
|
||||||
profile_edit_form_class = ProfileEditForm | ||||||
user_edit_form_class = UserEditForm | ||||||
|
||||||
def get_user_profile_info(self, request): | ||||||
# Get the user profile from database | ||||||
try: | ||||||
# Note that not all users have a user profile object | ||||||
# such as the admin superuser | ||||||
user_profile = UserProfile.objects.get(user_id=request.user.id) | ||||||
except ObjectDoesNotExist as e: | ||||||
logger.error(str(e), exc_info=True) | ||||||
user_profile = UserProfile() | ||||||
except Exception as e: | ||||||
logger.error(str(e), exc_info=True) | ||||||
user_profile = UserProfile() | ||||||
Comment on lines
+164
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added some dummy values for the super user to fix it. |
||||||
|
||||||
return user_profile | ||||||
|
||||||
def get(self, request, *args, **kwargs): | ||||||
user_profile_data = self.get_user_profile_info(request) | ||||||
|
||||||
profile_edit_form = self.profile_edit_form_class( | ||||||
initial={"affiliation": user_profile_data.affiliation, "department": user_profile_data.department} | ||||||
) | ||||||
|
||||||
user_edit_form = self.user_edit_form_class( | ||||||
initial={ | ||||||
"email": user_profile_data.user.email, | ||||||
"first_name": user_profile_data.user.first_name, | ||||||
"last_name": user_profile_data.user.last_name, | ||||||
} | ||||||
) | ||||||
|
||||||
return render(request, self.template_name, {"form": user_edit_form, "profile_form": profile_edit_form}) | ||||||
|
||||||
def post(self, request, *args, **kwargs): | ||||||
user_profile_data = self.get_user_profile_info(request) | ||||||
|
||||||
user_form_details = self.user_edit_form_class( | ||||||
request.POST, | ||||||
instance=request.user, | ||||||
initial={ | ||||||
"email": user_profile_data.user.email, | ||||||
}, | ||||||
) | ||||||
|
||||||
profile_form_details = self.profile_edit_form_class( | ||||||
request.POST, | ||||||
instance=user_profile_data, | ||||||
initial={ | ||||||
"affiliation": user_profile_data.affiliation, | ||||||
}, | ||||||
) | ||||||
Comment on lines
+199
to
+213
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make sure, that user cannot pass any other information in the form data. For instance, that user via direct curl request cannot change their email or password. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used decorator to ensure login-required in 'common/urls.py' and in 'EditProfileView' class in 'common/views.py'. |
||||||
|
||||||
if user_form_details.is_valid() and profile_form_details.is_valid(): | ||||||
try: | ||||||
with transaction.atomic(): | ||||||
user_form_retrived_data = user_form_details.save(commit=False) | ||||||
|
||||||
# Only saving the new values, overwriting other existing values | ||||||
if ( | ||||||
user_form_retrived_data.first_name != user_profile_data.user.first_name | ||||||
or user_form_retrived_data.last_name != user_profile_data.user.last_name | ||||||
): | ||||||
user_form_retrived_data.username = user_profile_data.user.username | ||||||
user_form_retrived_data.email = user_profile_data.user.email | ||||||
user_form_retrived_data.password1 = user_profile_data.user.password | ||||||
user_form_retrived_data.password2 = user_profile_data.user.password | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't fully get it, why are we doing this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to save only the new information. I removed it, as I checked the the previous way is okay to prevent the curl injection. |
||||||
user_form_retrived_data.save() | ||||||
|
||||||
else: | ||||||
logger.info("Not saving user form info as nothing has changed", exc_info=True) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Correct me if I'm wrong, but I don't think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. I removed it. |
||||||
|
||||||
profile_form_retrived_data = profile_form_details.save(commit=False) | ||||||
|
||||||
# Only saving the new values, overwriting other existing values | ||||||
profile_form_retrived_data.affiliation = user_profile_data.affiliation | ||||||
profile_form_retrived_data.deleted_on = user_profile_data.deleted_on | ||||||
profile_form_retrived_data.why_account_needed = user_profile_data.why_account_needed | ||||||
profile_form_retrived_data.save() | ||||||
|
||||||
# profile_form_details.save_m2m() | ||||||
# user_form_details.save() | ||||||
# profile_form_details.save() | ||||||
anondo1969 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
logger.info( | ||||||
"Updated First Name: " + str(self.get_user_profile_info(request).user.first_name), exc_info=True | ||||||
) | ||||||
logger.info( | ||||||
"Updated Last Name: " + str(self.get_user_profile_info(request).user.last_name), exc_info=True | ||||||
) | ||||||
logger.info( | ||||||
"Updated Department: " + str(self.get_user_profile_info(request).department), exc_info=True | ||||||
) | ||||||
|
||||||
except Exception as e: | ||||||
return HttpResponse("Error updating records: " + str(e)) | ||||||
|
||||||
return render(request, "user/profile.html", {"user_profile": self.get_user_profile_info(request)}) | ||||||
|
||||||
else: | ||||||
if not user_form_details.is_valid(): | ||||||
logger.error("Edit user error: " + str(user_form_details.errors), exc_info=True) | ||||||
|
||||||
if not profile_form_details.is_valid(): | ||||||
logger.error("Edit profile error: " + str(profile_form_details.errors), exc_info=True) | ||||||
|
||||||
return render( | ||||||
request, self.template_name, {"form": user_form_details, "profile_form": profile_form_details} | ||||||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you define
login_required
here and incommon/views.py
?Just curious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to make sure only logged-in user can access the view/form. I removed in the last commit.