Skip to content
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

Merged
merged 30 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d321a3f
Updated info about using Serve for teaching (#194)
akochari Apr 19, 2024
2b5cadc
Revert "Updated info about using Serve for teaching (#194)"
akochari Apr 19, 2024
2d769be
Merge branch 'staging' into main
sandstromviktor May 23, 2024
ab7d54f
Merge branch 'staging'
churnikov Aug 14, 2024
46f589c
Merge branch 'staging'
churnikov Sep 19, 2024
c958069
SS-643-Make-an-option-to-edit-account-details
anondo1969 Oct 7, 2024
7c8099b
SS-643-Make-an-option-to-edit-account-details
anondo1969 Oct 7, 2024
4e3de76
SS-643 extending ProfileForm
anondo1969 Oct 8, 2024
d38f4ce
SS-643 Updated edit HTML form
anondo1969 Oct 10, 2024
d86a443
SS-643 Updated edit HTML form
anondo1969 Oct 10, 2024
32b49eb
SS-643 pre-commit check fixed
anondo1969 Oct 10, 2024
bba6f8b
SS-643 pre-commit with black check
anondo1969 Oct 10, 2024
0048d66
SS-643 pre-commit with black check 2
anondo1969 Oct 10, 2024
cf33450
Merge branch 'develop' into SS-643-Make-an-option-to-edit-account-det…
anondo1969 Oct 14, 2024
cded668
fix failing e2e test
akochari Oct 14, 2024
8194013
change the edit profile icon
akochari Oct 14, 2024
a05bf15
uncomment the line I accidentally commented out
akochari Oct 14, 2024
ee23c4e
Update common/views.py
anondo1969 Oct 15, 2024
b3481fd
Update common/forms.py
anondo1969 Oct 15, 2024
6bb12aa
Update common/forms.py
anondo1969 Oct 15, 2024
41b2862
fix super class init.
anondo1969 Oct 15, 2024
491b6f8
changes in view
anondo1969 Oct 15, 2024
da00974
ensure login required in form post method
anondo1969 Oct 15, 2024
f78edf6
Merge branch 'develop' into SS-643-Make-an-option-to-edit-account-det…
anondo1969 Oct 15, 2024
7f13f1e
ensuring curl injection does not work
anondo1969 Oct 17, 2024
a96fdfd
fixing the profile-edit bug in superuser mode
anondo1969 Oct 21, 2024
5a7fa52
Merge branch 'develop' into SS-643-Make-an-option-to-edit-account-det…
anondo1969 Oct 21, 2024
0d50532
Merge branch 'develop' into SS-643-Make-an-option-to-edit-account-det…
anondo1969 Oct 21, 2024
2d11a65
differentiate admin user and common user with or without Staff/Superu…
anondo1969 Oct 22, 2024
ce5e4a8
differentiate admin user and common user with or without Staff/Superu…
anondo1969 Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions common/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from common.models import EmailVerificationTable, UserProfile
from studio.utils import get_logger

from django.utils.translation import gettext_lazy as _

logger = get_logger(__name__)


Expand Down Expand Up @@ -113,6 +115,7 @@ class UserForm(BootstrapErrorFormMixin, UserCreationForm):
"Swedish university</a> email address. If you are not affiliated with a Swedish university, "
"your account request will be reviewed manually."
),
#disabled = True,
)
password1 = forms.CharField(
min_length=8,
Expand Down Expand Up @@ -321,3 +324,125 @@ class Meta:
fields = [
"token",
]


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",
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(BootstrapErrorFormMixin, forms.ModelForm):
affiliation = forms.ChoiceField(
widget=forms.Select(attrs={"class": "form-control"}),
label="University",
choices=UNIVERSITIES,
help_text="Affiliation can not be changed. Please email [email protected] with any questions.",
disabled = True,
)
department = forms.CharField(
widget=ListTextWidget(data_list=DEPARTMENTS, name="department-list", attrs={"class": "form-control"}),
label="Department",
required=False,
help_text="Select closest department name or enter your own.",
)

required_css_class = "required"

class Meta:
model = UserProfile
fields = [
"affiliation",
"department",
"note",
"why_account_needed",
]
exclude = [
"note",
"why_account_needed",
]

def __repr__(self):
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(ProfileEditForm, self).__init__(*args, **kwargs)
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved
self.fields["affiliation"].disabled=True
#self.fields["affiliation"].required = False
self.fields["affiliation"].help_text="Affiliation can not be changed. Please email [email protected] with any questions."

'''
class UserEditForm(UserForm):


class Meta(UserForm.Meta):

exclude = [
"username",
"password1",
"password2",
]
def __init__(self, *args, **kwargs):
super(UserEditForm, self).__init__(*args, **kwargs)
self.fields["email"].disabled=True
self.fields["email"].help_text="Email address can not be changed. Please email [email protected] with any questions."
del self.fields["password1"]
del self.fields["password2"]
self.fields["email"].required = False
'''






1 change: 1 addition & 0 deletions common/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
path("success/", views.RegistrationCompleteView.as_view(), name="success"),
path("signup/", views.SignUpView.as_view(), name="signup"),
path("verify/", views.VerifyView.as_view(), name="verify"),
path("edit-profile/", views.EditProfileView.as_view(), name="edit-profile"),
]
91 changes: 88 additions & 3 deletions common/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
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 redirect, render, HttpResponse
from django.urls import reverse_lazy
from django.views.generic import CreateView, TemplateView
from django.views.generic.edit import UpdateView
from django.core.exceptions import ObjectDoesNotExist
from studio.utils import get_logger

