V1
This commit is contained in:
parent
e8065d5378
commit
3c67ef4f81
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
BIN
core/__pycache__/tokens.cpython-311.pyc
Normal file
BIN
core/__pycache__/tokens.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
22
core/forms.py
Normal file
22
core/forms.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
|
||||
class SignupForm(UserCreationForm):
|
||||
email = forms.EmailField(required=True, help_text="Required. A valid email address.")
|
||||
terms_accepted = forms.BooleanField(
|
||||
required=True,
|
||||
label="I accept the standard terms and conditions",
|
||||
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||||
)
|
||||
|
||||
class Meta(UserCreationForm.Meta):
|
||||
model = User
|
||||
fields = UserCreationForm.Meta.fields + ('email',)
|
||||
|
||||
def save(self, commit=True):
|
||||
user = super().save(commit=False)
|
||||
user.email = self.cleaned_data["email"]
|
||||
if commit:
|
||||
user.save()
|
||||
return user
|
||||
14
core/templates/core/emails/activation_email.html
Normal file
14
core/templates/core/emails/activation_email.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% autoescape off %}
|
||||
Hi {{ user.username }},
|
||||
|
||||
Welcome to Referral Rewards!
|
||||
|
||||
Please click on the link below to confirm your registration and activate your account:
|
||||
|
||||
{{ protocol }}://{{ domain }}{% url 'activate' uidb64=uid token=token %}
|
||||
|
||||
If you did not sign up for this account, please ignore this email.
|
||||
|
||||
Best regards,
|
||||
The Referral Rewards Team
|
||||
{% endautoescape %}
|
||||
15
core/templates/core/emails/password_reset_email.html
Normal file
15
core/templates/core/emails/password_reset_email.html
Normal file
@ -0,0 +1,15 @@
|
||||
{% autoescape off %}
|
||||
You're receiving this email because you requested a password reset for your user account at {{ site_name }}.
|
||||
|
||||
Please go to the following page and choose a new password:
|
||||
|
||||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
|
||||
{% endblock %}
|
||||
|
||||
Your username, in case you've forgotten: {{ user.get_username }}
|
||||
|
||||
Thanks for using our site!
|
||||
|
||||
The {{ site_name }} team
|
||||
{% endautoescape %}
|
||||
1
core/templates/core/emails/password_reset_subject.txt
Normal file
1
core/templates/core/emails/password_reset_subject.txt
Normal file
@ -0,0 +1 @@
|
||||
Password reset on {{ site_name }}
|
||||
@ -27,10 +27,14 @@
|
||||
<input type="text" name="username" class="form-control" id="id_username" placeholder="Username" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="mb-2">
|
||||
<label for="id_password" class="form-label fw-600">Password</label>
|
||||
<input type="password" name="password" class="form-control" id="id_password" placeholder="Password" required>
|
||||
</div>
|
||||
|
||||
<div class="text-end mb-4">
|
||||
<a href="{% url 'password_reset' %}" class="text-decoration-none small fw-bold">Forgot password?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 py-3">Log In</button>
|
||||
</form>
|
||||
@ -42,4 +46,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
25
core/templates/core/password_reset_complete.html
Normal file
25
core/templates/core/password_reset_complete.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Password Reset Complete - Referral Rewards{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card border-0 shadow-lg p-4 p-md-5 rounded-4 text-center">
|
||||
<div class="mb-4">
|
||||
<div class="display-1 text-success mb-3">
|
||||
<i class="bi bi-check-circle"></i>
|
||||
</div>
|
||||
<h2 class="mb-3">Password Reset!</h2>
|
||||
<p class="text-muted">Your password has been set. You may go ahead and log in now.</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<a href="{% url 'login' %}" class="btn btn-primary w-100 py-3">Log In</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
72
core/templates/core/password_reset_confirm.html
Normal file
72
core/templates/core/password_reset_confirm.html
Normal file
@ -0,0 +1,72 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Set New Password - Referral Rewards{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card border-0 shadow-lg p-4 p-md-5 rounded-4">
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="mb-3">Set New Password</h2>
|
||||
<p class="text-muted">Please enter your new password twice so we can verify you typed it correctly.</p>
|
||||
</div>
|
||||
|
||||
{% if validlink %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
Please correct the errors below.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for field in form %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ field.id_for_label }}" class="form-label fw-600">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<div class="form-text small">{{ field.help_text }}</div>
|
||||
{% endif %}
|
||||
{% if field.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 py-3 mt-4">Change My Password</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="alert alert-danger">
|
||||
The password reset link was invalid, possibly because it has already been used. Please request a new password reset.
|
||||
</div>
|
||||
<a href="{% url 'password_reset' %}" class="btn btn-primary w-100 py-3 mt-4">Request New Link</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Styling for Django generated form fields */
|
||||
input[type="password"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 0.375rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
27
core/templates/core/password_reset_done.html
Normal file
27
core/templates/core/password_reset_done.html
Normal file
@ -0,0 +1,27 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Reset Email Sent - Referral Rewards{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card border-0 shadow-lg p-4 p-md-5 rounded-4 text-center">
|
||||
<div class="mb-4">
|
||||
<div class="display-1 text-primary mb-3">
|
||||
<i class="bi bi-envelope-check"></i>
|
||||
</div>
|
||||
<h2 class="mb-3">Email Sent</h2>
|
||||
<p class="text-muted">We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.</p>
|
||||
</div>
|
||||
|
||||
<p class="text-muted">If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.</p>
|
||||
|
||||
<div class="mt-5">
|
||||
<a href="{% url 'login' %}" class="btn btn-primary w-100 py-3">Return to Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
43
core/templates/core/password_reset_form.html
Normal file
43
core/templates/core/password_reset_form.html
Normal file
@ -0,0 +1,43 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Reset Password - Referral Rewards{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5 mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card border-0 shadow-lg p-4 p-md-5 rounded-4">
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="mb-3">Reset Password</h2>
|
||||
<p class="text-muted">Enter your email address and we'll send you a link to reset your password.</p>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
Please correct the errors below.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="id_email" class="form-label fw-600">Email Address</label>
|
||||
<input type="email" name="email" class="form-control" id="id_email" placeholder="Email address" required>
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{{ form.email.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 py-3">Send Reset Link</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<p class="text-muted mb-0"><a href="{% url 'login' %}" class="text-decoration-none fw-bold">Back to Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -15,14 +15,23 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ field.id_for_label }}" class="form-label fw-600">{{ field.label }}</label>
|
||||
{{ field.errors }}
|
||||
<input type="{{ field.field.widget.input_type }}"
|
||||
name="{{ field.name }}"
|
||||
class="form-control {% if field.errors %}is-invalid{% endif %}"
|
||||
id="{{ field.id_for_label }}"
|
||||
placeholder="Enter {{ field.label|lower }}">
|
||||
<div class="mb-3 {% if field.name == 'terms_accepted' %}form-check{% endif %}">
|
||||
{% if field.name == 'terms_accepted' %}
|
||||
{{ field }}
|
||||
<label class="form-check-label ms-2" for="{{ field.id_for_label }}">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
{{ field.errors }}
|
||||
{% else %}
|
||||
<label for="{{ field.id_for_label }}" class="form-label fw-600">{{ field.label }}</label>
|
||||
{{ field.errors }}
|
||||
<input type="{{ field.field.widget.input_type }}"
|
||||
name="{{ field.name }}"
|
||||
class="form-control {% if field.errors %}is-invalid{% endif %}"
|
||||
id="{{ field.id_for_label }}"
|
||||
placeholder="Enter {{ field.label|lower }}">
|
||||
{% endif %}
|
||||
|
||||
{% if field.help_text %}
|
||||
<div class="form-text opacity-75 small">{{ field.help_text|safe }}</div>
|
||||
{% endif %}
|
||||
@ -39,4 +48,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
10
core/tokens.py
Normal file
10
core/tokens.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
|
||||
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
|
||||
def _make_hash_value(self, user, timestamp):
|
||||
return (
|
||||
str(user.pk) + str(timestamp) +
|
||||
str(user.is_active)
|
||||
)
|
||||
|
||||
account_activation_token = AccountActivationTokenGenerator()
|
||||
19
core/urls.py
19
core/urls.py
@ -5,7 +5,26 @@ from . import views
|
||||
urlpatterns = [
|
||||
path("", views.home, name="home"),
|
||||
path("signup/", views.signup, name="signup"),
|
||||
path("activate/<uidb64>/<token>/", views.activate, name="activate"),
|
||||
path("dashboard/", views.dashboard, name="dashboard"),
|
||||
path("login/", auth_views.LoginView.as_view(template_name='core/login.html'), name="login"),
|
||||
path("logout/", views.logout_view, name="logout"),
|
||||
|
||||
# Password Reset
|
||||
path("password-reset/", auth_views.PasswordResetView.as_view(
|
||||
template_name='core/password_reset_form.html',
|
||||
email_template_name='core/emails/password_reset_email.html',
|
||||
subject_template_name='core/emails/password_reset_subject.txt',
|
||||
success_url='/password-reset/done/'
|
||||
), name="password_reset"),
|
||||
path("password-reset/done/", auth_views.PasswordResetDoneView.as_view(
|
||||
template_name='core/password_reset_done.html'
|
||||
), name="password_reset_done"),
|
||||
path("password-reset-confirm/<uidb64>/<token>/", auth_views.PasswordResetConfirmView.as_view(
|
||||
template_name='core/password_reset_confirm.html',
|
||||
success_url='/password-reset-complete/'
|
||||
), name="password_reset_confirm"),
|
||||
path("password-reset-complete/", auth_views.PasswordResetCompleteView.as_view(
|
||||
template_name='core/password_reset_complete.html'
|
||||
), name="password_reset_complete"),
|
||||
]
|
||||
@ -1,9 +1,18 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth import login, logout
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth import login, logout, get_user_model
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.mail import EmailMessage
|
||||
|
||||
from .models import Profile
|
||||
from .forms import SignupForm
|
||||
from .tokens import account_activation_token
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
def home(request):
|
||||
if request.user.is_authenticated:
|
||||
@ -14,21 +23,59 @@ def signup(request):
|
||||
if request.user.is_authenticated:
|
||||
return redirect('dashboard')
|
||||
if request.method == 'POST':
|
||||
form = UserCreationForm(request.POST)
|
||||
form = SignupForm(request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save()
|
||||
login(request, user)
|
||||
messages.success(request, "Welcome to Referral Rewards! Your account has been created.")
|
||||
return redirect('dashboard')
|
||||
user = form.save(commit=False)
|
||||
user.is_active = False
|
||||
user.save()
|
||||
|
||||
# Send activation email
|
||||
current_site = get_current_site(request)
|
||||
mail_subject = 'Activate your Referral Rewards account.'
|
||||
message = render_to_string('core/emails/activation_email.html', {
|
||||
'user': user,
|
||||
'domain': current_site.domain,
|
||||
'protocol': 'https' if request.is_secure() else 'http',
|
||||
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
|
||||
'token': account_activation_token.make_token(user),
|
||||
})
|
||||
to_email = form.cleaned_data.get('email')
|
||||
email = EmailMessage(
|
||||
mail_subject, message, to=[to_email]
|
||||
)
|
||||
try:
|
||||
email.send()
|
||||
messages.success(request, 'Please confirm your email address to complete the registration. Check your inbox.')
|
||||
except Exception as e:
|
||||
messages.error(request, f'Error sending email: {str(e)}. Please contact support.')
|
||||
|
||||
return redirect('login')
|
||||
else:
|
||||
form = UserCreationForm()
|
||||
form = SignupForm()
|
||||
return render(request, 'core/signup.html', {'form': form})
|
||||
|
||||
def activate(request, uidb64, token):
|
||||
try:
|
||||
uid = force_str(urlsafe_base64_decode(uidb64))
|
||||
user = User.objects.get(pk=uid)
|
||||
except(TypeError, ValueError, OverflowError, User.DoesNotExist):
|
||||
user = None
|
||||
if user is not None and account_activation_token.check_token(user, token):
|
||||
user.is_active = True
|
||||
user.save()
|
||||
login(request, user)
|
||||
messages.success(request, 'Thank you for your email confirmation. Now you can enjoy our services.')
|
||||
return redirect('dashboard')
|
||||
else:
|
||||
messages.error(request, 'Activation link is invalid!')
|
||||
return redirect('home')
|
||||
|
||||
@login_required
|
||||
def dashboard(request):
|
||||
profile = request.user.profile
|
||||
# Ensure profile exists (though signal should handle it)
|
||||
profile, created = Profile.objects.get_or_create(user=request.user)
|
||||
return render(request, 'core/dashboard.html', {'profile': profile})
|
||||
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect('home')
|
||||
return redirect('home')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user