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

Muqadim/user management funtionalaties #1

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
347 changes: 291 additions & 56 deletions README.md
Abdul-Muqadim-Arbisoft marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

26 changes: 17 additions & 9 deletions UniManage/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,11 @@
'user',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


ROOT_URLCONF = 'UniManage.urls'


TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
Expand Down Expand Up @@ -122,3 +115,18 @@
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'user.middleware.CustomAuthenticationMiddleware',

]

AUTH_USER_MODEL = 'user.CustomUser'
AUTHENTICATION_BACKENDS = ['user.backends.EmailBackend']
10 changes: 8 additions & 2 deletions UniManage/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from user.admin_site import admin_site
from django.views.generic import RedirectView
from django.contrib import admin
from django.urls import path
from django.contrib.auth.decorators import login_required



urlpatterns = [
path('admin/', admin.site.urls),
path('admin/', admin_site.urls),
path('user/', include('user.urls')),
]
Binary file removed db.sqlite3
Binary file not shown.
20 changes: 18 additions & 2 deletions user/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
from .admin_site import admin_site

# Register your models here.

class CustomUserAdmin(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username','email','description', 'father_name', 'password1', 'password2'), # include other fields of CustomUser
}),
)
fieldsets = (
(None, {'fields': ('username','email','description', 'father_name','password')}), # include other fields of CustomUser
)
list_display = ('email', 'description','username','father_name') # display fields in the user list page


admin_site.register(CustomUser, CustomUserAdmin)
7 changes: 7 additions & 0 deletions user/admin_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# admin_forms.py
from django import forms
from django.contrib.admin.forms import AdminAuthenticationForm


class CustomAdminAuthenticationForm(AdminAuthenticationForm):
username = forms.EmailField(label='Email', max_length=255)
11 changes: 11 additions & 0 deletions user/admin_site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# admin_site.py
from django.contrib.admin.sites import AdminSite
from .admin_forms import CustomAdminAuthenticationForm


class CustomAdminSite(AdminSite):
login_form = CustomAdminAuthenticationForm


# Instantiate CustomAdminSite
admin_site = CustomAdminSite(name='custom_admin')
29 changes: 29 additions & 0 deletions user/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend


class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, email=None, password=None, **kwargs):
UserModel = get_user_model()

# Prioritize email if provided; otherwise, use username
lookup_value = email or username

print(f"Trying to authenticate: {lookup_value=}, {password=}")
try:
if "@" in lookup_value:
user = UserModel.objects.get(email=lookup_value)
print("Found a user with the provided email")
else:
user = UserModel.objects.get(username=lookup_value)
print("Found a user with the provided username")
except UserModel.DoesNotExist:
print("No user found with the provided email/username")
return None
else:
if user.check_password(password):
print("Password is correct for the user")
return user

print("Password is incorrect")
return None
35 changes: 35 additions & 0 deletions user/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
from utils.constants import USER_FORM_FIELDS
from utils.helpers import validate_password


class CustomUserCreationForm(UserCreationForm):
"""
Form for creating a new CustomUser.

This form inherits from Django's built-in UserCreationForm and adds
additional fields for the CustomUser.
"""

class Meta:
model = CustomUser
fields = USER_FORM_FIELDS

def clean_password1(self):
"""Custom validation for the password1 field."""
password = self.cleaned_data.get('password1')
return validate_password(password)


class EditProfileForm(forms.ModelForm):
"""
Form for editing a CustomUser's profile.

This form uses the CustomUser model.
"""

class Meta:
model = CustomUser
fields = USER_FORM_FIELDS
37 changes: 37 additions & 0 deletions user/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from utils.helpers import redirect_if_unauthenticated


class CustomAuthenticationMiddleware:
"""
Middleware to enforce authentication for specific view classes.

This middleware checks if the view class handling the request is listed
in the PROTECTED_VIEWS list. If it is, and the user is not authenticated,
the user will be redirected to the login page.
"""

def __init__(self, get_response):
"""
Constructor for the middleware.

:param get_response: A function to process the request and return the response.
"""
self.get_response = get_response

def __call__(self, request):
"""
Method to handle the request.

:param request: The HTTP request object.
:return: The HTTP response object.
"""
return self.get_response(request)

@staticmethod
def process_view(request, view_func, view_args, view_kwargs):
"""
Process the request before calling the view function.
Check for whether the called URL is allowed to be used by a non-authenticated user.
If not, check when the user is authenticated.
"""
return redirect_if_unauthenticated(request, view_func.__name__)
31 changes: 30 additions & 1 deletion user/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from utils.constants import REQUIRED_USER_FIELDS
from utils.helpers import update_user_profile_fields