from .forms import ProfileForm, SignUpForm, TokenVerificationForm, UserForm
from .models import EmailVerificationTable
from .forms import ProfileForm, SignUpForm, TokenVerificationForm, UserForm, ProfileEditForm, UserEditForm
from .models import EmailVerificationTable, UserProfile

logger = get_logger(__name__)

# Create your views here.
class HomeView(TemplateView):
Expand Down Expand Up @@ -129,3 +133,84 @@ def post(self, request, *args, **kwargs):
messages.error(request, "Invalid token!")
return redirect("portal:home")
return render(request, self.template_name, {"form": form})


class EditProfileView(TemplateView):
"""

"""
template_name = "registration/edit_profile.html"

profile_edit_form_class = ProfileEditForm
user_edit_form_class= UserEditForm

def get_user_profile_info(self, request):
# Get the user profile
try:
# Note that not all users have a user profile object
# such as the admin superuser
user_profile_data = 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, just want to tell you that it doesn't solve it for the admin user.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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_data


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,})

if user_form_details.is_valid() and profile_form_details.is_valid():

user_form_details.save()
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved
profile_form_details.save()


#logger.info(user_form_details.cleaned_data, exc_info=True)

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)

return render(request, "registration/edit_profile_done.html")
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved

else:

# Redirect back to the same page if the data
# was invalid

#print (form.errors)
if user_form_details.is_valid()==False:
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved
logger.error("Edit user error: " + str(user_form_details.errors), exc_info=True)
if profile_form_details.is_valid()==False:
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved
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})
1 change: 1 addition & 0 deletions templates/common/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'user-profile' %}"><i class="bi bi-person me-1"></i>My profile</a></li>
<li><a class="dropdown-item" href="{% url 'common:edit-profile' %}"><i class="bi bi-person me-1"></i>Edit profile</a></li>
<li><a class="dropdown-item" href="{% url 'password_change' %}"><i class="bi bi-key me-1"></i>Change password</a></li>
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
Expand Down
118 changes: 118 additions & 0 deletions templates/registration/edit_profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{% extends 'base.html' %}
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved

{% block title %}Edit your Profile{% endblock %}

{% load static %}
{% load is_login_signup_disabled %}
{% block extra_scripts %}
<script src="{% static 'js/form-helpers.js' %}"></script>
{% endblock %}


{% block content %}
<div class="container">
{% if not maintenance_mode|is_login_signup_disabled %}
<div class="card border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-xs-12 col-lg-8 offset-lg-2">
<div class="px-3 py-5">
<div class="row">
<div class="col">
<div class="col">
<h2 class="text-dark mb-4 text-center">Edit your Profile</h2>
</div>
</div>
</div>

<div class="mw-30 m-auto pt-1">
<form method="post">
{% csrf_token %}
<div class="row pb-3">
<div class="col-12">
<label class="form-label">{{form.email.label_tag}}</label>
{{form.email}}
<div class="form-text">{{form.email.help_text}}</div>
{% if form.email.errors %}
<div id="validation_email" class="invalid-feedback pt-1">
anondo1969 marked this conversation as resolved.
Show resolved Hide resolved
{% for error in form.email.errors %}
<p class="m-0">{{error|escape}}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="row pb-3">
<div class="col-12 col-md-6 pb-3 pb-md-0">
<label class="form-label">{{form.first_name.label_tag}}</label>
{{form.first_name}}
{% if form.first_name.errors %}
<div class="form-text">{{form.first_name.help_text}}</div>
<div id="validation_first_name" class="invalid-feedback pt-1">
{% for error in form.first_name.errors %}
<p class="m-0">{{error|escape}}</p>
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-12 col-md-6 pb-3 pb-md-0">
<label class="form-label">{{form.last_name.label_tag}} </label>
{{form.last_name}}
<div class="form-text">{{form.last_name.help_text}}</div>
{% if form.last_name.errors %}
<div id="validation_last_name" class="pt-1 invalid-feedback">
{% for error in form.last_name.errors %}
<p class="m-0">{{error|escape}}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>

<div class="row pb-3">
<div class="col-12 col-md-6 pb-3 pb-md-0">
<label class="form-label">{{profile_form.affiliation.label_tag}}</label>
{{ profile_form.affiliation }}
<div class="form-text">{{profile_form.affiliation.help_text}}</div>
{% if profile_form.affiliation.errors %}
<div id="validation_affiliation" class="pt-1 invalid-feedback">
{% for error in profile_form.affiliation.errors %}
<p class="m-0">{{error|escape}}</p>
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-12 col-md-6 pb-3 pb-md-0">
<label class="form-label">{{profile_form.department.label_tag}}</label>
{{ profile_form.department | safe }}
<div class="form-text">{{profile_form.department.help_text}}</div>
{% if profile_form.department.errors %}
<div id="validation_department" class="pt-1 invalid-feedback">
{% for error in profile_form.department.errors %}
<p class="m-0">{{error|escape}}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="d-flex justify-content-end">

<a href="/" class="btn me-2">Cancel</a>
<input type="submit" name="save" value="Submit" class="btn btn-primary ms-2"
id="submit-id-save">
</div>
</div>
</form>
</div>

<hr>
</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}
Loading
Loading