# Create your models here.

class CustomUser(AbstractUser):
"""
Custom user model that extends Django's built-in AbstractUser class.

This model includes the default user fields provided by AbstractUser and adds custom
fields for the user's email address, father's name, description, software engineering experience,
and the timestamp of the last profile update.
"""
email = models.EmailField('email address', unique=True)
father_name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
software_engineering_experience = models.PositiveIntegerField(null=True, blank=True)
last_profile_update = models.DateTimeField(null=True, blank=True)

REQUIRED_FIELDS = REQUIRED_USER_FIELDS

def save(self, *args, **kwargs):
"""
Override the save method to update custom fields.
"""
update_user_profile_fields(self)
super().save(*args, **kwargs)

def __str__(self):
"""Return the email address as the string representation of the user."""
return self.email
14 changes: 14 additions & 0 deletions user/templates/user/change_password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Change Password</title>
</head>
<body>
<h2>Change Password</h2>
<form method="post">
{% csrf_token %} <!-- CSRF token for security -->
{{ form.as_p }} <!-- Django form rendered as a series of paragraphs, including labels, input widgets, and error messages -->
<button type="submit">Change Password</button>
</form>
</body>
</html>
14 changes: 14 additions & 0 deletions user/templates/user/edit_profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Edit Profile</title>
</head>
<body>
<h2>Edit Profile</h2>
<form method="post">
{% csrf_token %} <!-- CSRF token for security -->
{{ form.as_p }} <!-- Django form rendered as a series of paragraphs, including labels, input widgets, and error messages -->
<button type="submit">Update</button>
</form>
</body>
</html>
18 changes: 18 additions & 0 deletions user/templates/user/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<h2>Welcome, {{ person.username }}!</h2> <!-- Dynamically display the username -->
<p>Email: {{ user.email }}</p> <!-- Dynamically display the email -->
<p>Father's Name: {{ user.father_name }}</p> <!-- Dynamically display the father's name -->
<p>Description: {{ user.description }}</p> <!-- Dynamically display the description -->
<p>Software Engineering Experience: {{ user.software_engineering_experience }}</p> <!-- Dynamically display the software engineering experience -->
<p>Last Profile Update: {{ user.last_profile_update }}</p> <!-- Dynamically display the last profile update time -->

<a href="{% url 'edit_profile' %}">Edit Profile</a> <!-- Link to edit profile -->
<a href="{% url 'change_password' %}">Change Password</a> <!-- Link to change password -->
<a href="{% url 'logout' %}">Logout</a> <!-- Link to logout -->
</body>
</html>
21 changes: 21 additions & 0 deletions user/templates/user/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form method="post">
{% csrf_token %} <!-- CSRF token for security -->
<label for="email_or_username">Email OR Username:</label>
<input type="email_or_username" name="email_or_username" required><br>
<label for="password">Password:</label>
<input type="password" name="password" required><br>
<button type="submit">Login</button>
</form>
{% if error %} <!-- Conditionally display an error message -->
<p><strong>{{ error }}</strong></p>
{% endif %}
<p>Don't have an account? <a href="{% url 'signup' %}">Signup</a></p> <!-- Link to the signup page -->
</body>
</html>
15 changes: 15 additions & 0 deletions user/templates/user/signup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Signup</title>
</head>
<body>
<h2>Signup</h2>
<form method="post">
{% csrf_token %} <!-- CSRF token for security -->
{{ form.as_p }} <!-- Render the form fields using Django's built-in form rendering -->
<button type="submit">Signup</button>
</form>
<p>Already have an account? <a href="{% url 'login' %}">Login</a></p> <!-- Link to the login page -->
</body>
</html>
12 changes: 12 additions & 0 deletions user/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.urls import path
from .views import SignupView, LoginView, LogoutView, EditProfileView, ChangePasswordView, HomeView

# URL patterns for the user management functionalities
urlpatterns = [
path('login/', LoginView.as_view(), name='login'), # URL pattern for user login
Abdul-Muqadim-Arbisoft marked this conversation as resolved.
Show resolved Hide resolved
path('signup/', SignupView.as_view(), name='signup'), # URL pattern for user registration
path('logout/', LogoutView.as_view(), name='logout'), # URL pattern for user logout
path('edit_profile/', EditProfileView.as_view(), name='edit_profile'), # URL pattern for editing user profiles
path('change_password/', ChangePasswordView.as_view(), name='change_password'), # URL for changing user passwords
path('home/', HomeView.as_view(), name='home'), # URL pattern for rendering the home page
]
Loading